From ae091aba041d00ceeeb939c607938ead23f2e112 Mon Sep 17 00:00:00 2001 From: pseusys Date: Mon, 22 Jan 2024 04:46:56 +0100 Subject: [PATCH 001/140] new telegram message proposal --- dff/messengers/telegram.py | 132 ++++++++++++++ dff/messengers/telegram/__init__.py | 14 -- dff/messengers/telegram/interface.py | 222 ------------------------ dff/messengers/telegram/message.py | 105 ------------ dff/messengers/telegram/messenger.py | 247 --------------------------- dff/messengers/telegram/utils.py | 54 ------ dff/script/core/message.py | 81 +++++++-- pyproject.toml | 4 +- 8 files changed, 197 insertions(+), 662 deletions(-) create mode 100644 dff/messengers/telegram.py delete mode 100644 dff/messengers/telegram/__init__.py delete mode 100644 dff/messengers/telegram/interface.py delete mode 100644 dff/messengers/telegram/message.py delete mode 100644 dff/messengers/telegram/messenger.py delete mode 100644 dff/messengers/telegram/utils.py diff --git a/dff/messengers/telegram.py b/dff/messengers/telegram.py new file mode 100644 index 000000000..ee4f6da9d --- /dev/null +++ b/dff/messengers/telegram.py @@ -0,0 +1,132 @@ +""" +Interface +------------ +This module implements various interfaces for :py:class:`~dff.messengers.telegram.messenger.TelegramMessenger` +that can be used to interact with the Telegram API. +""" +from pydantic import HttpUrl + +from telegram import InputMediaAnimation, InputMediaAudio, InputMediaDocument, InputMediaPhoto, InputMediaVideo, Update, Sticker as TelegramSticker, Message as TelegramMessage +from telegram.ext import Application, MessageHandler, ContextTypes +from telegram.ext.filters import ALL + +from dff.messengers.common import MessengerInterface +from dff.pipeline.types import PipelineRunnerFunction +from dff.script.core.message import Animation, Audio, Contact, Document, Image, Invoice, Location, Message, Poll, PollOption, Sticker, Story, Venue, Video + + +# TODO: do we need Game, VideoNote, Dice classes? +def extract_message_from_telegram(update: TelegramMessage) -> Message: # pragma: no cover + message = Message() + message.attachments = list() + + if update.text is not None: + message.text = update.text + if update.location is not None: + message.attachments += [Location(latitude=update.location.latitude, longitude=update.location.longitude)] + if update.venue is not None: + message.attachments += [Venue(latitude=update.venue.location.latitude, longitude=update.venue.location.longitude, title=update.venue.title)] + if update.contact is not None: + message.attachments += [Contact(phone_number=update.contact.phone_number, first_name=update.contact.first_name, last_name=update.contact.last_name)] + if update.invoice is not None: + message.attachments += [Invoice(title=update.invoice.title, description=update.invoice.description, currency=update.invoice.currency, amount=update.invoice.total_amount)] + if update.poll is not None: + message.attachments += [Poll(question=update.poll.question, options=[PollOption(text=option.text, votes=option.voter_count) for option in update.poll.options])] + if update.audio is not None: + message.attachments += [Audio(source=HttpUrl(update.audio.file_id))] + if update.video is not None: + message.attachments += [Video(source=HttpUrl(update.video.file_id))] + if update.animation is not None: + message.attachments += [Animation(source=HttpUrl(update.animation.file_id))] + if len(update.photo) > 0: + message.attachments += [Image(source=HttpUrl(photo.file_id)) for photo in update.photo] + if update.story is not None: + message.attachments += [Story()] + if update.sticker is not None: + message.attachments += [Sticker(source=HttpUrl(update.sticker.file_id))] + if update.document is not None: + message.attachments += [Document(source=HttpUrl(update.document.file_id))] + + message.original_message = update + return message + + +async def cast_message_to_telegram_and_send(update: TelegramMessage, message: Message) -> None: # pragma: no cover + if message.attachments is not None: + files = list() + for attachment in message.attachments: + if isinstance(attachment, Location): + await update.reply_location(attachment.latitude, attachment.longitude) + if isinstance(attachment, Venue): + await update.reply_venue(attachment.latitude, attachment.longitude, attachment.title) + if isinstance(attachment, Contact): + await update.reply_contact(attachment.phone_number, attachment.first_name, attachment.last_name) + if isinstance(attachment, Poll): + await update.reply_poll(attachment.question, [option.text for option in attachment.options]) + if isinstance(attachment, Audio): + attachment_bytes = attachment.get_bytes() + if attachment_bytes is not None: + files += [InputMediaAudio(attachment_bytes)] + if isinstance(attachment, Video): + attachment_bytes = attachment.get_bytes() + if attachment_bytes is not None: + files += [InputMediaVideo(attachment_bytes)] + if isinstance(attachment, Animation): + attachment_bytes = attachment.get_bytes() + if attachment_bytes is not None: + files += [InputMediaAnimation(attachment_bytes)] + if isinstance(attachment, Image): + attachment_bytes = attachment.get_bytes() + if attachment_bytes is not None: + files += [InputMediaPhoto(attachment_bytes)] + if isinstance(attachment, Story): + pass + if isinstance(attachment, Sticker): + attachment_bytes = attachment.get_bytes() + if attachment_bytes is not None: + await update.reply_sticker(attachment_bytes) + if isinstance(attachment, Document): + attachment_bytes = attachment.get_bytes() + if attachment_bytes is not None: + files += [InputMediaDocument(attachment_bytes)] + await update.reply_media_group(files, caption=message.text) + elif message.text is not None: + await update.reply_text(message.text) + + +class AbstractTelegramInterface(MessengerInterface): # pragma: no cover + def __init__(self, token: str) -> None: + self.application = Application.builder().token(token).build() + self.application.add_handler(MessageHandler(ALL, self.on_message)) + + async def on_message(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> None: + if update.effective_user is not None and update.message is not None: + message = extract_message_from_telegram(update.message) + resp = self.callback(message, update.effective_user.id) + if resp.last_response is not None: + await cast_message_to_telegram_and_send(update.message, resp.last_response) + + async def connect(self, callback: PipelineRunnerFunction, *args, **kwargs): + self.callback = callback + + +class PollingTelegramInterface(AbstractTelegramInterface): # pragma: no cover + def __init__(self, token: str, interval: int = 2, timeout: int = 20) -> None: + super().__init__(token) + self.interval = interval + self.timeout = timeout + + async def connect(self, callback: PipelineRunnerFunction, *args, **kwargs): + await super().connect(callback, *args, **kwargs) + self.application.run_polling(poll_interval=self.interval, timeout=self.timeout, allowed_updates=Update.ALL_TYPES) + + +class CallbackTelegramInterface(AbstractTelegramInterface): # pragma: no cover + def __init__(self, token: str, host: str = "localhost", port: int = 844): + super().__init__(token) + self.listen = host + self.port = port + + async def connect(self, callback: PipelineRunnerFunction, *args, **kwargs): + await super().connect(callback, *args, **kwargs) + self.application.run_webhook(listen=self.listen, port=self.port, allowed_updates=Update.ALL_TYPES) diff --git a/dff/messengers/telegram/__init__.py b/dff/messengers/telegram/__init__.py deleted file mode 100644 index cb7e38305..000000000 --- a/dff/messengers/telegram/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# -*- coding: utf-8 -*- - -try: - import telebot -except ImportError: - raise ImportError("telebot is not installed. Run `pip install dff[telegram]`") - -from .messenger import TelegramMessenger -from .interface import PollingTelegramInterface, CallbackTelegramInterface -from .message import TelegramUI, TelegramMessage, RemoveKeyboard, ParseMode -from .messenger import ( - UpdateType, - telegram_condition, -) diff --git a/dff/messengers/telegram/interface.py b/dff/messengers/telegram/interface.py deleted file mode 100644 index ba482f01b..000000000 --- a/dff/messengers/telegram/interface.py +++ /dev/null @@ -1,222 +0,0 @@ -""" -Interface ------------- -This module implements various interfaces for :py:class:`~dff.messengers.telegram.messenger.TelegramMessenger` -that can be used to interact with the Telegram API. -""" -import asyncio -from typing import Any, Optional, List, Tuple, Callable - -from telebot import types, apihelper - -from dff.messengers.common import MessengerInterface, CallbackMessengerInterface -from dff.pipeline.types import PipelineRunnerFunction -from .messenger import TelegramMessenger -from .message import TelegramMessage - -try: - from flask import Flask, request, abort - - flask_imported = True -except ImportError: - flask_imported = False - Flask = Any - request, abort = None, None - - -apihelper.ENABLE_MIDDLEWARE = True - - -def extract_telegram_request_and_id( - update: types.Update, messenger: Optional[TelegramMessenger] = None -) -> Tuple[TelegramMessage, int]: # pragma: no cover - """ - Utility function that extracts parameters from a telegram update. - Changes the messenger state, setting the last update id. - - Returned message has the following fields: - - - | `update_id` -- this field stores `update.update_id`, - - | `update` -- this field stores the first non-empty field of `update`, - - | `update_type` -- this field stores the name of the first non-empty field of `update`, - - | `text` -- this field stores `update.message.text`, - - | `callback_query` -- this field stores `update.callback_query.data`. - - Also return context id which is `chat`, `from_user` or `user` of the update. - - :param update: Update to process. - :param messenger: - Messenger instance. If passed updates `last_update_id`. - Defaults to None. - """ - if messenger is not None: - if update.update_id > messenger.last_update_id: - messenger.last_update_id = update.update_id - - message = TelegramMessage(update_id=update.update_id) - ctx_id = None - - for update_field, update_value in vars(update).items(): - if update_field != "update_id" and update_value is not None: - if message.update is not None: - raise RuntimeError(f"Two update fields. First: {message.update_type}; second: {update_field}") - message.update_type = update_field - message.update = update_value - if isinstance(update_value, types.Message): - message.text = update_value.text - - if isinstance(update_value, types.CallbackQuery): - data = update_value.data - if data is not None: - message.callback_query = data - - dict_update = vars(update_value) - # if 'chat' is not available, fall back to 'from_user', then to 'user' - user = dict_update.get("chat", dict_update.get("from_user", dict_update.get("user"))) - ctx_id = getattr(user, "id", None) - if message.update is None: - raise RuntimeError(f"No update fields found: {update}") - - return message, ctx_id - - -class PollingTelegramInterface(MessengerInterface): # pragma: no cover - """ - Telegram interface that retrieves updates by polling. - Multi-threaded polling is currently not supported. - - :param token: Bot token - :param messenger: - :py:class:`~dff.messengers.telegram.messenger.TelegramMessenger` instance. - If not `None` will be used instead of creating messenger from token. - Token value does not matter in that case. - Defaults to None. - :param interval: - Polling interval. See `link `__. - Defaults to 2. - :param allowed_updates: - Processed updates. See `link `__. - Defaults to None. - :param timeout: - General timeout. See `link `__. - Defaults to 20. - :param long_polling_timeout: - Polling timeout. See `link `__. - Defaults to 20. - """ - - def __init__( - self, - token: str, - interval: int = 2, - allowed_updates: Optional[List[str]] = None, - timeout: int = 20, - long_polling_timeout: int = 20, - messenger: Optional[TelegramMessenger] = None, - ): - self.messenger = ( - messenger if messenger is not None else TelegramMessenger(token, suppress_middleware_excepions=True) - ) - self.allowed_updates = allowed_updates - self.interval = interval - self.timeout = timeout - self.long_polling_timeout = long_polling_timeout - - async def connect(self, callback: PipelineRunnerFunction, loop: Optional[Callable] = None, *args, **kwargs): - def dff_middleware(bot_instance, update): - message, ctx_id = extract_telegram_request_and_id(update, self.messenger) - - ctx = asyncio.run(callback(message, ctx_id)) - - bot_instance.send_response(ctx_id, ctx.last_response) - - self.messenger.middleware_handler()(dff_middleware) - - self.messenger.infinity_polling( - timeout=self.timeout, long_polling_timeout=self.long_polling_timeout, interval=self.interval - ) - - -class CallbackTelegramInterface(CallbackMessengerInterface): # pragma: no cover - """ - Asynchronous Telegram interface that retrieves updates via webhook. - Any Flask server can be passed to set up a webhook on a separate endpoint. - - :param token: Bot token - :param messenger: - :py:class:`~dff.messengers.telegram.messenger.TelegramMessenger` instance. - If not `None` will be used instead of creating messenger from token. - Token value does not matter in that case. - Defaults to None. - :param app: - Flask instance. - Defaults to `Flask(__name__)`. - :param endpoint: - Webhook endpoint. Should be prefixed with "/". - Defaults to "/telegram-webhook". - :param host: - Host IP. - Defaults to "localhost". - :param port: - Port of the app. - Defaults to 8443. - :param debug: - Run the Flask app in debug mode. - :param load_dotenv: - Whether or not the .env file in the project folder - should be used to set environment variables. - :param full_uri: - Full public IP of your webhook that is accessible by https. - Defaults to `"https://{host}:{port}{endpoint}"`. - :param wsgi_options: - Keyword arguments to forward to `Flask.run` method. - Use these to set `ssl_context` and other WSGI options. - """ - - def __init__( - self, - token: str, - app: Optional[Flask] = None, - host: str = "localhost", - port: int = 8443, - debug: Optional[bool] = None, - load_dotenv: bool = True, - endpoint: str = "/telegram-webhook", - full_uri: Optional[str] = None, - messenger: Optional[TelegramMessenger] = None, - **wsgi_options, - ): - if not flask_imported: - raise ModuleNotFoundError("Flask is not installed. Install it with `pip install flask`.") - - self.messenger = messenger if messenger is not None else TelegramMessenger(token) - self.app = app if app else Flask(__name__) - self.host = host - self.port = port - self.debug = debug - self.load_dotenv = load_dotenv - self.wsgi_options = wsgi_options - self.endpoint = endpoint - self.full_uri = full_uri if full_uri is not None else "".join([f"https://{host}:{port}", endpoint]) - - async def endpoint(): - if not request.headers.get("content-type") == "application/json": - abort(403) - - json_string = request.get_data().decode("utf-8") - update = types.Update.de_json(json_string) - resp = await self.on_request_async(*extract_telegram_request_and_id(update, self.messenger)) - self.messenger.send_response(resp.id, resp.last_response) - return "" - - self.app.route(self.endpoint, methods=["POST"])(endpoint) - - async def connect(self, callback: PipelineRunnerFunction): - await super().connect(callback) - - self.messenger.remove_webhook() - self.messenger.set_webhook(self.full_uri) - - self.app.run( - host=self.host, port=self.port, load_dotenv=self.load_dotenv, debug=self.debug, **self.wsgi_options - ) diff --git a/dff/messengers/telegram/message.py b/dff/messengers/telegram/message.py deleted file mode 100644 index bc47c4f21..000000000 --- a/dff/messengers/telegram/message.py +++ /dev/null @@ -1,105 +0,0 @@ -""" -Telegram Message ----------------- -This module implements inherited classes :py:mod:`dff.script.core.message` modified for usage with Telegram. -""" -from typing import Optional, Union -from enum import Enum - -from telebot.types import ( - ReplyKeyboardRemove, - ReplyKeyboardMarkup, - InlineKeyboardMarkup, - Message as tlMessage, - InlineQuery, - ChosenInlineResult, - CallbackQuery as tlCallbackQuery, - ShippingQuery, - PreCheckoutQuery, - Poll, - PollAnswer, - ChatMemberUpdated, - ChatJoinRequest, -) - -from dff.script.core.message import Message, Location, Keyboard, DataModel -from pydantic import model_validator - - -class TelegramUI(Keyboard): - is_inline: bool = True - """ - Whether to use `inline keyboard `__ or - a `keyboard `__. - """ - row_width: int = 3 - """Limits the maximum number of buttons in a row.""" - - @model_validator(mode="after") - def validate_buttons(self, _): - if not self.is_inline: - for button in self.buttons: - if button.payload is not None or button.source is not None: - raise AssertionError(f"`payload` and `source` are only used for inline keyboards: {button}") - return self - - -class _ClickButton(DataModel): - """This class is only used in telegram tests (to click buttons as a client).""" - - button_index: int - - -class RemoveKeyboard(DataModel): - """Pass an instance of this class to :py:attr:`~.TelegramMessage.ui` to remove current keyboard.""" - - ... - - -class ParseMode(Enum): - """ - Parse mode of the message. - More info: https://core.telegram.org/bots/api#formatting-options. - """ - - HTML = "HTML" - MARKDOWN = "MarkdownV2" - - -class TelegramMessage(Message): - ui: Optional[ - Union[TelegramUI, RemoveKeyboard, ReplyKeyboardRemove, ReplyKeyboardMarkup, InlineKeyboardMarkup] - ] = None - location: Optional[Location] = None - callback_query: Optional[Union[str, _ClickButton]] = None - update: Optional[ - Union[ - tlMessage, - InlineQuery, - ChosenInlineResult, - tlCallbackQuery, - ShippingQuery, - PreCheckoutQuery, - Poll, - PollAnswer, - ChatMemberUpdated, - ChatJoinRequest, - ] - ] = None - """This field stores an update representing this message.""" - update_id: Optional[int] = None - update_type: Optional[str] = None - """Name of the field that stores an update representing this message.""" - parse_mode: Optional[ParseMode] = None - """Parse mode of the message.""" - - def __eq__(self, other): - if isinstance(other, Message): - for field in self.model_fields: - if field not in ("parse_mode", "update_id", "update", "update_type"): - if field not in other.model_fields: - return False - if self.__getattribute__(field) != other.__getattribute__(field): - return False - return True - return NotImplemented diff --git a/dff/messengers/telegram/messenger.py b/dff/messengers/telegram/messenger.py deleted file mode 100644 index 07919c3d0..000000000 --- a/dff/messengers/telegram/messenger.py +++ /dev/null @@ -1,247 +0,0 @@ -""" -Messenger ------------------ -The Messenger module provides the :py:class:`~dff.messengers.telegram.messenger.TelegramMessenger` class. -The former inherits from the :py:class:`~TeleBot` class from the `pytelegrambotapi` library. -Using it, you can put Telegram update handlers inside your script and condition your transitions accordingly. - -""" -from pathlib import Path -from typing import Union, List, Optional, Callable -from enum import Enum - -from telebot import types, TeleBot - -from dff.script import Context -from dff.pipeline import Pipeline - -from .utils import batch_open_io -from .message import TelegramMessage, TelegramUI, RemoveKeyboard - -from dff.script import Message -from dff.script.core.message import Audio, Video, Image, Document - - -class TelegramMessenger(TeleBot): # pragma: no cover - """ - This class inherits from `Telebot` and implements framework-specific functionality - like sending generic responses. - - :param token: A Telegram API bot token. - :param kwargs: Arbitrary parameters that match the signature of the `Telebot` class. - For reference see: `link `_ . - - """ - - def __init__( - self, - token: str, - **kwargs, - ): - super().__init__(token, threaded=False, **kwargs) - - def send_response(self, chat_id: Union[str, int], response: Union[str, dict, Message]) -> None: - """ - Cast `response` to :py:class:`~dff.messengers.telegram.types.TelegramMessage` and send it. - Message fields are sent in separate messages in the following order: - - 1. Attachments - 2. Location - 3. Text with keyboard - - :param chat_id: Telegram chat ID. - :param response: Response data. String, dictionary or :py:class:`~dff.script.responses.generics.Response`. - will be cast to :py:class:`~dff.messengers.telegram.types.TelegramMessage`. - """ - if isinstance(response, TelegramMessage): - ready_response = response - elif isinstance(response, str): - ready_response = TelegramMessage(text=response) - elif isinstance(response, Message): - ready_response = TelegramMessage.model_validate(response.model_dump()) - elif isinstance(response, dict): - ready_response = TelegramMessage.model_validate(response) - else: - raise TypeError( - "Type of the response argument should be one of the following:" - " `str`, `dict`, `Message`, or `TelegramMessage`." - ) - parse_mode = ready_response.parse_mode.value if ready_response.parse_mode is not None else None - if ready_response.attachments is not None: - if len(ready_response.attachments.files) == 1: - attachment = ready_response.attachments.files[0] - if isinstance(attachment, Audio): - method = self.send_audio - elif isinstance(attachment, Document): - method = self.send_document - elif isinstance(attachment, Video): - method = self.send_video - elif isinstance(attachment, Image): - method = self.send_photo - else: - raise TypeError(type(attachment)) - params = {"caption": attachment.title, "parse_mode": parse_mode} - if isinstance(attachment.source, Path): - with open(attachment.source, "rb") as file: - method(chat_id, file, **params) - else: - method(chat_id, str(attachment.source or attachment.id), **params) - else: - - def cast(file): - if isinstance(file, Image): - cast_to_media_type = types.InputMediaPhoto - elif isinstance(file, Audio): - cast_to_media_type = types.InputMediaAudio - elif isinstance(file, Document): - cast_to_media_type = types.InputMediaDocument - elif isinstance(file, Video): - cast_to_media_type = types.InputMediaVideo - else: - raise TypeError(type(file)) - return cast_to_media_type(media=str(file.source or file.id), caption=file.title) - - files = map(cast, ready_response.attachments.files) - with batch_open_io(files) as media: - self.send_media_group(chat_id=chat_id, media=media) - - if ready_response.location: - self.send_location( - chat_id=chat_id, - latitude=ready_response.location.latitude, - longitude=ready_response.location.longitude, - ) - - if ready_response.ui is not None: - if isinstance(ready_response.ui, RemoveKeyboard): - keyboard = types.ReplyKeyboardRemove() - elif isinstance(ready_response.ui, TelegramUI): - if ready_response.ui.is_inline: - keyboard = types.InlineKeyboardMarkup(row_width=ready_response.ui.row_width) - buttons = [ - types.InlineKeyboardButton( - text=item.text, - url=item.source, - callback_data=item.payload, - ) - for item in ready_response.ui.buttons - ] - else: - keyboard = types.ReplyKeyboardMarkup(row_width=ready_response.ui.row_width) - buttons = [ - types.KeyboardButton( - text=item.text, - ) - for item in ready_response.ui.buttons - ] - keyboard.add(*buttons, row_width=ready_response.ui.row_width) - else: - keyboard = ready_response.ui - else: - keyboard = None - - if ready_response.text is not None: - self.send_message( - chat_id=chat_id, - text=ready_response.text, - reply_markup=keyboard, - parse_mode=parse_mode, - ) - elif keyboard is not None: - self.send_message( - chat_id=chat_id, - text="", - reply_markup=keyboard, - parse_mode=parse_mode, - ) - - -_default_messenger = TeleBot("") - - -class UpdateType(Enum): - """ - Represents a type of the telegram update - (which field contains an update in :py:class:`telebot.types.Update`). - See `link `__. - """ - - ALL = "ALL" - MESSAGE = "message" - EDITED_MESSAGE = "edited_message" - CHANNEL_POST = "channel_post" - EDITED_CHANNEL_POST = "edited_channel_post" - INLINE_QUERY = "inline_query" - CHOSEN_INLINE_RESULT = "chosen_inline_result" - CALLBACK_QUERY = "callback_query" - SHIPPING_QUERY = "shipping_query" - PRE_CHECKOUT_QUERY = "pre_checkout_query" - POLL = "poll" - POLL_ANSWER = "poll_answer" - MY_CHAT_MEMBER = "my_chat_member" - CHAT_MEMBER = "chat_member" - CHAT_JOIN_REQUEST = "chat_join_request" - - -def telegram_condition( - messenger: TeleBot = _default_messenger, - update_type: UpdateType = UpdateType.MESSAGE, - commands: Optional[List[str]] = None, - regexp: Optional[str] = None, - func: Optional[Callable] = None, - content_types: Optional[List[str]] = None, - chat_types: Optional[List[str]] = None, - **kwargs, -): - """ - A condition triggered by updates that match the given parameters. - - :param messenger: - Messenger to test filters on. Used only for :py:attr:`Telebot.custom_filters`. - Defaults to :py:data:`._default_messenger`. - :param update_type: - If set to any `UpdateType` other than `UpdateType.ALL` - it will check that an update is of the same type. - Defaults to `UpdateType.Message`. - :param commands: - Telegram command trigger. - See `link `__. - :param regexp: - Regex trigger. - See `link `__. - :param func: - Callable trigger. - See `link `__. - :param content_types: - Content type trigger. - See `link `__. - :param chat_types: - Chat type trigger. - See `link `__. - """ - - update_handler = messenger._build_handler_dict( - None, - False, - commands=commands, - regexp=regexp, - func=func, - content_types=content_types, - chat_types=chat_types, - **kwargs, - ) - - def condition(ctx: Context, _: Pipeline, *__, **___): # pragma: no cover - last_request = ctx.last_request - if last_request is None: - return False - update = getattr(last_request, "update", None) - request_update_type = getattr(last_request, "update_type", None) - if update is None: - return False - if update_type != UpdateType.ALL and request_update_type != update_type.value: - return False - test_result = messenger._test_message_handler(update_handler, update) - return test_result - - return condition diff --git a/dff/messengers/telegram/utils.py b/dff/messengers/telegram/utils.py deleted file mode 100644 index f21dc0016..000000000 --- a/dff/messengers/telegram/utils.py +++ /dev/null @@ -1,54 +0,0 @@ -""" -Utils ------- -This module contains utilities for connecting to Telegram. -""" -from typing import Union, Iterable -from contextlib import contextmanager -from pathlib import Path -from io import IOBase - -from telebot import types - - -def open_io(item: types.InputMedia): - """ - Returns `InputMedia` with an opened file descriptor instead of path. - - :param item: InputMedia object. - """ - if isinstance(item.media, Path): - item.media = item.media.open(mode="rb") - return item - - -def close_io(item: types.InputMedia): - """ - Closes an IO in an `InputMedia` object to perform the cleanup. - - :param item: InputMedia object. - """ - if isinstance(item.media, IOBase): - item.media.close() - - -@contextmanager -def batch_open_io(item: Union[types.InputMedia, Iterable[types.InputMedia]]): - """ - Context manager that controls the state of file descriptors inside `InputMedia`. - Can be used both for single objects and collections. - - :param item: InputMedia objects that contain file descriptors. - """ - if isinstance(item, Iterable): - resources = list(map(open_io, item)) - else: - resources = open_io(item) - try: - yield resources - finally: - if isinstance(resources, Iterable): - for resource in resources: - close_io(resource) - else: - close_io(resources) diff --git a/dff/script/core/message.py b/dff/script/core/message.py index 05f9974f9..25cd2465e 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -26,7 +26,7 @@ class DataModel(BaseModel, extra="allow", arbitrary_types_allowed=True): This class is a Pydantic BaseModel that serves as a base class for all DFF models. """ - ... + pass class Command(DataModel): @@ -35,10 +35,15 @@ class Command(DataModel): a command that can be executed in response to a user input. """ - ... + pass -class Location(DataModel): +class Attachment(DataModel): + + pass + + +class Location(Attachment): """ This class is a data model that represents a geographical location on the Earth's surface. @@ -56,7 +61,39 @@ def __eq__(self, other): return NotImplemented -class Attachment(DataModel): +class Venue(Location): + + title: str + + +class Contact(Attachment): + + phone_number: str + first_name: str + last_name: Optional[str] + + +class Invoice(Attachment): + + title: str + description: str + currency: str + amount: int + + +class PollOption(DataModel): + + text: str + votes: int + + +class Poll(Attachment): + + question: str + options: List[PollOption] + + +class DataAttachment(Attachment): """ This class represents an attachment that can be either a file or a URL, along with an optional ID and title. @@ -77,7 +114,7 @@ def get_bytes(self) -> Optional[bytes]: return file.read() def __eq__(self, other): - if isinstance(other, Attachment): + if isinstance(other, DataAttachment): if self.title != other.title: return False if self.id != other.id: @@ -102,39 +139,46 @@ def validate_source(cls, value): return value -class Audio(Attachment): +class Audio(DataAttachment): """Represents an audio file attachment.""" pass -class Video(Attachment): +class Video(DataAttachment): """Represents a video file attachment.""" pass -class Image(Attachment): +class Animation(DataAttachment): + """Represents an animation file attachment.""" + + pass + + +class Image(DataAttachment): """Represents an image file attachment.""" pass -class Document(Attachment): - """Represents a document file attachment.""" +class Story(DataAttachment): + """Represents an story attachment.""" pass -class Attachments(DataModel): - """This class is a data model that represents a list of attachments.""" +class Sticker(DataAttachment): + """Represents an sticker attachment.""" - files: List[Attachment] = Field(default_factory=list) + pass - def __eq__(self, other): - if isinstance(other, Attachments): - return self.files == other.files - return NotImplemented + +class Document(DataAttachment): + """Represents a document file attachment.""" + + pass class Link(DataModel): @@ -192,9 +236,10 @@ class level variables to store message information. text: Optional[str] = None commands: Optional[List[Command]] = None - attachments: Optional[Attachments] = None + attachments: Optional[List[Attachment]] = None annotations: Optional[dict] = None misc: Optional[dict] = None + original_message: Optional[Any] = None # commands and state options are required for integration with services # that use an intermediate backend server, like Yandex's Alice # state: Optional[Session] = Session.ACTIVE diff --git a/pyproject.toml b/pyproject.toml index e071ae56e..29b2e3834 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,7 +73,7 @@ aiosqlite = { version = "*", optional = true } omegaconf = { version = "*", optional = true } cryptography = { version = "*", optional = true } requests = { version = "*", optional = true } -pytelegrambotapi = { version = "*", optional = true } +python-telegram-bot = { version = "*", optional = true } opentelemetry-instrumentation = { version = "*", optional = true } sqlalchemy = { version = "*", extras = ["asyncio"], optional = true } opentelemetry-exporter-otlp = { version = ">=1.20.0", optional = true } # log body serialization is required @@ -87,7 +87,7 @@ mongodb = ["motor"] mysql = ["sqlalchemy", "asyncmy", "cryptography"] postgresql = ["sqlalchemy", "asyncpg"] ydb = ["ydb", "six"] -telegram = ["pytelegrambotapi"] +telegram = ["python-telegram-bot"] stats = ["opentelemetry-exporter-otlp", "opentelemetry-instrumentation", "requests", "tqdm", "omegaconf"] benchmark = ["pympler", "humanize", "pandas", "altair", "tqdm"] From b1c4da9967c6dd91b1c605cd7981985914f6dade Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 23 Jan 2024 00:54:20 +0100 Subject: [PATCH 002/140] only required attachments remain --- dff/messengers/telegram.py | 19 ++----------------- dff/script/core/message.py | 17 ----------------- 2 files changed, 2 insertions(+), 34 deletions(-) diff --git a/dff/messengers/telegram.py b/dff/messengers/telegram.py index ee4f6da9d..3b9582943 100644 --- a/dff/messengers/telegram.py +++ b/dff/messengers/telegram.py @@ -6,16 +6,15 @@ """ from pydantic import HttpUrl -from telegram import InputMediaAnimation, InputMediaAudio, InputMediaDocument, InputMediaPhoto, InputMediaVideo, Update, Sticker as TelegramSticker, Message as TelegramMessage +from telegram import InputMediaAnimation, InputMediaAudio, InputMediaDocument, InputMediaPhoto, InputMediaVideo, Update, Message as TelegramMessage from telegram.ext import Application, MessageHandler, ContextTypes from telegram.ext.filters import ALL from dff.messengers.common import MessengerInterface from dff.pipeline.types import PipelineRunnerFunction -from dff.script.core.message import Animation, Audio, Contact, Document, Image, Invoice, Location, Message, Poll, PollOption, Sticker, Story, Venue, Video +from dff.script.core.message import Animation, Audio, Contact, Document, Image, Invoice, Location, Message, Poll, PollOption, Video -# TODO: do we need Game, VideoNote, Dice classes? def extract_message_from_telegram(update: TelegramMessage) -> Message: # pragma: no cover message = Message() message.attachments = list() @@ -24,8 +23,6 @@ def extract_message_from_telegram(update: TelegramMessage) -> Message: # pragma message.text = update.text if update.location is not None: message.attachments += [Location(latitude=update.location.latitude, longitude=update.location.longitude)] - if update.venue is not None: - message.attachments += [Venue(latitude=update.venue.location.latitude, longitude=update.venue.location.longitude, title=update.venue.title)] if update.contact is not None: message.attachments += [Contact(phone_number=update.contact.phone_number, first_name=update.contact.first_name, last_name=update.contact.last_name)] if update.invoice is not None: @@ -40,10 +37,6 @@ def extract_message_from_telegram(update: TelegramMessage) -> Message: # pragma message.attachments += [Animation(source=HttpUrl(update.animation.file_id))] if len(update.photo) > 0: message.attachments += [Image(source=HttpUrl(photo.file_id)) for photo in update.photo] - if update.story is not None: - message.attachments += [Story()] - if update.sticker is not None: - message.attachments += [Sticker(source=HttpUrl(update.sticker.file_id))] if update.document is not None: message.attachments += [Document(source=HttpUrl(update.document.file_id))] @@ -57,8 +50,6 @@ async def cast_message_to_telegram_and_send(update: TelegramMessage, message: Me for attachment in message.attachments: if isinstance(attachment, Location): await update.reply_location(attachment.latitude, attachment.longitude) - if isinstance(attachment, Venue): - await update.reply_venue(attachment.latitude, attachment.longitude, attachment.title) if isinstance(attachment, Contact): await update.reply_contact(attachment.phone_number, attachment.first_name, attachment.last_name) if isinstance(attachment, Poll): @@ -79,12 +70,6 @@ async def cast_message_to_telegram_and_send(update: TelegramMessage, message: Me attachment_bytes = attachment.get_bytes() if attachment_bytes is not None: files += [InputMediaPhoto(attachment_bytes)] - if isinstance(attachment, Story): - pass - if isinstance(attachment, Sticker): - attachment_bytes = attachment.get_bytes() - if attachment_bytes is not None: - await update.reply_sticker(attachment_bytes) if isinstance(attachment, Document): attachment_bytes = attachment.get_bytes() if attachment_bytes is not None: diff --git a/dff/script/core/message.py b/dff/script/core/message.py index 25cd2465e..876b9186b 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -61,11 +61,6 @@ def __eq__(self, other): return NotImplemented -class Venue(Location): - - title: str - - class Contact(Attachment): phone_number: str @@ -163,18 +158,6 @@ class Image(DataAttachment): pass -class Story(DataAttachment): - """Represents an story attachment.""" - - pass - - -class Sticker(DataAttachment): - """Represents an sticker attachment.""" - - pass - - class Document(DataAttachment): """Represents a document file attachment.""" From 86c15c2548178bb384198785dd30444b0d482ddb Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 23 Jan 2024 01:17:47 +0100 Subject: [PATCH 003/140] keyboard implemented --- dff/messengers/telegram.py | 29 ++++++++++++++++++++++------- dff/script/core/message.py | 35 +---------------------------------- 2 files changed, 23 insertions(+), 41 deletions(-) diff --git a/dff/messengers/telegram.py b/dff/messengers/telegram.py index 3b9582943..cc02e4d6f 100644 --- a/dff/messengers/telegram.py +++ b/dff/messengers/telegram.py @@ -4,15 +4,16 @@ This module implements various interfaces for :py:class:`~dff.messengers.telegram.messenger.TelegramMessenger` that can be used to interact with the Telegram API. """ +from typing import Optional, Sequence from pydantic import HttpUrl -from telegram import InputMediaAnimation, InputMediaAudio, InputMediaDocument, InputMediaPhoto, InputMediaVideo, Update, Message as TelegramMessage +from telegram import InlineKeyboardButton, InlineKeyboardMarkup, InputMediaAnimation, InputMediaAudio, InputMediaDocument, InputMediaPhoto, InputMediaVideo, Update, Message as TelegramMessage from telegram.ext import Application, MessageHandler, ContextTypes from telegram.ext.filters import ALL from dff.messengers.common import MessengerInterface from dff.pipeline.types import PipelineRunnerFunction -from dff.script.core.message import Animation, Audio, Contact, Document, Image, Invoice, Location, Message, Poll, PollOption, Video +from dff.script.core.message import Animation, Audio, Contact, Document, Image, Invoice, Keyboard, Location, Message, Poll, PollOption, Video def extract_message_from_telegram(update: TelegramMessage) -> Message: # pragma: no cover @@ -44,16 +45,27 @@ def extract_message_from_telegram(update: TelegramMessage) -> Message: # pragma return message +def _create_keyboard(buttons: Sequence[Sequence[str]]) -> Optional[InlineKeyboardMarkup]: + button_list = None + if len(buttons) > 0: + button_list = [[InlineKeyboardButton(button) for button in row] for row in buttons] + if button_list is None: + return None + else: + return InlineKeyboardMarkup(button_list) + + async def cast_message_to_telegram_and_send(update: TelegramMessage, message: Message) -> None: # pragma: no cover + buttons = list() if message.attachments is not None: files = list() for attachment in message.attachments: if isinstance(attachment, Location): - await update.reply_location(attachment.latitude, attachment.longitude) + await update.reply_location(attachment.latitude, attachment.longitude, reply_markup=_create_keyboard(buttons)) if isinstance(attachment, Contact): - await update.reply_contact(attachment.phone_number, attachment.first_name, attachment.last_name) + await update.reply_contact(attachment.phone_number, attachment.first_name, attachment.last_name, reply_markup=_create_keyboard(buttons)) if isinstance(attachment, Poll): - await update.reply_poll(attachment.question, [option.text for option in attachment.options]) + await update.reply_poll(attachment.question, [option.text for option in attachment.options], reply_markup=_create_keyboard(buttons)) if isinstance(attachment, Audio): attachment_bytes = attachment.get_bytes() if attachment_bytes is not None: @@ -74,9 +86,12 @@ async def cast_message_to_telegram_and_send(update: TelegramMessage, message: Me attachment_bytes = attachment.get_bytes() if attachment_bytes is not None: files += [InputMediaDocument(attachment_bytes)] - await update.reply_media_group(files, caption=message.text) + if isinstance(attachment, Keyboard): + buttons = attachment.buttons + if len(files > 0): + await update.reply_media_group(files, caption=message.text) elif message.text is not None: - await update.reply_text(message.text) + await update.reply_text(message.text, reply_markup=_create_keyboard(buttons)) class AbstractTelegramInterface(MessengerInterface): # pragma: no cover diff --git a/dff/script/core/message.py b/dff/script/core/message.py index 876b9186b..5a47d80ba 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -164,46 +164,13 @@ class Document(DataAttachment): pass -class Link(DataModel): - """This class is a DataModel representing a hyperlink.""" - - source: HttpUrl - title: Optional[str] = None - - @property - def html(self): - return f'{self.title if self.title else self.source}' - - -class Button(DataModel): - """ - This class allows for the creation of a button object - with a source URL, a text description, and a payload. - """ - - source: Optional[HttpUrl] = None - text: str - payload: Optional[Any] = None - - def __eq__(self, other): - if isinstance(other, Button): - if self.source != other.source: - return False - if self.text != other.text: - return False - first_payload = bytes(self.payload, encoding="utf-8") if isinstance(self.payload, str) else self.payload - second_payload = bytes(other.payload, encoding="utf-8") if isinstance(other.payload, str) else other.payload - return first_payload == second_payload - return NotImplemented - - class Keyboard(DataModel): """ This class is a DataModel that represents a keyboard object that can be used for a chatbot or messaging application. """ - buttons: List[Button] = Field(default_factory=list, min_length=1) + buttons: List[List[str]] = Field(default_factory=list, min_length=1) def __eq__(self, other): if isinstance(other, Keyboard): From a0a859c04522ad235097e05ee4095a7dd9714992 Mon Sep 17 00:00:00 2001 From: pseusys Date: Mon, 29 Jan 2024 15:43:31 +0100 Subject: [PATCH 004/140] tests reworked --- dff/messengers/telegram.py | 59 +++++--- dff/script/core/message.py | 24 +++- tutorials/messengers/telegram/2_buttons.py | 130 ++++++++---------- .../telegram/3_buttons_with_callback.py | 108 ++++++++------- tutorials/messengers/telegram/4_conditions.py | 61 ++++---- .../telegram/5_conditions_with_media.py | 117 +++++++++------- .../telegram/6_conditions_extras.py | 29 ++-- .../messengers/telegram/7_polling_setup.py | 5 +- .../messengers/telegram/8_webhook_setup.py | 4 +- 9 files changed, 294 insertions(+), 243 deletions(-) diff --git a/dff/messengers/telegram.py b/dff/messengers/telegram.py index cc02e4d6f..550649987 100644 --- a/dff/messengers/telegram.py +++ b/dff/messengers/telegram.py @@ -4,16 +4,18 @@ This module implements various interfaces for :py:class:`~dff.messengers.telegram.messenger.TelegramMessenger` that can be used to interact with the Telegram API. """ -from typing import Optional, Sequence +from typing import Callable, Optional, Sequence, cast from pydantic import HttpUrl from telegram import InlineKeyboardButton, InlineKeyboardMarkup, InputMediaAnimation, InputMediaAudio, InputMediaDocument, InputMediaPhoto, InputMediaVideo, Update, Message as TelegramMessage -from telegram.ext import Application, MessageHandler, ContextTypes +from telegram.ext import Application, ExtBot, MessageHandler, CallbackQueryHandler, ContextTypes from telegram.ext.filters import ALL from dff.messengers.common import MessengerInterface +from dff.pipeline import Pipeline from dff.pipeline.types import PipelineRunnerFunction -from dff.script.core.message import Animation, Audio, Contact, Document, Image, Invoice, Keyboard, Location, Message, Poll, PollOption, Video +from dff.script.core.context import Context +from dff.script.core.message import Animation, Audio, Button, Contact, Document, Image, Invoice, Keyboard, Location, Message, Poll, PollOption, Video def extract_message_from_telegram(update: TelegramMessage) -> Message: # pragma: no cover @@ -41,31 +43,30 @@ def extract_message_from_telegram(update: TelegramMessage) -> Message: # pragma if update.document is not None: message.attachments += [Document(source=HttpUrl(update.document.file_id))] - message.original_message = update return message -def _create_keyboard(buttons: Sequence[Sequence[str]]) -> Optional[InlineKeyboardMarkup]: +def _create_keyboard(buttons: Sequence[Sequence[Button]]) -> Optional[InlineKeyboardMarkup]: button_list = None if len(buttons) > 0: - button_list = [[InlineKeyboardButton(button) for button in row] for row in buttons] + button_list = [[InlineKeyboardButton(text=button.text, callback_data=button.data) for button in row] for row in buttons] if button_list is None: return None else: return InlineKeyboardMarkup(button_list) -async def cast_message_to_telegram_and_send(update: TelegramMessage, message: Message) -> None: # pragma: no cover +async def cast_message_to_telegram_and_send(bot: ExtBot, chat_id: int, message: Message) -> None: # pragma: no cover buttons = list() if message.attachments is not None: files = list() for attachment in message.attachments: if isinstance(attachment, Location): - await update.reply_location(attachment.latitude, attachment.longitude, reply_markup=_create_keyboard(buttons)) + await bot.send_location(chat_id, attachment.latitude, attachment.longitude, reply_markup=_create_keyboard(buttons)) if isinstance(attachment, Contact): - await update.reply_contact(attachment.phone_number, attachment.first_name, attachment.last_name, reply_markup=_create_keyboard(buttons)) + await bot.send_contact(chat_id, attachment.phone_number, attachment.first_name, attachment.last_name, reply_markup=_create_keyboard(buttons)) if isinstance(attachment, Poll): - await update.reply_poll(attachment.question, [option.text for option in attachment.options], reply_markup=_create_keyboard(buttons)) + await bot.send_poll(chat_id, attachment.question, [option.text for option in attachment.options], reply_markup=_create_keyboard(buttons)) if isinstance(attachment, Audio): attachment_bytes = attachment.get_bytes() if attachment_bytes is not None: @@ -89,28 +90,38 @@ async def cast_message_to_telegram_and_send(update: TelegramMessage, message: Me if isinstance(attachment, Keyboard): buttons = attachment.buttons if len(files > 0): - await update.reply_media_group(files, caption=message.text) + await bot.send_media_group(chat_id, files, caption=message.text) elif message.text is not None: - await update.reply_text(message.text, reply_markup=_create_keyboard(buttons)) + await bot.send_message(chat_id, message.text, reply_markup=_create_keyboard(buttons)) -class AbstractTelegramInterface(MessengerInterface): # pragma: no cover +class _AbstractTelegramInterface(MessengerInterface): # pragma: no cover def __init__(self, token: str) -> None: self.application = Application.builder().token(token).build() self.application.add_handler(MessageHandler(ALL, self.on_message)) + self.application.add_handler(CallbackQueryHandler(self.on_callback)) async def on_message(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> None: - if update.effective_user is not None and update.message is not None: + if update.effective_chat is not None and update.message is not None: message = extract_message_from_telegram(update.message) - resp = self.callback(message, update.effective_user.id) + message.original_message = update + resp = self.callback(message, update.effective_chat.id) if resp.last_response is not None: - await cast_message_to_telegram_and_send(update.message, resp.last_response) + await cast_message_to_telegram_and_send(self.application.bot, update.effective_chat.id, resp.last_response) + + async def on_callback(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> None: + if update.effective_chat is not None and update.callback_query is not None: + message = Message(text=update.callback_query.data) + message.original_message = update + resp = self.callback(message, update.effective_chat.id) + if resp.last_response is not None: + await cast_message_to_telegram_and_send(self.application.bot, update.effective_chat.id, resp.last_response) async def connect(self, callback: PipelineRunnerFunction, *args, **kwargs): self.callback = callback -class PollingTelegramInterface(AbstractTelegramInterface): # pragma: no cover +class PollingTelegramInterface(_AbstractTelegramInterface): # pragma: no cover def __init__(self, token: str, interval: int = 2, timeout: int = 20) -> None: super().__init__(token) self.interval = interval @@ -121,7 +132,7 @@ async def connect(self, callback: PipelineRunnerFunction, *args, **kwargs): self.application.run_polling(poll_interval=self.interval, timeout=self.timeout, allowed_updates=Update.ALL_TYPES) -class CallbackTelegramInterface(AbstractTelegramInterface): # pragma: no cover +class CallbackTelegramInterface(_AbstractTelegramInterface): # pragma: no cover def __init__(self, token: str, host: str = "localhost", port: int = 844): super().__init__(token) self.listen = host @@ -130,3 +141,15 @@ def __init__(self, token: str, host: str = "localhost", port: int = 844): async def connect(self, callback: PipelineRunnerFunction, *args, **kwargs): await super().connect(callback, *args, **kwargs) self.application.run_webhook(listen=self.listen, port=self.port, allowed_updates=Update.ALL_TYPES) + + +def telegram_condition(func: Callable[[Update], bool]): + + def condition(ctx: Context, _: Pipeline, *__, **___): # pragma: no cover + last_request = ctx.last_request + if last_request is None: + return False + original_message = cast(Update, last_request.original_message) + return func(original_message) + + return condition diff --git a/dff/script/core/message.py b/dff/script/core/message.py index 5a47d80ba..034d60689 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -164,13 +164,31 @@ class Document(DataAttachment): pass -class Keyboard(DataModel): +class Button(DataModel): + """Represents a button of an inline keyboard.""" + + text: str + data: Optional[str] = None + + @field_validator("data") + @classmethod + def data_length_should_be_constrained(cls, value: Optional[str]) -> Optional[str]: + if value is None: + return value + value_size = len(value.encode("utf-8")) + if 1 >= value_size >= 64 and value: + return value + else: + raise ValueError(f"Unexpected data length: {value_size} bytes") + + +class Keyboard(Attachment): """ - This class is a DataModel that represents a keyboard object + This class is an Attachment that represents a keyboard object that can be used for a chatbot or messaging application. """ - buttons: List[List[str]] = Field(default_factory=list, min_length=1) + buttons: List[List[Button]] = Field(default_factory=list, min_length=1) def __eq__(self, other): if isinstance(other, Keyboard): diff --git a/tutorials/messengers/telegram/2_buttons.py b/tutorials/messengers/telegram/2_buttons.py index 6e5817fe0..a92851bc0 100644 --- a/tutorials/messengers/telegram/2_buttons.py +++ b/tutorials/messengers/telegram/2_buttons.py @@ -23,14 +23,9 @@ class is used to represent telegram message, import dff.script.conditions as cnd from dff.script import TRANSITIONS, RESPONSE -from dff.script.core.message import Button +from dff.script.core.message import Button, Keyboard, Message from dff.pipeline import Pipeline -from dff.messengers.telegram import ( - PollingTelegramInterface, - TelegramUI, - TelegramMessage, - RemoveKeyboard, -) +from dff.messengers.telegram import PollingTelegramInterface from dff.utils.testing.common import is_interactive_mode @@ -57,7 +52,7 @@ class is used to represent telegram message, }, }, "fallback": { - RESPONSE: TelegramMessage( + RESPONSE: Message( text="Finishing test, send /restart command to restart" ), TRANSITIONS: { @@ -70,40 +65,30 @@ class is used to represent telegram message, }, "general": { "native_keyboard": { - RESPONSE: TelegramMessage( + RESPONSE: Message( text="Question: What's 2 + 2?", - # In this case, we use telegram-specific classes. - # They derive from the generic ones and include more options, - # e.g. simple keyboard or inline keyboard. - ui=TelegramUI( - buttons=[ - Button(text="5"), - Button(text="4"), - ], - is_inline=False, - row_width=4, - ), + attachments=[ + Keyboard( + buttons=[ + [ + Button(text="5"), + Button(text="4"), + ], + ], + ), + ] ), TRANSITIONS: { - ("general", "success"): cnd.exact_match( - TelegramMessage(text="4") - ), + ("general", "success"): cnd.exact_match(Message(text="4")), ("general", "fail"): cnd.true(), }, }, "success": { - RESPONSE: TelegramMessage( - **{"text": "Success!", "ui": RemoveKeyboard()} - ), + RESPONSE: Message(text="Success!"), TRANSITIONS: {("root", "fallback"): cnd.true()}, }, "fail": { - RESPONSE: TelegramMessage( - **{ - "text": "Incorrect answer, type anything to try again", - "ui": RemoveKeyboard(), - } - ), + RESPONSE: Message(text="Incorrect answer, type anything to try again"), TRANSITIONS: {("general", "native_keyboard"): cnd.true()}, }, }, @@ -114,62 +99,65 @@ class is used to represent telegram message, # this variable is only for testing happy_path = ( ( - TelegramMessage(text="/start"), - TelegramMessage( + Message(text="/start"), + Message( text="Question: What's 2 + 2?", - ui=TelegramUI( - buttons=[ - Button(text="5"), - Button(text="4"), - ], - is_inline=False, - row_width=4, - ), + attachments= [ + Keyboard( + buttons=[ + [ + Button(text="5"), + Button(text="4"), + ], + ], + ), + ], ), ), ( - TelegramMessage(text="5"), - TelegramMessage( - text="Incorrect answer, type anything to try again", - ui=RemoveKeyboard(), - ), + Message(text="5"), + Message(text="Incorrect answer, type anything to try again"), ), ( - TelegramMessage(text="ok"), - TelegramMessage( + Message(text="ok"), + Message( text="Question: What's 2 + 2?", - ui=TelegramUI( - buttons=[ - Button(text="5"), - Button(text="4"), - ], - is_inline=False, - row_width=4, - ), + attachments= [ + Keyboard( + buttons=[ + [ + Button(text="5"), + Button(text="4"), + ], + ], + ), + ], ), ), ( - TelegramMessage(text="4"), - TelegramMessage(text="Success!", ui=RemoveKeyboard()), + Message(text="4"), + Message(text="Success!"), ), ( - TelegramMessage(text="Yay!"), - TelegramMessage( + Message(text="Yay!"), + Message( text="Finishing test, send /restart command to restart" ), ), ( - TelegramMessage(text="/start"), - TelegramMessage( + Message(text="/start"), + Message( text="Question: What's 2 + 2?", - ui=TelegramUI( - buttons=[ - Button(text="5"), - Button(text="4"), - ], - is_inline=False, - row_width=4, - ), + attachments= [ + Keyboard( + buttons=[ + [ + Button(text="5"), + Button(text="4"), + ], + ], + ), + ], ), ), ) diff --git a/tutorials/messengers/telegram/3_buttons_with_callback.py b/tutorials/messengers/telegram/3_buttons_with_callback.py index 10d54927f..0fdae39b3 100644 --- a/tutorials/messengers/telegram/3_buttons_with_callback.py +++ b/tutorials/messengers/telegram/3_buttons_with_callback.py @@ -25,13 +25,8 @@ class is used to represent telegram message, import dff.script.conditions as cnd from dff.script import TRANSITIONS, RESPONSE from dff.pipeline import Pipeline -from dff.script.core.message import Button -from dff.messengers.telegram import ( - PollingTelegramInterface, - TelegramUI, - TelegramMessage, -) -from dff.messengers.telegram.message import _ClickButton +from dff.script.core.message import Button, Keyboard, Message +from dff.messengers.telegram import PollingTelegramInterface from dff.utils.testing.common import is_interactive_mode @@ -59,7 +54,7 @@ class is used to represent telegram message, }, }, "fallback": { - RESPONSE: TelegramMessage( + RESPONSE: Message( text="Finishing test, send /restart command to restart" ), TRANSITIONS: { @@ -72,33 +67,34 @@ class is used to represent telegram message, }, "general": { "keyboard": { - RESPONSE: TelegramMessage( - **{ - "text": "Starting test! What's 9 + 10?", - "ui": TelegramUI( + RESPONSE: Message( + text="Starting test! What's 9 + 10?", + attachments=[ + Keyboard( buttons=[ - Button(text="19", payload="correct"), - Button(text="21", payload="wrong"), + [ + Button(text="19", data="correct"), + Button(text="21", data="wrong"), + ], ], - is_inline=True, ), - } + ], ), TRANSITIONS: { ("general", "success"): cnd.exact_match( - TelegramMessage(callback_query="correct") + Message(text="correct") ), ("general", "fail"): cnd.exact_match( - TelegramMessage(callback_query="wrong") + Message(text="wrong") ), }, }, "success": { - RESPONSE: TelegramMessage(text="Success!"), + RESPONSE: Message(text="Success!"), TRANSITIONS: {("root", "fallback"): cnd.true()}, }, "fail": { - RESPONSE: TelegramMessage( + RESPONSE: Message( text="Incorrect answer, type anything to try again" ), TRANSITIONS: {("general", "keyboard"): cnd.true()}, @@ -109,53 +105,65 @@ class is used to represent telegram message, # this variable is only for testing happy_path = ( ( - TelegramMessage(text="/start"), - TelegramMessage( + Message(text="/start"), + Message( text="Starting test! What's 9 + 10?", - ui=TelegramUI( - buttons=[ - Button(text="19", payload="correct"), - Button(text="21", payload="wrong"), - ], - ), + attachments=[ + Keyboard( + buttons=[ + [ + Button(text="19", data="correct"), + Button(text="21", data="wrong"), + ], + ], + ), + ], ), ), ( - TelegramMessage(callback_query=_ClickButton(button_index=1)), - TelegramMessage(text="Incorrect answer, type anything to try again"), + Message(text="wrong"), + Message(text="Incorrect answer, type anything to try again"), ), ( - TelegramMessage(text="try again"), - TelegramMessage( + Message(text="try again"), + Message( text="Starting test! What's 9 + 10?", - ui=TelegramUI( - buttons=[ - Button(text="19", payload="correct"), - Button(text="21", payload="wrong"), - ], - ), + attachments=[ + Keyboard( + buttons=[ + [ + Button(text="19", data="correct"), + Button(text="21", data="wrong"), + ], + ], + ), + ], ), ), ( - TelegramMessage(callback_query=_ClickButton(button_index=0)), - TelegramMessage(text="Success!"), + Message(text="correct"), + Message(text="Success!"), ), ( - TelegramMessage(text="Yay!"), - TelegramMessage( + Message(text="Yay!"), + Message( text="Finishing test, send /restart command to restart" ), ), ( - TelegramMessage(text="/restart"), - TelegramMessage( + Message(text="/restart"), + Message( text="Starting test! What's 9 + 10?", - ui=TelegramUI( - buttons=[ - Button(text="19", payload="correct"), - Button(text="21", payload="wrong"), - ], - ), + attachments=[ + Keyboard( + buttons=[ + [ + Button(text="19", data="correct"), + Button(text="21", data="wrong"), + ], + ], + ), + ], ), ), ) diff --git a/tutorials/messengers/telegram/4_conditions.py b/tutorials/messengers/telegram/4_conditions.py index d856e3056..347597202 100644 --- a/tutorials/messengers/telegram/4_conditions.py +++ b/tutorials/messengers/telegram/4_conditions.py @@ -17,13 +17,10 @@ from dff.script import TRANSITIONS, RESPONSE -from dff.messengers.telegram import ( - PollingTelegramInterface, - telegram_condition, - UpdateType, -) +import dff.script.conditions as cnd +from dff.messengers.telegram import PollingTelegramInterface, telegram_condition from dff.pipeline import Pipeline -from dff.messengers.telegram import TelegramMessage +from dff.script.core.message import Message from dff.utils.testing.common import is_interactive_mode @@ -63,47 +60,59 @@ "greeting_flow": { "start_node": { TRANSITIONS: { - "node1": telegram_condition(commands=["start", "restart"]) + "node1": cnd.any( + [ + cnd.exact_match(Message(text="start")), + cnd.exact_match(Message(text="restart")), + ] + ) }, }, "node1": { - RESPONSE: TelegramMessage(text="Hi, how are you?"), + RESPONSE: Message(text="Hi, how are you?"), TRANSITIONS: { - "node2": telegram_condition( - update_type=UpdateType.MESSAGE, regexp="fine" - ) + "node2": cnd.regexp("fine") }, # this is the same as # TRANSITIONS: {"node2": telegram_condition(regexp="fine")}, }, "node2": { - RESPONSE: TelegramMessage( + RESPONSE: Message( text="Good. What do you want to talk about?" ), TRANSITIONS: { "node3": telegram_condition( - func=lambda msg: "music" in msg.text + func=lambda upd: ( + upd.message is not None + and upd.message.text is not None + and "music" in upd.message.text + ) ) }, }, "node3": { - RESPONSE: TelegramMessage( + RESPONSE: Message( text="Sorry, I can not talk about music now." ), TRANSITIONS: { - "node4": telegram_condition(update_type=UpdateType.ALL) + "node4": telegram_condition(func=lambda _: True) }, # This condition is true for any type of update }, "node4": { - RESPONSE: TelegramMessage(text="bye"), - TRANSITIONS: {"node1": telegram_condition()}, + RESPONSE: Message(text="bye"), + TRANSITIONS: {"node1": telegram_condition(func=lambda _: True)}, # This condition is true if the last update is of type `message` }, "fallback_node": { - RESPONSE: TelegramMessage(text="Ooops"), + RESPONSE: Message(text="Ooops"), TRANSITIONS: { - "node1": telegram_condition(commands=["start", "restart"]) + "node1": cnd.any( + [ + cnd.exact_match(Message(text="start")), + cnd.exact_match(Message(text="restart")), + ] + ) }, }, } @@ -111,17 +120,17 @@ # this variable is only for testing happy_path = ( - (TelegramMessage(text="/start"), TelegramMessage(text="Hi, how are you?")), + (Message(text="/start"), Message(text="Hi, how are you?")), ( - TelegramMessage(text="I'm fine"), - TelegramMessage(text="Good. What do you want to talk about?"), + Message(text="I'm fine"), + Message(text="Good. What do you want to talk about?"), ), ( - TelegramMessage(text="About music"), - TelegramMessage(text="Sorry, I can not talk about music now."), + Message(text="About music"), + Message(text="Sorry, I can not talk about music now."), ), - (TelegramMessage(text="ok"), TelegramMessage(text="bye")), - (TelegramMessage(text="bye"), TelegramMessage(text="Hi, how are you?")), + (Message(text="ok"), Message(text="bye")), + (Message(text="bye"), Message(text="Hi, how are you?")), ) diff --git a/tutorials/messengers/telegram/5_conditions_with_media.py b/tutorials/messengers/telegram/5_conditions_with_media.py index 144ef9c32..6e39b34dc 100644 --- a/tutorials/messengers/telegram/5_conditions_with_media.py +++ b/tutorials/messengers/telegram/5_conditions_with_media.py @@ -17,24 +17,22 @@ # %% import os +from typing import cast -from telebot.types import Message +from pydantic import HttpUrl +from telegram import Update, Message as TelegramMessage 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.script.core.message import Message, Image +from dff.messengers.telegram import PollingTelegramInterface, telegram_condition from dff.pipeline import Pipeline from dff.utils.testing.common import is_interactive_mode # %% -picture_url = "https://avatars.githubusercontent.com/u/29918795?s=200&v=4" +picture_url = HttpUrl("https://avatars.githubusercontent.com/u/29918795?s=200&v=4") # %% [markdown] @@ -53,25 +51,31 @@ "root": { "start": { TRANSITIONS: { - ("pics", "ask_picture"): telegram_condition( - commands=["start", "restart"] + ("pics", "ask_picture"): cnd.any( + [ + cnd.exact_match(Message(text="start")), + cnd.exact_match(Message(text="restart")), + ] ) }, }, "fallback": { - RESPONSE: TelegramMessage( + RESPONSE: Message( text="Finishing test, send /restart command to restart" ), TRANSITIONS: { - ("pics", "ask_picture"): telegram_condition( - commands=["start", "restart"] + ("pics", "ask_picture"): cnd.any( + [ + cnd.exact_match(Message(text="start")), + cnd.exact_match(Message(text="restart")), + ] ) }, }, }, "pics": { "ask_picture": { - RESPONSE: TelegramMessage(text="Send me a picture"), + RESPONSE: Message(text="Send me a picture"), TRANSITIONS: { ("pics", "send_one"): cnd.any( [ @@ -79,36 +83,41 @@ # 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: ( + func=lambda update: ( + update.message is not None + and len(update.message.photo) > 0 + ) + ), + telegram_condition( + func=lambda update: ( # check attachments in message properties - message.document - and message.document.mime_type == "image/jpeg" - ), - content_types=["document"], + update.message is not None + and update.message.document is not None + and update.message.document.mime_type == "image/jpeg" + ) ), ] ), ("pics", "send_many"): telegram_condition( - content_types=["text"] + func=lambda upd: upd.message is not None and upd.message.text is not None ), ("pics", "ask_picture"): cnd.true(), }, }, "send_one": { # An HTTP path or a path to a local file can be used here. - RESPONSE: TelegramMessage( + RESPONSE: Message( text="Here's my picture!", - attachments=Attachments(files=[Image(source=picture_url)]), + attachments=[Image(source=picture_url)], ), TRANSITIONS: {("root", "fallback"): cnd.true()}, }, "send_many": { - RESPONSE: TelegramMessage( + RESPONSE: Message( 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), + attachments=list(tuple([Image(source=picture_url)] * 2)), ), TRANSITIONS: {("root", "fallback"): cnd.true()}, }, @@ -119,69 +128,71 @@ # testing happy_path = ( ( - TelegramMessage(text="/start"), - TelegramMessage(text="Send me a picture"), + Message(text="/start"), + Message(text="Send me a picture"), ), ( - TelegramMessage( - attachments=Attachments(files=[Image(source=picture_url)]) + Message( + attachments=[Image(source=picture_url)] ), - TelegramMessage( + Message( text="Here's my picture!", - attachments=Attachments(files=[Image(source=picture_url)]), + attachments=[Image(source=picture_url)], ), ), ( - TelegramMessage(text="ok"), - TelegramMessage( + Message(text="ok"), + Message( text="Finishing test, send /restart command to restart" ), ), ( - TelegramMessage(text="/restart"), - TelegramMessage(text="Send me a picture"), + Message(text="/restart"), + Message(text="Send me a picture"), ), ( - TelegramMessage(text="No"), - TelegramMessage( + Message(text="No"), + Message( text="Look at my pictures!", - attachments=Attachments(files=[Image(source=picture_url)] * 2), + attachments=list(tuple([Image(source=picture_url)] * 2)), ), ), ( - TelegramMessage(text="ok"), - TelegramMessage( + Message(text="ok"), + Message( text="Finishing test, send /restart command to restart" ), ), ( - TelegramMessage(text="/restart"), - TelegramMessage(text="Send me a picture"), + Message(text="/restart"), + Message(text="Send me a picture"), ), ) # %% -def extract_data(ctx: Context, _: Pipeline): # A function to extract data with +async 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: + original_update = cast(Update, message.original_message) + if original_update is None: + return + if not isinstance(original_update, Update): + return + original_message = original_update.message + if not isinstance(original_message, TelegramMessage): return - if not isinstance(update, Message): + if original_message is None: return if ( # check attachments in update properties - not update.photo - and not (update.document and update.document.mime_type == "image/jpeg") + len(original_message.photo) > 0 + and not (original_message.document is not None and original_message.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) + photo = original_message.document or original_message.photo[-1] + await (await photo.get_file()).download_to_drive("photo.jpg") return diff --git a/tutorials/messengers/telegram/6_conditions_extras.py b/tutorials/messengers/telegram/6_conditions_extras.py index c154a2ed7..cf4d22e65 100644 --- a/tutorials/messengers/telegram/6_conditions_extras.py +++ b/tutorials/messengers/telegram/6_conditions_extras.py @@ -19,13 +19,9 @@ from dff.script import TRANSITIONS, RESPONSE, GLOBAL import dff.script.conditions as cnd -from dff.messengers.telegram import ( - PollingTelegramInterface, - TelegramMessage, - telegram_condition, - UpdateType, -) +from dff.messengers.telegram import PollingTelegramInterface, telegram_condition from dff.pipeline import Pipeline +from dff.script.core.message import Message from dff.utils.testing.common import is_interactive_mode @@ -67,38 +63,41 @@ [ # say hi when invited to a chat telegram_condition( - update_type=UpdateType.CHAT_JOIN_REQUEST, - func=lambda x: True, + func=lambda x: x.chat_join_request is not None, ), # say hi when someone enters the chat telegram_condition( - update_type=UpdateType.MY_CHAT_MEMBER, - func=lambda x: True, + func=lambda x: x.my_chat_member is not None, ), ] ), # send a message when inline query is received ("greeting_flow", "node2"): telegram_condition( - update_type=UpdateType.INLINE_QUERY, + func=lambda x: x.inline_query is not None, ), }, }, "greeting_flow": { "start_node": { TRANSITIONS: { - "node1": telegram_condition(commands=["start", "restart"]) + "node1": cnd.any( + [ + cnd.exact_match(Message(text="start")), + cnd.exact_match(Message(text="restart")), + ] + ) }, }, "node1": { - RESPONSE: TelegramMessage(text="Hi"), + RESPONSE: Message(text="Hi"), TRANSITIONS: {"start_node": cnd.true()}, }, "node2": { - RESPONSE: TelegramMessage(text="Inline query received."), + RESPONSE: Message(text="Inline query received."), TRANSITIONS: {"start_node": cnd.true()}, }, "fallback_node": { - RESPONSE: TelegramMessage(text="Ooops"), + RESPONSE: Message(text="Ooops"), }, }, } diff --git a/tutorials/messengers/telegram/7_polling_setup.py b/tutorials/messengers/telegram/7_polling_setup.py index d070e4728..48ceb5ede 100644 --- a/tutorials/messengers/telegram/7_polling_setup.py +++ b/tutorials/messengers/telegram/7_polling_setup.py @@ -13,12 +13,11 @@ # %% import os -from dff.messengers.telegram.interface import PollingTelegramInterface +from dff.messengers.telegram import PollingTelegramInterface from dff.pipeline import Pipeline from dff.utils.testing.common import is_interactive_mode from dff.utils.testing.toy_script import TOY_SCRIPT_ARGS, HAPPY_PATH -from telebot.util import update_types # %% [markdown] @@ -37,9 +36,7 @@ interface = PollingTelegramInterface( token=os.environ["TG_BOT_TOKEN"], interval=2, - allowed_updates=update_types, timeout=30, - long_polling_timeout=30, ) diff --git a/tutorials/messengers/telegram/8_webhook_setup.py b/tutorials/messengers/telegram/8_webhook_setup.py index a7f4fd68f..f29e24620 100644 --- a/tutorials/messengers/telegram/8_webhook_setup.py +++ b/tutorials/messengers/telegram/8_webhook_setup.py @@ -14,9 +14,7 @@ # %% import os -from dff.messengers.telegram import ( - CallbackTelegramInterface, -) +from dff.messengers.telegram import CallbackTelegramInterface from dff.pipeline import Pipeline from dff.utils.testing.toy_script import TOY_SCRIPT_ARGS, HAPPY_PATH from dff.utils.testing.common import is_interactive_mode From 89fa2c781cc5629f9f9eb25015cb6e30588e77ec Mon Sep 17 00:00:00 2001 From: pseusys Date: Wed, 31 Jan 2024 12:36:10 +0100 Subject: [PATCH 005/140] telegram examples debugged --- dff/messengers/telegram.py | 46 ++++--- dff/pipeline/types.py | 2 +- dff/script/core/message.py | 2 +- dff/utils/testing/telegram.py | 17 +-- tests/messengers/telegram/test_types.py | 130 ++++++++---------- tutorials/messengers/telegram/4_conditions.py | 8 +- .../telegram/5_conditions_with_media.py | 35 +---- .../telegram/6_conditions_extras.py | 18 +-- 8 files changed, 106 insertions(+), 152 deletions(-) diff --git a/dff/messengers/telegram.py b/dff/messengers/telegram.py index 550649987..d869139b0 100644 --- a/dff/messengers/telegram.py +++ b/dff/messengers/telegram.py @@ -4,12 +4,15 @@ This module implements various interfaces for :py:class:`~dff.messengers.telegram.messenger.TelegramMessenger` that can be used to interact with the Telegram API. """ +from pathlib import Path +from tempfile import gettempdir from typing import Callable, Optional, Sequence, cast -from pydantic import HttpUrl +from pydantic import FilePath from telegram import InlineKeyboardButton, InlineKeyboardMarkup, InputMediaAnimation, InputMediaAudio, InputMediaDocument, InputMediaPhoto, InputMediaVideo, Update, Message as TelegramMessage from telegram.ext import Application, ExtBot, MessageHandler, CallbackQueryHandler, ContextTypes from telegram.ext.filters import ALL +from telegram._files._basemedium import _BaseMedium from dff.messengers.common import MessengerInterface from dff.pipeline import Pipeline @@ -18,7 +21,14 @@ from dff.script.core.message import Animation, Audio, Button, Contact, Document, Image, Invoice, Keyboard, Location, Message, Poll, PollOption, Video -def extract_message_from_telegram(update: TelegramMessage) -> Message: # pragma: no cover +async def _download_telegram_file(file: _BaseMedium) -> FilePath: # pragma: no cover + file_name = Path(gettempdir()) / file.file_unique_id + if not file_name.exists(): + await (await file.get_file()).download_to_drive(file_name) + return FilePath(file_name) + + +async def extract_message_from_telegram(update: TelegramMessage) -> Message: # pragma: no cover message = Message() message.attachments = list() @@ -33,23 +43,23 @@ def extract_message_from_telegram(update: TelegramMessage) -> Message: # pragma if update.poll is not None: message.attachments += [Poll(question=update.poll.question, options=[PollOption(text=option.text, votes=option.voter_count) for option in update.poll.options])] if update.audio is not None: - message.attachments += [Audio(source=HttpUrl(update.audio.file_id))] + message.attachments += [Audio(source=await _download_telegram_file(update.audio))] if update.video is not None: - message.attachments += [Video(source=HttpUrl(update.video.file_id))] + message.attachments += [Video(source=await _download_telegram_file(update.video))] if update.animation is not None: - message.attachments += [Animation(source=HttpUrl(update.animation.file_id))] + message.attachments += [Animation(source=await _download_telegram_file(update.animation))] if len(update.photo) > 0: - message.attachments += [Image(source=HttpUrl(photo.file_id)) for photo in update.photo] + message.attachments += [Image(source=await _download_telegram_file(photo)) for photo in update.photo] if update.document is not None: - message.attachments += [Document(source=HttpUrl(update.document.file_id))] + message.attachments += [Document(source=await _download_telegram_file(update.document))] return message -def _create_keyboard(buttons: Sequence[Sequence[Button]]) -> Optional[InlineKeyboardMarkup]: +def _create_keyboard(buttons: Sequence[Sequence[Button]]) -> Optional[InlineKeyboardMarkup]: # pragma: no cover button_list = None if len(buttons) > 0: - button_list = [[InlineKeyboardButton(text=button.text, callback_data=button.data) for button in row] for row in buttons] + button_list = [[InlineKeyboardButton(text=button.text, callback_data=button.data if button.data is not None else button.text) for button in row] for row in buttons] if button_list is None: return None else: @@ -63,10 +73,13 @@ async def cast_message_to_telegram_and_send(bot: ExtBot, chat_id: int, message: for attachment in message.attachments: if isinstance(attachment, Location): await bot.send_location(chat_id, attachment.latitude, attachment.longitude, reply_markup=_create_keyboard(buttons)) + return if isinstance(attachment, Contact): await bot.send_contact(chat_id, attachment.phone_number, attachment.first_name, attachment.last_name, reply_markup=_create_keyboard(buttons)) + return if isinstance(attachment, Poll): await bot.send_poll(chat_id, attachment.question, [option.text for option in attachment.options], reply_markup=_create_keyboard(buttons)) + return if isinstance(attachment, Audio): attachment_bytes = attachment.get_bytes() if attachment_bytes is not None: @@ -89,9 +102,10 @@ async def cast_message_to_telegram_and_send(bot: ExtBot, chat_id: int, message: files += [InputMediaDocument(attachment_bytes)] if isinstance(attachment, Keyboard): buttons = attachment.buttons - if len(files > 0): + if len(files) > 0: await bot.send_media_group(chat_id, files, caption=message.text) - elif message.text is not None: + return + if message.text is not None: await bot.send_message(chat_id, message.text, reply_markup=_create_keyboard(buttons)) @@ -103,9 +117,9 @@ def __init__(self, token: str) -> None: async def on_message(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> None: if update.effective_chat is not None and update.message is not None: - message = extract_message_from_telegram(update.message) + message = await extract_message_from_telegram(update.message) message.original_message = update - resp = self.callback(message, update.effective_chat.id) + resp = await self.callback(message, update.effective_chat.id) if resp.last_response is not None: await cast_message_to_telegram_and_send(self.application.bot, update.effective_chat.id, resp.last_response) @@ -113,7 +127,7 @@ async def on_callback(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> Non if update.effective_chat is not None and update.callback_query is not None: message = Message(text=update.callback_query.data) message.original_message = update - resp = self.callback(message, update.effective_chat.id) + resp = await self.callback(message, update.effective_chat.id) if resp.last_response is not None: await cast_message_to_telegram_and_send(self.application.bot, update.effective_chat.id, resp.last_response) @@ -143,11 +157,11 @@ async def connect(self, callback: PipelineRunnerFunction, *args, **kwargs): self.application.run_webhook(listen=self.listen, port=self.port, allowed_updates=Update.ALL_TYPES) -def telegram_condition(func: Callable[[Update], bool]): +def telegram_condition(func: Callable[[Update], bool]): # pragma: no cover def condition(ctx: Context, _: Pipeline, *__, **___): # pragma: no cover last_request = ctx.last_request - if last_request is None: + if last_request is None or last_request.original_message is None: return False original_message = cast(Update, last_request.original_message) return func(original_message) diff --git a/dff/pipeline/types.py b/dff/pipeline/types.py index ef7bef9ae..80ee9d116 100644 --- a/dff/pipeline/types.py +++ b/dff/pipeline/types.py @@ -32,7 +32,7 @@ class PipelineRunnerFunction(Protocol): def __call__( self, message: Message, ctx_id: Optional[Hashable] = None, update_ctx_misc: Optional[dict] = None - ) -> Context: + ) -> Awaitable[Context]: """ :param message: User request for pipeline to process. :param ctx_id: diff --git a/dff/script/core/message.py b/dff/script/core/message.py index 034d60689..640e50eb9 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -176,7 +176,7 @@ def data_length_should_be_constrained(cls, value: Optional[str]) -> Optional[str if value is None: return value value_size = len(value.encode("utf-8")) - if 1 >= value_size >= 64 and value: + if 1 <= value_size <= 64 and value: return value else: raise ValueError(f"Unexpected data length: {value_size} bytes") diff --git a/dff/utils/testing/telegram.py b/dff/utils/testing/telegram.py index d06ba3bb6..85c6b6a7e 100644 --- a/dff/utils/testing/telegram.py +++ b/dff/utils/testing/telegram.py @@ -11,16 +11,9 @@ from pathlib import Path from copy import deepcopy -from telethon.tl.types import ReplyKeyboardHide -from telethon import TelegramClient -from telethon.types import User -from telethon.custom import Message as TlMessage -from telebot import types - from dff.pipeline.pipeline.pipeline import Pipeline -from dff.script.core.message import Message, Attachments, Attachment, Button, Location -from dff.messengers.telegram.interface import PollingTelegramInterface -from dff.messengers.telegram.message import TelegramMessage, TelegramUI, RemoveKeyboard, _ClickButton +from dff.script.core.message import Message, Attachment, Location +from dff.messengers.telegram import PollingTelegramInterface def replace_click_button(happy_path): @@ -34,12 +27,12 @@ def replace_click_button(happy_path): result = deepcopy(happy_path) for index in range(len(happy_path)): user_request = happy_path[index][0] - if not isinstance(user_request, TelegramMessage): + if not isinstance(user_request, Message): continue if isinstance(user_request.callback_query, _ClickButton): callback_query = None for _, bot_response in reversed(happy_path[:index]): - if isinstance(bot_response, TelegramMessage) and bot_response.ui is not None and callback_query is None: + if isinstance(bot_response, Message) and bot_response.ui is not None and callback_query is None: callback_query = bot_response.ui.buttons[user_request.callback_query.button_index].payload if callback_query is None: raise RuntimeError("Bot response with buttons not found.") @@ -98,7 +91,7 @@ def __init__( self.bot = bot """Bot user (to know whom to send messages to from client).""" - async def send_message(self, message: TelegramMessage, last_bot_messages: List[TlMessage]): + async def send_message(self, message: Message, last_bot_messages: List[TlMessage]): """ Send a message from client to bot. If the message contains `callback_query`, only press the button, ignore other fields. diff --git a/tests/messengers/telegram/test_types.py b/tests/messengers/telegram/test_types.py index cc50ae558..01f1b0063 100644 --- a/tests/messengers/telegram/test_types.py +++ b/tests/messengers/telegram/test_types.py @@ -5,39 +5,28 @@ import pytest try: - import telebot # noqa: F401 - import telethon # noqa: F401 + import telegram # noqa: F401 except ImportError: pytest.skip(reason="`telegram` is not available", allow_module_level=True) -from pydantic import ValidationError -from telebot import types +from pydantic import FilePath, HttpUrl, ValidationError -from dff.messengers.telegram.message import ( - TelegramMessage, - TelegramUI, - RemoveKeyboard, - ParseMode, -) from dff.script import Message -from dff.script.core.message import Attachments, Keyboard, Button, Image, Location, Audio, Video, Attachment, Document -from dff.messengers.telegram.utils import open_io, close_io, batch_open_io +from dff.script.core.message import Button, DataAttachment, Keyboard, Image, Location, Audio, Video, Attachment, Document from dff.utils.testing.telegram import TelegramTesting -image = Image( - source="https://gist.githubusercontent.com/scotthaleen/32f76a413e0dfd4b4d79c2a534d49c0b/raw" - "/6c1036b1eca90b341caf06d4056d36f64fc11e88/tiny.jpg" -) -audio = Audio(source="https://github.com/mathiasbynens/small/raw/master/mp3.mp3") -video = Video(source="https://github.com/mathiasbynens/small/raw/master/Mpeg4.mp4") -document = Document(source="https://github.com/mathiasbynens/small/raw/master/pdf.pdf") +_image_link = "https://gist.githubusercontent.com/scotthaleen/32f76a413e0dfd4b4d79c2a534d49c0b/raw/6c1036b1eca90b341caf06d4056d36f64fc11e88/tiny.jpg" +image = Image(source=HttpUrl(_image_link)) +audio = Audio(source=HttpUrl("https://github.com/mathiasbynens/small/raw/master/mp3.mp3")) +video = Video(source=HttpUrl("https://github.com/mathiasbynens/small/raw/master/Mpeg4.mp4")) +document = Document(source=HttpUrl("https://github.com/mathiasbynens/small/raw/master/pdf.pdf")) @pytest.mark.asyncio @pytest.mark.telegram async def test_text(pipeline_instance, api_credentials, bot_user, session_file): - telegram_response = TelegramMessage(text="test") + telegram_response = Message(text="test") test_helper = TelegramTesting( pipeline=pipeline_instance, api_credentials=api_credentials, session_file=session_file, bot=bot_user ) @@ -46,48 +35,37 @@ async def test_text(pipeline_instance, api_credentials, bot_user, session_file): @pytest.mark.asyncio @pytest.mark.parametrize( - ["ui", "markup_type", "button_type"], + ["keyboard"], [ - ( - Keyboard( - buttons=[ - Button(text="button 1", payload=json.dumps({"text": "1", "other_prop": "4"})), - Button(text="button 2", payload=json.dumps({"text": "2", "other_prop": "3"})), - Button(text="button 3", payload=json.dumps({"text": "3", "other_prop": "2"})), - Button(text="button 4", payload=json.dumps({"text": "4", "other_prop": "1"})), + Keyboard( + buttons=[ + [ + Button(text="button 1", data=json.dumps({"text": "1", "other_prop": "4"})), + Button(text="button 2", data=json.dumps({"text": "2", "other_prop": "3"})), + ], + [ + Button(text="button 3", data=json.dumps({"text": "3", "other_prop": "2"})), + Button(text="button 4", data=json.dumps({"text": "4", "other_prop": "1"})), ] - ), - types.InlineKeyboardMarkup, - types.InlineKeyboardButton, + ] ), - ( - TelegramUI( - is_inline=False, - buttons=[ + Keyboard( + buttons=[ + [ Button(text="button 1"), Button(text="button 2"), + ], + [ Button(text="button 3"), Button(text="button 4"), - ], - ), - types.ReplyKeyboardMarkup, - types.KeyboardButton, + ] + ], ), ], ) @pytest.mark.telegram -async def test_buttons(ui, button_type, markup_type, pipeline_instance, api_credentials, bot_user, session_file): - telegram_response = TelegramMessage(text="test", ui=ui) - test_helper = TelegramTesting( - pipeline=pipeline_instance, api_credentials=api_credentials, session_file=session_file, bot=bot_user - ) - await test_helper.send_and_check(telegram_response) - - -@pytest.mark.asyncio -@pytest.mark.telegram -async def test_keyboard_remove(pipeline_instance, api_credentials, bot_user, session_file): - telegram_response = TelegramMessage(text="test", ui=RemoveKeyboard()) +async def test_buttons(keyboard: Keyboard, pipeline_instance, api_credentials, bot_user, session_file): + telegram_response = Message(text="test", attachments=[keyboard]) test_helper = TelegramTesting( pipeline=pipeline_instance, api_credentials=api_credentials, session_file=session_file, bot=bot_user ) @@ -101,27 +79,32 @@ async def test_keyboard_remove(pipeline_instance, api_credentials, bot_user, ses ( Message( text="test", - attachments=Attachments(files=[image]), + attachments=[image], + ), + ), + ( + Message( + text="test", + attachments=[audio], ), ), ( Message( text="test", - attachments=Attachments(files=[audio]), + attachments=[video], ), ), ( Message( text="test", - attachments=Attachments(files=[video]), + attachments=[document], ), ), - (Message(text="test", attachments=Attachments(files=[document])),), ], ) @pytest.mark.telegram async def test_telegram_attachment(generic_response, pipeline_instance, api_credentials, bot_user, session_file): - telegram_response = TelegramMessage.model_validate(generic_response) + telegram_response = Message.model_validate(generic_response) test_helper = TelegramTesting( pipeline=pipeline_instance, api_credentials=api_credentials, session_file=session_file, bot=bot_user ) @@ -135,27 +118,32 @@ async def test_telegram_attachment(generic_response, pipeline_instance, api_cred ( Message( text="test", - attachments=Attachments(files=2 * [image]), + attachments=list(tuple(2 * [image])), + ), + ), + ( + Message( + text="test", + attachments=list(tuple(2 * [audio])), ), ), ( Message( text="test", - attachments=Attachments(files=2 * [audio]), + attachments=list(tuple(2 * [video])), ), ), ( Message( text="test", - attachments=Attachments(files=2 * [video]), + attachments=list(tuple(2 * [document])), ), ), - (Message(text="test", attachments=Attachments(files=2 * [document])),), ], ) @pytest.mark.telegram async def test_attachments(attachments, pipeline_instance, api_credentials, bot_user, session_file): - telegram_response = TelegramMessage.model_validate(attachments) + telegram_response = Message.model_validate(attachments) test_helper = TelegramTesting( pipeline=pipeline_instance, api_credentials=api_credentials, session_file=session_file, bot=bot_user ) @@ -165,7 +153,7 @@ async def test_attachments(attachments, pipeline_instance, api_credentials, bot_ @pytest.mark.asyncio @pytest.mark.telegram async def test_location(pipeline_instance, api_credentials, bot_user, session_file): - telegram_response = TelegramMessage(text="location", location=Location(longitude=39.0, latitude=43.0)) + telegram_response = Message(text="location", attachments=[Location(longitude=39.0, latitude=43.0)]) test_helper = TelegramTesting( pipeline=pipeline_instance, api_credentials=api_credentials, session_file=session_file, bot=bot_user ) @@ -175,7 +163,7 @@ async def test_location(pipeline_instance, api_credentials, bot_user, session_fi @pytest.mark.asyncio @pytest.mark.telegram async def test_parsed_text(pipeline_instance, api_credentials, bot_user, session_file): - telegram_response = TelegramMessage(text="[inline URL](http://www.example.com/)", parse_mode=ParseMode.MARKDOWN) + telegram_response = Message(text="[inline URL](http://www.example.com/)", parse_mode=ParseMode.MARKDOWN) test_helper = TelegramTesting( pipeline=pipeline_instance, api_credentials=api_credentials, session_file=session_file, bot=bot_user ) @@ -184,29 +172,23 @@ async def test_parsed_text(pipeline_instance, api_credentials, bot_user, session def test_missing_error(): with pytest.raises(ValidationError) as e: - _ = Attachment(source="http://google.com", id="123") + _ = DataAttachment(source=HttpUrl("http://google.com"), id="123") assert e with pytest.raises(ValidationError) as e: - _ = Attachment(source="/etc/missing_file") - assert e - - -def test_attachment_error(): - with pytest.raises(ValidationError) as e: - _ = Attachments(files=["a", "b"]) + _ = DataAttachment(source=FilePath("/etc/missing_file")) assert e def test_empty_keyboard(): with pytest.raises(ValidationError) as e: - _ = TelegramUI(buttons=[]) + _ = Keyboard(buttons=[]) assert e -def test_non_inline_keyboard_with_payload(): +def test_long_button_data(): with pytest.raises(ValidationError) as error: - TelegramUI(buttons=[Button(text="", payload="")], is_inline=False) + Button(text="", data="test" * 64) assert error diff --git a/tutorials/messengers/telegram/4_conditions.py b/tutorials/messengers/telegram/4_conditions.py index 347597202..f32f791dd 100644 --- a/tutorials/messengers/telegram/4_conditions.py +++ b/tutorials/messengers/telegram/4_conditions.py @@ -62,8 +62,8 @@ TRANSITIONS: { "node1": cnd.any( [ - cnd.exact_match(Message(text="start")), - cnd.exact_match(Message(text="restart")), + cnd.exact_match(Message(text="/start")), + cnd.exact_match(Message(text="/restart")), ] ) }, @@ -109,8 +109,8 @@ TRANSITIONS: { "node1": cnd.any( [ - cnd.exact_match(Message(text="start")), - cnd.exact_match(Message(text="restart")), + cnd.exact_match(Message(text="/start")), + cnd.exact_match(Message(text="/restart")), ] ) }, diff --git a/tutorials/messengers/telegram/5_conditions_with_media.py b/tutorials/messengers/telegram/5_conditions_with_media.py index 6e39b34dc..b39dbd84a 100644 --- a/tutorials/messengers/telegram/5_conditions_with_media.py +++ b/tutorials/messengers/telegram/5_conditions_with_media.py @@ -53,8 +53,8 @@ TRANSITIONS: { ("pics", "ask_picture"): cnd.any( [ - cnd.exact_match(Message(text="start")), - cnd.exact_match(Message(text="restart")), + cnd.exact_match(Message(text="/start")), + cnd.exact_match(Message(text="/restart")), ] ) }, @@ -66,8 +66,8 @@ TRANSITIONS: { ("pics", "ask_picture"): cnd.any( [ - cnd.exact_match(Message(text="start")), - cnd.exact_match(Message(text="restart")), + cnd.exact_match(Message(text="/start")), + cnd.exact_match(Message(text="/restart")), ] ) }, @@ -170,39 +170,12 @@ ) -# %% -async def extract_data(ctx: Context, _: Pipeline): # A function to extract data with - message = ctx.last_request - if message is None: - return - original_update = cast(Update, message.original_message) - if original_update is None: - return - if not isinstance(original_update, Update): - return - original_message = original_update.message - if not isinstance(original_message, TelegramMessage): - return - if original_message is None: - return - if ( - # check attachments in update properties - len(original_message.photo) > 0 - and not (original_message.document is not None and original_message.document.mime_type == "image/jpeg") - ): - return - photo = original_message.document or original_message.photo[-1] - await (await photo.get_file()).download_to_drive("photo.jpg") - return - - # %% pipeline = Pipeline.from_script( script=script, start_label=("root", "start"), fallback_label=("root", "fallback"), messenger_interface=interface, - pre_services=[extract_data], ) diff --git a/tutorials/messengers/telegram/6_conditions_extras.py b/tutorials/messengers/telegram/6_conditions_extras.py index cf4d22e65..ea2b8da15 100644 --- a/tutorials/messengers/telegram/6_conditions_extras.py +++ b/tutorials/messengers/telegram/6_conditions_extras.py @@ -59,17 +59,9 @@ script = { GLOBAL: { TRANSITIONS: { - ("greeting_flow", "node1"): cnd.any( - [ - # say hi when invited to a chat - telegram_condition( - func=lambda x: x.chat_join_request is not None, - ), - # say hi when someone enters the chat - telegram_condition( - func=lambda x: x.my_chat_member is not None, - ), - ] + # say hi when someone enters the chat + ("greeting_flow", "node1"): telegram_condition( + func=lambda x: x.message is not None and x.message.new_chat_members is not None, ), # send a message when inline query is received ("greeting_flow", "node2"): telegram_condition( @@ -82,8 +74,8 @@ TRANSITIONS: { "node1": cnd.any( [ - cnd.exact_match(Message(text="start")), - cnd.exact_match(Message(text="restart")), + cnd.exact_match(Message(text="/start")), + cnd.exact_match(Message(text="/restart")), ] ) }, From 3745cff8208ccae2afaf0d18b57c6459f0afeceb Mon Sep 17 00:00:00 2001 From: pseusys Date: Wed, 31 Jan 2024 16:02:21 +0100 Subject: [PATCH 006/140] optional attachment processing added --- dff/messengers/telegram.py | 202 +++++++++--------- .../telegram/5_conditions_with_media.py | 2 +- 2 files changed, 105 insertions(+), 99 deletions(-) diff --git a/dff/messengers/telegram.py b/dff/messengers/telegram.py index d869139b0..f8d48e68c 100644 --- a/dff/messengers/telegram.py +++ b/dff/messengers/telegram.py @@ -6,7 +6,7 @@ """ from pathlib import Path from tempfile import gettempdir -from typing import Callable, Optional, Sequence, cast +from typing import Callable, Optional, Sequence, Type, cast from pydantic import FilePath from telegram import InlineKeyboardButton, InlineKeyboardMarkup, InputMediaAnimation, InputMediaAudio, InputMediaDocument, InputMediaPhoto, InputMediaVideo, Update, Message as TelegramMessage @@ -18,110 +18,116 @@ from dff.pipeline import Pipeline from dff.pipeline.types import PipelineRunnerFunction from dff.script.core.context import Context -from dff.script.core.message import Animation, Audio, Button, Contact, Document, Image, Invoice, Keyboard, Location, Message, Poll, PollOption, Video - - -async def _download_telegram_file(file: _BaseMedium) -> FilePath: # pragma: no cover - file_name = Path(gettempdir()) / file.file_unique_id - if not file_name.exists(): - await (await file.get_file()).download_to_drive(file_name) - return FilePath(file_name) - - -async def extract_message_from_telegram(update: TelegramMessage) -> Message: # pragma: no cover - message = Message() - message.attachments = list() - - if update.text is not None: - message.text = update.text - if update.location is not None: - message.attachments += [Location(latitude=update.location.latitude, longitude=update.location.longitude)] - if update.contact is not None: - message.attachments += [Contact(phone_number=update.contact.phone_number, first_name=update.contact.first_name, last_name=update.contact.last_name)] - if update.invoice is not None: - message.attachments += [Invoice(title=update.invoice.title, description=update.invoice.description, currency=update.invoice.currency, amount=update.invoice.total_amount)] - if update.poll is not None: - message.attachments += [Poll(question=update.poll.question, options=[PollOption(text=option.text, votes=option.voter_count) for option in update.poll.options])] - if update.audio is not None: - message.attachments += [Audio(source=await _download_telegram_file(update.audio))] - if update.video is not None: - message.attachments += [Video(source=await _download_telegram_file(update.video))] - if update.animation is not None: - message.attachments += [Animation(source=await _download_telegram_file(update.animation))] - if len(update.photo) > 0: - message.attachments += [Image(source=await _download_telegram_file(photo)) for photo in update.photo] - if update.document is not None: - message.attachments += [Document(source=await _download_telegram_file(update.document))] - - return message - - -def _create_keyboard(buttons: Sequence[Sequence[Button]]) -> Optional[InlineKeyboardMarkup]: # pragma: no cover - button_list = None - if len(buttons) > 0: - button_list = [[InlineKeyboardButton(text=button.text, callback_data=button.data if button.data is not None else button.text) for button in row] for row in buttons] - if button_list is None: - return None - else: - return InlineKeyboardMarkup(button_list) - - -async def cast_message_to_telegram_and_send(bot: ExtBot, chat_id: int, message: Message) -> None: # pragma: no cover - buttons = list() - if message.attachments is not None: - files = list() - for attachment in message.attachments: - if isinstance(attachment, Location): - await bot.send_location(chat_id, attachment.latitude, attachment.longitude, reply_markup=_create_keyboard(buttons)) - return - if isinstance(attachment, Contact): - await bot.send_contact(chat_id, attachment.phone_number, attachment.first_name, attachment.last_name, reply_markup=_create_keyboard(buttons)) - return - if isinstance(attachment, Poll): - await bot.send_poll(chat_id, attachment.question, [option.text for option in attachment.options], reply_markup=_create_keyboard(buttons)) - return - if isinstance(attachment, Audio): - attachment_bytes = attachment.get_bytes() - if attachment_bytes is not None: - files += [InputMediaAudio(attachment_bytes)] - if isinstance(attachment, Video): - attachment_bytes = attachment.get_bytes() - if attachment_bytes is not None: - files += [InputMediaVideo(attachment_bytes)] - if isinstance(attachment, Animation): - attachment_bytes = attachment.get_bytes() - if attachment_bytes is not None: - files += [InputMediaAnimation(attachment_bytes)] - if isinstance(attachment, Image): - attachment_bytes = attachment.get_bytes() - if attachment_bytes is not None: - files += [InputMediaPhoto(attachment_bytes)] - if isinstance(attachment, Document): - attachment_bytes = attachment.get_bytes() - if attachment_bytes is not None: - files += [InputMediaDocument(attachment_bytes)] - if isinstance(attachment, Keyboard): - buttons = attachment.buttons - if len(files) > 0: - await bot.send_media_group(chat_id, files, caption=message.text) - return - if message.text is not None: - await bot.send_message(chat_id, message.text, reply_markup=_create_keyboard(buttons)) +from dff.script.core.message import Animation, Audio, Button, Contact, DataAttachment, Document, Image, Invoice, Keyboard, Location, Message, Poll, PollOption, Video class _AbstractTelegramInterface(MessengerInterface): # pragma: no cover - def __init__(self, token: str) -> None: + def __init__(self, token: str, download_all_attachments: bool) -> None: self.application = Application.builder().token(token).build() self.application.add_handler(MessageHandler(ALL, self.on_message)) self.application.add_handler(CallbackQueryHandler(self.on_callback)) + self.download_all = download_all_attachments + + async def download_telegram_file(self, file: DataAttachment) -> Optional[FilePath]: # pragma: no cover + if file.title is not None and file.id is not None: + file_name = Path(gettempdir()) / str(file.title) + if not file_name.exists(): + await (await self.application.bot.get_file(file.id)).download_to_drive(file_name) + return FilePath(file_name) + else: + return None + + async def _process_attachment(self, attachment: _BaseMedium, download: bool, cls: Type[DataAttachment]) -> DataAttachment: # pragma: no cover + data_attachment = cls(id=attachment.file_id, title=attachment.file_unique_id) + if download: + data_attachment.source = await self.download_telegram_file(data_attachment) + return data_attachment + + async def extract_message_from_telegram(self, update: TelegramMessage) -> Message: # pragma: no cover + message = Message() + message.attachments = list() + + if update.text is not None: + message.text = update.text + if update.location is not None: + message.attachments += [Location(latitude=update.location.latitude, longitude=update.location.longitude)] + if update.contact is not None: + message.attachments += [Contact(phone_number=update.contact.phone_number, first_name=update.contact.first_name, last_name=update.contact.last_name)] + if update.invoice is not None: + message.attachments += [Invoice(title=update.invoice.title, description=update.invoice.description, currency=update.invoice.currency, amount=update.invoice.total_amount)] + if update.poll is not None: + message.attachments += [Poll(question=update.poll.question, options=[PollOption(text=option.text, votes=option.voter_count) for option in update.poll.options])] + if update.audio is not None: + message.attachments += [await self._process_attachment(update.audio, self.download_all, Audio)] + if update.video is not None: + message.attachments += [await self._process_attachment(update.video, self.download_all, Video)] + if update.animation is not None: + message.attachments += [await self._process_attachment(update.animation, self.download_all, Animation)] + if len(update.photo) > 0: + message.attachments += [await self._process_attachment(picture, self.download_all, Image) for picture in update.photo] + if update.document is not None: + message.attachments += [await self._process_attachment(update.document, self.download_all, Document)] + + return message + + def _create_keyboard(self, buttons: Sequence[Sequence[Button]]) -> Optional[InlineKeyboardMarkup]: # pragma: no cover + button_list = None + if len(buttons) > 0: + button_list = [[InlineKeyboardButton(text=button.text, callback_data=button.data if button.data is not None else button.text) for button in row] for row in buttons] + if button_list is None: + return None + else: + return InlineKeyboardMarkup(button_list) + + async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, message: Message) -> None: # pragma: no cover + buttons = list() + if message.attachments is not None: + files = list() + for attachment in message.attachments: + if isinstance(attachment, Location): + await bot.send_location(chat_id, attachment.latitude, attachment.longitude, reply_markup=self._create_keyboard(buttons)) + return + if isinstance(attachment, Contact): + await bot.send_contact(chat_id, attachment.phone_number, attachment.first_name, attachment.last_name, reply_markup=self._create_keyboard(buttons)) + return + if isinstance(attachment, Poll): + await bot.send_poll(chat_id, attachment.question, [option.text for option in attachment.options], reply_markup=self._create_keyboard(buttons)) + return + if isinstance(attachment, Audio): + attachment_bytes = attachment.get_bytes() + if attachment_bytes is not None: + files += [InputMediaAudio(attachment_bytes)] + if isinstance(attachment, Video): + attachment_bytes = attachment.get_bytes() + if attachment_bytes is not None: + files += [InputMediaVideo(attachment_bytes)] + if isinstance(attachment, Animation): + attachment_bytes = attachment.get_bytes() + if attachment_bytes is not None: + files += [InputMediaAnimation(attachment_bytes)] + if isinstance(attachment, Image): + attachment_bytes = attachment.get_bytes() + if attachment_bytes is not None: + files += [InputMediaPhoto(attachment_bytes)] + if isinstance(attachment, Document): + attachment_bytes = attachment.get_bytes() + if attachment_bytes is not None: + files += [InputMediaDocument(attachment_bytes)] + if isinstance(attachment, Keyboard): + buttons = attachment.buttons + if len(files) > 0: + await bot.send_media_group(chat_id, files, caption=message.text) + return + if message.text is not None: + await bot.send_message(chat_id, message.text, reply_markup=self._create_keyboard(buttons)) async def on_message(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> None: if update.effective_chat is not None and update.message is not None: - message = await extract_message_from_telegram(update.message) + message = await self.extract_message_from_telegram(update.message) message.original_message = update resp = await self.callback(message, update.effective_chat.id) if resp.last_response is not None: - await cast_message_to_telegram_and_send(self.application.bot, update.effective_chat.id, resp.last_response) + await self.cast_message_to_telegram_and_send(self.application.bot, update.effective_chat.id, resp.last_response) async def on_callback(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> None: if update.effective_chat is not None and update.callback_query is not None: @@ -129,15 +135,15 @@ async def on_callback(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> Non message.original_message = update resp = await self.callback(message, update.effective_chat.id) if resp.last_response is not None: - await cast_message_to_telegram_and_send(self.application.bot, update.effective_chat.id, resp.last_response) + await self.cast_message_to_telegram_and_send(self.application.bot, update.effective_chat.id, resp.last_response) async def connect(self, callback: PipelineRunnerFunction, *args, **kwargs): self.callback = callback class PollingTelegramInterface(_AbstractTelegramInterface): # pragma: no cover - def __init__(self, token: str, interval: int = 2, timeout: int = 20) -> None: - super().__init__(token) + def __init__(self, token: str, download_all_attachments: bool = False, interval: int = 2, timeout: int = 20) -> None: + super().__init__(token, download_all_attachments) self.interval = interval self.timeout = timeout @@ -147,8 +153,8 @@ async def connect(self, callback: PipelineRunnerFunction, *args, **kwargs): class CallbackTelegramInterface(_AbstractTelegramInterface): # pragma: no cover - def __init__(self, token: str, host: str = "localhost", port: int = 844): - super().__init__(token) + def __init__(self, token: str, download_all_attachments: bool = False, host: str = "localhost", port: int = 844): + super().__init__(token, download_all_attachments) self.listen = host self.port = port diff --git a/tutorials/messengers/telegram/5_conditions_with_media.py b/tutorials/messengers/telegram/5_conditions_with_media.py index b39dbd84a..08357dddc 100644 --- a/tutorials/messengers/telegram/5_conditions_with_media.py +++ b/tutorials/messengers/telegram/5_conditions_with_media.py @@ -43,7 +43,7 @@ # %% -interface = PollingTelegramInterface(token=os.environ["TG_BOT_TOKEN"]) +interface = PollingTelegramInterface(token=os.environ["TG_BOT_TOKEN"], download_all_attachments=True) # %% From 26dc2d63d4ff7a31a80a37f6172bc53618d2acca Mon Sep 17 00:00:00 2001 From: pseusys Date: Sun, 4 Feb 2024 20:42:19 +0100 Subject: [PATCH 007/140] download all removed, special downloading method created --- dff/messengers/common/interface.py | 5 ++ dff/messengers/telegram.py | 49 ++++++++----------- dff/script/core/message.py | 15 +++--- .../telegram/5_conditions_with_media.py | 2 +- 4 files changed, 36 insertions(+), 35 deletions(-) diff --git a/dff/messengers/common/interface.py b/dff/messengers/common/interface.py index 13747b06c..67d11cb1c 100644 --- a/dff/messengers/common/interface.py +++ b/dff/messengers/common/interface.py @@ -13,6 +13,7 @@ from dff.script import Context, Message from dff.messengers.common.types import PollingInterfaceLoopFunction +from dff.script.core.message import DataAttachment if TYPE_CHECKING: from dff.pipeline.types import PipelineRunnerFunction @@ -37,6 +38,10 @@ async def connect(self, pipeline_runner: PipelineRunnerFunction): """ raise NotImplementedError + @abc.abstractmethod + async def populate_attachment(self, attachment: DataAttachment) -> None: + raise NotImplementedError + class PollingMessengerInterface(MessengerInterface): """ diff --git a/dff/messengers/telegram.py b/dff/messengers/telegram.py index f8d48e68c..5970eeef1 100644 --- a/dff/messengers/telegram.py +++ b/dff/messengers/telegram.py @@ -22,26 +22,19 @@ class _AbstractTelegramInterface(MessengerInterface): # pragma: no cover - def __init__(self, token: str, download_all_attachments: bool) -> None: + def __init__(self, token: str) -> None: self.application = Application.builder().token(token).build() self.application.add_handler(MessageHandler(ALL, self.on_message)) self.application.add_handler(CallbackQueryHandler(self.on_callback)) - self.download_all = download_all_attachments - async def download_telegram_file(self, file: DataAttachment) -> Optional[FilePath]: # pragma: no cover - if file.title is not None and file.id is not None: - file_name = Path(gettempdir()) / str(file.title) + async def populate_attachment(self, attachment: DataAttachment) -> None: # pragma: no cover + if attachment.title is not None and attachment.id is not None: + file_name = Path(gettempdir()) / str(attachment.title) if not file_name.exists(): - await (await self.application.bot.get_file(file.id)).download_to_drive(file_name) - return FilePath(file_name) + await (await self.application.bot.get_file(attachment.id)).download_to_drive(file_name) + attachment.source = FilePath(file_name) else: - return None - - async def _process_attachment(self, attachment: _BaseMedium, download: bool, cls: Type[DataAttachment]) -> DataAttachment: # pragma: no cover - data_attachment = cls(id=attachment.file_id, title=attachment.file_unique_id) - if download: - data_attachment.source = await self.download_telegram_file(data_attachment) - return data_attachment + raise ValueError(f"For attachment {attachment} title or id is not defined!") async def extract_message_from_telegram(self, update: TelegramMessage) -> Message: # pragma: no cover message = Message() @@ -58,15 +51,15 @@ async def extract_message_from_telegram(self, update: TelegramMessage) -> Messag if update.poll is not None: message.attachments += [Poll(question=update.poll.question, options=[PollOption(text=option.text, votes=option.voter_count) for option in update.poll.options])] if update.audio is not None: - message.attachments += [await self._process_attachment(update.audio, self.download_all, Audio)] + message.attachments += [Audio(id=update.audio.file_id, title=update.audio.file_unique_id)] if update.video is not None: - message.attachments += [await self._process_attachment(update.video, self.download_all, Video)] + message.attachments += [Video(id=update.video.file_id, title=update.video.file_unique_id)] if update.animation is not None: - message.attachments += [await self._process_attachment(update.animation, self.download_all, Animation)] + message.attachments += [Animation(id=update.animation.file_id, title=update.animation.file_unique_id)] if len(update.photo) > 0: - message.attachments += [await self._process_attachment(picture, self.download_all, Image) for picture in update.photo] + message.attachments += [Image(id=picture.file_id, title=picture.file_unique_id) for picture in update.photo] if update.document is not None: - message.attachments += [await self._process_attachment(update.document, self.download_all, Document)] + message.attachments += [Document(id=update.document.file_id, title=update.document.file_unique_id)] return message @@ -94,23 +87,23 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes await bot.send_poll(chat_id, attachment.question, [option.text for option in attachment.options], reply_markup=self._create_keyboard(buttons)) return if isinstance(attachment, Audio): - attachment_bytes = attachment.get_bytes() + attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: files += [InputMediaAudio(attachment_bytes)] if isinstance(attachment, Video): - attachment_bytes = attachment.get_bytes() + attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: files += [InputMediaVideo(attachment_bytes)] if isinstance(attachment, Animation): - attachment_bytes = attachment.get_bytes() + attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: files += [InputMediaAnimation(attachment_bytes)] if isinstance(attachment, Image): - attachment_bytes = attachment.get_bytes() + attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: files += [InputMediaPhoto(attachment_bytes)] if isinstance(attachment, Document): - attachment_bytes = attachment.get_bytes() + attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: files += [InputMediaDocument(attachment_bytes)] if isinstance(attachment, Keyboard): @@ -142,8 +135,8 @@ async def connect(self, callback: PipelineRunnerFunction, *args, **kwargs): class PollingTelegramInterface(_AbstractTelegramInterface): # pragma: no cover - def __init__(self, token: str, download_all_attachments: bool = False, interval: int = 2, timeout: int = 20) -> None: - super().__init__(token, download_all_attachments) + def __init__(self, token: str, interval: int = 2, timeout: int = 20) -> None: + super().__init__(token) self.interval = interval self.timeout = timeout @@ -153,8 +146,8 @@ async def connect(self, callback: PipelineRunnerFunction, *args, **kwargs): class CallbackTelegramInterface(_AbstractTelegramInterface): # pragma: no cover - def __init__(self, token: str, download_all_attachments: bool = False, host: str = "localhost", port: int = 844): - super().__init__(token, download_all_attachments) + def __init__(self, token: str, host: str = "localhost", port: int = 844): + super().__init__(token) self.listen = host self.port = port diff --git a/dff/script/core/message.py b/dff/script/core/message.py index 640e50eb9..2181c3cb7 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -11,6 +11,8 @@ from pydantic import field_validator, Field, FilePath, HttpUrl, BaseModel, model_validator +from dff.messengers.common.interface import MessengerInterface + class Session(Enum): """ @@ -98,23 +100,24 @@ class DataAttachment(Attachment): id: Optional[str] = None # id field is made separate to simplify type validation title: Optional[str] = None - def get_bytes(self) -> Optional[bytes]: + async def get_bytes(self, interface: MessengerInterface) -> Optional[bytes]: if self.source is None: - return None + await interface.populate_attachment(self) if isinstance(self.source, Path): with open(self.source, "rb") as file: return file.read() - else: + elif isinstance(self.source, HttpUrl): with urlopen(self.source.unicode_string()) as file: return file.read() + else: + return None def __eq__(self, other): if isinstance(other, DataAttachment): - if self.title != other.title: - return False if self.id != other.id: return False - return self.get_bytes() == other.get_bytes() + if self.title != other.title: + return False return NotImplemented @model_validator(mode="before") diff --git a/tutorials/messengers/telegram/5_conditions_with_media.py b/tutorials/messengers/telegram/5_conditions_with_media.py index 08357dddc..b39dbd84a 100644 --- a/tutorials/messengers/telegram/5_conditions_with_media.py +++ b/tutorials/messengers/telegram/5_conditions_with_media.py @@ -43,7 +43,7 @@ # %% -interface = PollingTelegramInterface(token=os.environ["TG_BOT_TOKEN"], download_all_attachments=True) +interface = PollingTelegramInterface(token=os.environ["TG_BOT_TOKEN"]) # %% From 7e65068070e20bf0203e8f02c785ca2de1467135 Mon Sep 17 00:00:00 2001 From: pseusys Date: Wed, 7 Feb 2024 21:41:13 +0100 Subject: [PATCH 008/140] callback query added as an attachment --- dff/messengers/telegram.py | 33 ++++++++++++------- dff/script/core/message.py | 5 +++ tutorials/messengers/telegram/2_buttons.py | 6 ++-- .../telegram/3_buttons_with_callback.py | 12 +++---- 4 files changed, 36 insertions(+), 20 deletions(-) diff --git a/dff/messengers/telegram.py b/dff/messengers/telegram.py index 5970eeef1..e09163cf2 100644 --- a/dff/messengers/telegram.py +++ b/dff/messengers/telegram.py @@ -6,19 +6,18 @@ """ from pathlib import Path from tempfile import gettempdir -from typing import Callable, Optional, Sequence, Type, cast +from typing import Callable, Optional, Sequence, cast from pydantic import FilePath from telegram import InlineKeyboardButton, InlineKeyboardMarkup, InputMediaAnimation, InputMediaAudio, InputMediaDocument, InputMediaPhoto, InputMediaVideo, Update, Message as TelegramMessage from telegram.ext import Application, ExtBot, MessageHandler, CallbackQueryHandler, ContextTypes from telegram.ext.filters import ALL -from telegram._files._basemedium import _BaseMedium from dff.messengers.common import MessengerInterface from dff.pipeline import Pipeline from dff.pipeline.types import PipelineRunnerFunction from dff.script.core.context import Context -from dff.script.core.message import Animation, Audio, Button, Contact, DataAttachment, Document, Image, Invoice, Keyboard, Location, Message, Poll, PollOption, Video +from dff.script.core.message import Animation, Audio, Button, CallbackQuery, Contact, DataAttachment, Document, Image, Invoice, Keyboard, Location, Message, Poll, PollOption, Video class _AbstractTelegramInterface(MessengerInterface): # pragma: no cover @@ -114,21 +113,19 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes if message.text is not None: await bot.send_message(chat_id, message.text, reply_markup=self._create_keyboard(buttons)) - async def on_message(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> None: + async def _on_event(self, update: Update, _: ContextTypes.DEFAULT_TYPE, create_message: Callable[[Update], Message]) -> None: if update.effective_chat is not None and update.message is not None: - message = await self.extract_message_from_telegram(update.message) + message = create_message(update) message.original_message = update resp = await self.callback(message, update.effective_chat.id) if resp.last_response is not None: await self.cast_message_to_telegram_and_send(self.application.bot, update.effective_chat.id, resp.last_response) + async def on_message(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> None: + await self._on_event(update, _, lambda u: await self.extract_message_from_telegram(u.message)) + async def on_callback(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> None: - if update.effective_chat is not None and update.callback_query is not None: - message = Message(text=update.callback_query.data) - message.original_message = update - resp = await self.callback(message, update.effective_chat.id) - if resp.last_response is not None: - await self.cast_message_to_telegram_and_send(self.application.bot, update.effective_chat.id, resp.last_response) + await self._on_event(update, _, lambda u: Message(attachments=[CallbackQuery(query_string=u.callback_query.data)])) async def connect(self, callback: PipelineRunnerFunction, *args, **kwargs): self.callback = callback @@ -166,3 +163,17 @@ def condition(ctx: Context, _: Pipeline, *__, **___): # pragma: no cover return func(original_message) return condition + + +def query_callback_condition_exact_match(expected: CallbackQuery): + + def condition(ctx: Context, _: Pipeline, *__, **___) -> bool: # pragma: no cover + last_request = ctx.last_request + if last_request is None or last_request.attachments is None or len(last_request.attachments) == 0: + return False + callback_query = next((attachment for attachment in last_request.attachments if isinstance(attachment, CallbackQuery)), None) + if callback_query is None: + return False + return callback_query == expected + + return condition diff --git a/dff/script/core/message.py b/dff/script/core/message.py index 2181c3cb7..780b1daf5 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -45,6 +45,11 @@ class Attachment(DataModel): pass +class CallbackQuery(Attachment): + + query_string: Optional[str] + + class Location(Attachment): """ This class is a data model that represents a geographical diff --git a/tutorials/messengers/telegram/2_buttons.py b/tutorials/messengers/telegram/2_buttons.py index a92851bc0..7951bbe11 100644 --- a/tutorials/messengers/telegram/2_buttons.py +++ b/tutorials/messengers/telegram/2_buttons.py @@ -23,9 +23,9 @@ class is used to represent telegram message, import dff.script.conditions as cnd from dff.script import TRANSITIONS, RESPONSE -from dff.script.core.message import Button, Keyboard, Message +from dff.script.core.message import Button, Keyboard, Message, CallbackQuery from dff.pipeline import Pipeline -from dff.messengers.telegram import PollingTelegramInterface +from dff.messengers.telegram import PollingTelegramInterface, query_callback_condition_exact_match from dff.utils.testing.common import is_interactive_mode @@ -79,7 +79,7 @@ class is used to represent telegram message, ] ), TRANSITIONS: { - ("general", "success"): cnd.exact_match(Message(text="4")), + ("general", "success"): query_callback_condition_exact_match(CallbackQuery(query_string="4")), ("general", "fail"): cnd.true(), }, }, diff --git a/tutorials/messengers/telegram/3_buttons_with_callback.py b/tutorials/messengers/telegram/3_buttons_with_callback.py index 0fdae39b3..a05f02581 100644 --- a/tutorials/messengers/telegram/3_buttons_with_callback.py +++ b/tutorials/messengers/telegram/3_buttons_with_callback.py @@ -25,8 +25,8 @@ class is used to represent telegram message, import dff.script.conditions as cnd from dff.script import TRANSITIONS, RESPONSE from dff.pipeline import Pipeline -from dff.script.core.message import Button, Keyboard, Message -from dff.messengers.telegram import PollingTelegramInterface +from dff.script.core.message import Button, Keyboard, Message, CallbackQuery +from dff.messengers.telegram import PollingTelegramInterface, query_callback_condition_exact_match from dff.utils.testing.common import is_interactive_mode @@ -81,11 +81,11 @@ class is used to represent telegram message, ], ), TRANSITIONS: { - ("general", "success"): cnd.exact_match( - Message(text="correct") + ("general", "success"): query_callback_condition_exact_match( + CallbackQuery(query_string="correct") ), - ("general", "fail"): cnd.exact_match( - Message(text="wrong") + ("general", "fail"): query_callback_condition_exact_match( + CallbackQuery(query_string="wrong") ), }, }, From efece761dd92de6b37b3b8d6ec819cef7bf2d3be Mon Sep 17 00:00:00 2001 From: pseusys Date: Wed, 7 Feb 2024 21:56:23 +0100 Subject: [PATCH 009/140] callback query conditions added --- dff/messengers/telegram.py | 4 ++-- tutorials/messengers/telegram/2_buttons.py | 4 ++-- .../messengers/telegram/3_buttons_with_callback.py | 10 +++------- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/dff/messengers/telegram.py b/dff/messengers/telegram.py index e09163cf2..60e68339d 100644 --- a/dff/messengers/telegram.py +++ b/dff/messengers/telegram.py @@ -165,7 +165,7 @@ def condition(ctx: Context, _: Pipeline, *__, **___): # pragma: no cover return condition -def query_callback_condition_exact_match(expected: CallbackQuery): +def has_callback_query(expected: str): def condition(ctx: Context, _: Pipeline, *__, **___) -> bool: # pragma: no cover last_request = ctx.last_request @@ -174,6 +174,6 @@ def condition(ctx: Context, _: Pipeline, *__, **___) -> bool: # pragma: no cove callback_query = next((attachment for attachment in last_request.attachments if isinstance(attachment, CallbackQuery)), None) if callback_query is None: return False - return callback_query == expected + return callback_query.query_string == expected return condition diff --git a/tutorials/messengers/telegram/2_buttons.py b/tutorials/messengers/telegram/2_buttons.py index 7951bbe11..431877ba4 100644 --- a/tutorials/messengers/telegram/2_buttons.py +++ b/tutorials/messengers/telegram/2_buttons.py @@ -25,7 +25,7 @@ class is used to represent telegram message, from dff.script import TRANSITIONS, RESPONSE from dff.script.core.message import Button, Keyboard, Message, CallbackQuery from dff.pipeline import Pipeline -from dff.messengers.telegram import PollingTelegramInterface, query_callback_condition_exact_match +from dff.messengers.telegram import PollingTelegramInterface, has_callback_query from dff.utils.testing.common import is_interactive_mode @@ -79,7 +79,7 @@ class is used to represent telegram message, ] ), TRANSITIONS: { - ("general", "success"): query_callback_condition_exact_match(CallbackQuery(query_string="4")), + ("general", "success"): has_callback_query("4"), ("general", "fail"): cnd.true(), }, }, diff --git a/tutorials/messengers/telegram/3_buttons_with_callback.py b/tutorials/messengers/telegram/3_buttons_with_callback.py index a05f02581..0abe31d15 100644 --- a/tutorials/messengers/telegram/3_buttons_with_callback.py +++ b/tutorials/messengers/telegram/3_buttons_with_callback.py @@ -26,7 +26,7 @@ class is used to represent telegram message, from dff.script import TRANSITIONS, RESPONSE from dff.pipeline import Pipeline from dff.script.core.message import Button, Keyboard, Message, CallbackQuery -from dff.messengers.telegram import PollingTelegramInterface, query_callback_condition_exact_match +from dff.messengers.telegram import PollingTelegramInterface, has_callback_query from dff.utils.testing.common import is_interactive_mode @@ -81,12 +81,8 @@ class is used to represent telegram message, ], ), TRANSITIONS: { - ("general", "success"): query_callback_condition_exact_match( - CallbackQuery(query_string="correct") - ), - ("general", "fail"): query_callback_condition_exact_match( - CallbackQuery(query_string="wrong") - ), + ("general", "success"): has_callback_query("correct"), + ("general", "fail"): has_callback_query("wrong"), }, }, "success": { From c947b4ea1779deaca114959a380989bbfb376259 Mon Sep 17 00:00:00 2001 From: Roman Zlobin Date: Thu, 22 Feb 2024 15:20:19 +0300 Subject: [PATCH 010/140] remove tg tests; move message tests to core tests TG tutorials are tested (without tg API) during tutorial tests. --- dff/utils/testing/telegram.py | 271 -------------------- tests/messengers/__init__.py | 0 tests/messengers/telegram/__init__.py | 0 tests/messengers/telegram/conftest.py | 78 ------ tests/messengers/telegram/test_tutorials.py | 58 ----- tests/messengers/telegram/test_types.py | 204 --------------- tests/script/core/test_message.py | 45 +++- 7 files changed, 34 insertions(+), 622 deletions(-) delete mode 100644 dff/utils/testing/telegram.py delete mode 100644 tests/messengers/__init__.py delete mode 100644 tests/messengers/telegram/__init__.py delete mode 100644 tests/messengers/telegram/conftest.py delete mode 100644 tests/messengers/telegram/test_tutorials.py delete mode 100644 tests/messengers/telegram/test_types.py diff --git a/dff/utils/testing/telegram.py b/dff/utils/testing/telegram.py deleted file mode 100644 index 85c6b6a7e..000000000 --- a/dff/utils/testing/telegram.py +++ /dev/null @@ -1,271 +0,0 @@ -""" -Telegram testing utils ----------------------- -This module defines functions used to test Telegram interface. -""" -from typing import List, Optional, cast, Tuple -from contextlib import asynccontextmanager, nullcontext -import logging -import asyncio -from tempfile import TemporaryDirectory -from pathlib import Path -from copy import deepcopy - -from dff.pipeline.pipeline.pipeline import Pipeline -from dff.script.core.message import Message, Attachment, Location -from dff.messengers.telegram import PollingTelegramInterface - - -def replace_click_button(happy_path): - """ - Replace all _ClickButton instances in `happy_path`. - This allows using :py:func:`~dff.utils.testing.common.check_happy_path` instead of - :py:meth:~dff.utils.testing.telegram.TelegramTesting.check_happy_path`. - - :return: A `happy_path` with all `_ClickButton` replaced with payload values of the buttons. - """ - result = deepcopy(happy_path) - for index in range(len(happy_path)): - user_request = happy_path[index][0] - if not isinstance(user_request, Message): - continue - if isinstance(user_request.callback_query, _ClickButton): - callback_query = None - for _, bot_response in reversed(happy_path[:index]): - if isinstance(bot_response, Message) and bot_response.ui is not None and callback_query is None: - callback_query = bot_response.ui.buttons[user_request.callback_query.button_index].payload - if callback_query is None: - raise RuntimeError("Bot response with buttons not found.") - result[index][0].callback_query = callback_query - return result - - -async def get_bot_user(client: TelegramClient, username: str): - async with client: - return await client.get_entity(username) - - -class TelegramTesting: # pragma: no cover - """ - Defines functions for testing. - - :param pipeline: - Pipeline with the telegram messenger interface. - Required for :py:meth:`~dff.utils.testing.telegram.TelegramTesting.send_and_check` and - :py:meth:`~dff.utils.testing.telegram.TelegramTesting.check_happy_path` with `run_bot=True` - :param api_credentials: - Telegram API id and hash. - Obtainable via https://core.telegram.org/api/obtaining_api_id. - :param session_file: - A `telethon` session file. - Obtainable by connecting to :py:class:`telethon.TelegramClient` and entering phone number and code. - :param client: - An alternative to passing `api_credentials` and `session_file`. - :param bot_username: - Either a link to the bot user or its handle. Used to determine whom to talk with as a client. - :param bot: - An alternative to passing `bot_username`. - Result of calling :py:func:`~dff.utils.testing.telegram.get_bot_user` with `bot_username` as parameter. - """ - - def __init__( - self, - pipeline: Pipeline, - api_credentials: Optional[Tuple[int, str]] = None, - session_file: Optional[str] = None, - client: Optional[TelegramClient] = None, - bot_username: Optional[str] = None, - bot: Optional[User] = None, - ): - if client is None: - if api_credentials is None or session_file is None: - raise RuntimeError("Pass either `client` or `api_credentials` and `session_file`.") - client = TelegramClient(session_file, *api_credentials) - self.client = client - """Telegram client (not bot). Needed to verify bot replies.""" - self.pipeline = pipeline - if bot is None: - if bot_username is None: - raise RuntimeError("Pass either `bot_username` or `bot`.") - bot = asyncio.run(get_bot_user(self.client, bot_username)) - self.bot = bot - """Bot user (to know whom to send messages to from client).""" - - async def send_message(self, message: Message, last_bot_messages: List[TlMessage]): - """ - Send a message from client to bot. - If the message contains `callback_query`, only press the button, ignore other fields. - - :param message: Message to send. - :param last_bot_messages: - The last bot response. Accepts a list because messages with multiple fields are split in telegram. - Can only contain one keyboard in the list. - Used to determine which button to press when message contains - :py:class:`~dff.messengers.telegram.message._ClickButton`. - """ - if message.callback_query is not None: - query = message.callback_query - if not isinstance(query, _ClickButton): - raise RuntimeError(f"Use `_ClickButton` during tests: {query}") - for bot_message in last_bot_messages: - if bot_message.buttons is not None: - await bot_message.click(i=query.button_index) - return None - if message.attachments is None or len(message.attachments.files) == 0: - return await self.client.send_message(self.bot, message.text) - else: - if len(message.attachments.files) == 1: - attachment = message.attachments.files[0] - files = attachment.source - else: - files = [file.source for file in message.attachments.files] - return await self.client.send_file(self.bot, files, caption=message.text) - - @staticmethod - async def parse_responses(responses: List[TlMessage], file_download_destination) -> Message: - """ - Convert a list of bot responses into a single message. - This function accepts a list because messages with multiple attachments are split. - - :param responses: A list of bot responses that are considered to be a single message. - :param file_download_destination: A directory to download sent media to. - """ - msg = TelegramMessage() - for response in responses: - if response.text and response.file is None: - if msg.text: - raise RuntimeError(f"Several messages with text:\n{msg.text}\n{response.text}") - msg.text = response.text or msg.text - if response.file is not None: - file = Path(file_download_destination) / (str(response.file.media.id) + response.file.ext) - await response.download_media(file=file) - if msg.attachments is None: - msg.attachments = Attachments() - msg.attachments.files.append( - Attachment(source=file, id=None, title=response.file.title or response.text or None) - ) - if response.buttons is not None: - buttons = [] - for row in response.buttons: - for button in row: - buttons.append( - Button( - source=button.url, - text=button.text, - payload=button.data, - ) - ) - if msg.ui is not None: - raise RuntimeError(f"Several messages with ui:\n{msg.ui}\n{TelegramUI(buttons=buttons)}") - msg.ui = TelegramUI(buttons=buttons) - if isinstance(response.reply_markup, ReplyKeyboardHide): - if msg.ui is not None: - raise RuntimeError(f"Several messages with ui:\n{msg.ui}\n{types.ReplyKeyboardRemove()}") - msg.ui = RemoveKeyboard() - if response.geo is not None: - location = Location(latitude=response.geo.lat, longitude=response.geo.long) - if msg.location is not None: - raise RuntimeError(f"Several messages with location:\n{msg.location}\n{location}") - msg.location = location - return msg - - @asynccontextmanager - async def run_bot_loop(self): - """A context manager that returns a function to run one polling loop of a messenger interface.""" - self.pipeline.messenger_interface.timeout = 2 - self.pipeline.messenger_interface.long_polling_timeout = 2 - await self.forget_previous_updates() - - yield lambda: self.pipeline.messenger_interface._polling_loop(self.pipeline._run_pipeline) - - self.pipeline.messenger_interface.forget_processed_updates() - - async def send_and_check(self, message: Message, file_download_destination=None): - """ - Send a message from a bot, receive it as client, verify it. - - :param message: Message to send and check. - :param file_download_destination: - Temporary directory (used to download sent files). - Defaults to :py:class:`tempfile.TemporaryDirectory`. - """ - await self.forget_previous_updates() - - async with self.client: - messenger_interface = cast(PollingTelegramInterface, self.pipeline.messenger_interface) - - messages = await self.client.get_messages(self.bot, limit=1) - if len(messages) == 0: - last_message_id = 0 - else: - last_message_id = messages[0].id - - messenger_interface.messenger.send_response((await self.client.get_me(input_peer=True)).user_id, message) - - await asyncio.sleep(3) - bot_messages = [ - x async for x in self.client.iter_messages(self.bot, min_id=last_message_id, from_user=self.bot) - ] # iter_messages is used instead of get_messages because get_messages requires bot min_id and max_id - - if file_download_destination is None: - fd_context = TemporaryDirectory() - else: - fd_context = nullcontext(file_download_destination) - - with fd_context as file_download_destination: - result = await self.parse_responses(bot_messages, file_download_destination) - - assert result == message - - async def forget_previous_updates(self): - messenger_interface = cast(PollingTelegramInterface, self.pipeline.messenger_interface) - messenger = messenger_interface.messenger - updates = messenger.get_updates(offset=messenger.last_update_id + 1, timeout=1, long_polling_timeout=1) - max_update_id = max([*map(lambda x: x.update_id, updates), -1]) - messenger.get_updates(offset=max_update_id + 1, timeout=1, long_polling_timeout=1) - - async def check_happy_path(self, happy_path, file_download_destination=None, run_bot: bool = True): - """ - Play out a dialogue with the bot. Check that the dialogue is correct. - - :param happy_path: Expected dialogue - :param file_download_destination: Temporary directory (used to download sent files) - :param run_bot: Whether a bot inside pipeline should be running (disable this to test non-async bots) - :return: - """ - if run_bot: - bot = self.run_bot_loop() - else: - - async def null(): - ... - - bot = nullcontext(null) - - if file_download_destination is None: - fd_context = TemporaryDirectory() - else: - fd_context = nullcontext(file_download_destination) - - async with self.client, bot as boot_loop: - with fd_context as file_download_destination: - bot_messages = [] - last_message = None - for request, response in happy_path: - logging.info(f"Sending request {request}") - user_message = await self.send_message(TelegramMessage.model_validate(request), bot_messages) - if user_message is not None: - last_message = user_message - logging.info("Request sent") - await boot_loop() - await asyncio.sleep(2) - logging.info("Extracting responses") - bot_messages = [ - x async for x in self.client.iter_messages(self.bot, min_id=last_message.id, from_user=self.bot) - ] - # iter_messages is used instead of get_messages because get_messages requires bot min_id and max_id - if len(bot_messages) > 0: - last_message = bot_messages[0] - logging.info("Got responses") - result = await self.parse_responses(bot_messages, file_download_destination) - assert result == TelegramMessage.model_validate(response) diff --git a/tests/messengers/__init__.py b/tests/messengers/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/messengers/telegram/__init__.py b/tests/messengers/telegram/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/messengers/telegram/conftest.py b/tests/messengers/telegram/conftest.py deleted file mode 100644 index d3436410a..000000000 --- a/tests/messengers/telegram/conftest.py +++ /dev/null @@ -1,78 +0,0 @@ -import os -import asyncio -import importlib -from pathlib import Path - -import pytest - -from tests.test_utils import get_path_from_tests_to_current_dir - -try: - from dff.utils.testing.telegram import get_bot_user, TelegramClient - - telegram_available = True -except ImportError: - telegram_available = False - -dot_path_to_addon = get_path_from_tests_to_current_dir(__file__, separator=".") - - -@pytest.fixture(scope="session") -def pipeline_tutorial(): - if not telegram_available: - pytest.skip("`telegram` not available.") - yield importlib.import_module(f"tutorials.{dot_path_to_addon}.{'7_polling_setup'}") - - -@pytest.fixture(scope="session") -def session_file(): - dff_root_dir = Path(__file__).parent.parent.parent.parent - file = dff_root_dir / "anon.session" - - if not file.exists(): - pytest.skip(f"Session file does not exist at {str(file)}") - - return str(file) - - -@pytest.fixture(scope="session") -def env_vars(): - env_variables = {"TG_BOT_TOKEN": None, "TG_API_ID": None, "TG_API_HASH": None, "TG_BOT_USERNAME": None} - - for arg in env_variables: - env_variables[arg] = os.getenv(arg) - - if env_variables[arg] is None: - pytest.skip(f"`{arg}` is not set", allow_module_level=True) - - yield env_variables - - -@pytest.fixture(scope="session") -def pipeline_instance(env_vars, pipeline_tutorial): - yield pipeline_tutorial.pipeline - - -@pytest.fixture(scope="session") -def document(tmpdir_factory): - filename: Path = tmpdir_factory.mktemp("data").join("file.txt") - with filename.open("w") as f: - f.write("test") - yield filename - - -@pytest.fixture(scope="session") -def api_credentials(env_vars): - yield (int(env_vars["TG_API_ID"]), env_vars["TG_API_HASH"]) - - -@pytest.fixture(scope="session") -def bot_user(api_credentials, env_vars, session_file): - if not telegram_available: - pytest.skip("`telegram` not available.") - client = TelegramClient(session_file, *api_credentials) - yield asyncio.run(get_bot_user(client, env_vars["TG_BOT_USERNAME"])) - - -def pytest_sessionfinish(session, exitstatus): - asyncio.get_event_loop().close() diff --git a/tests/messengers/telegram/test_tutorials.py b/tests/messengers/telegram/test_tutorials.py deleted file mode 100644 index c92e0e048..000000000 --- a/tests/messengers/telegram/test_tutorials.py +++ /dev/null @@ -1,58 +0,0 @@ -""" -These tests check that pipelines defined in tutorials follow `happy_path` defined in the same tutorials. -""" -import importlib -import logging - -import pytest - -try: - import telebot # noqa: F401 - import telethon # noqa: F401 -except ImportError: - pytest.skip(reason="`telegram` is not available", allow_module_level=True) - -from tests.test_utils import get_path_from_tests_to_current_dir -from dff.utils.testing.common import check_happy_path -from dff.utils.testing.telegram import TelegramTesting, replace_click_button - -dot_path_to_addon = get_path_from_tests_to_current_dir(__file__, separator=".") - - -@pytest.mark.parametrize( - "tutorial_module_name", - [ - "1_basic", - "2_buttons", - "3_buttons_with_callback", - ], -) -def test_client_tutorials_without_telegram(tutorial_module_name, env_vars): - tutorial_module = importlib.import_module(f"tutorials.{dot_path_to_addon}.{tutorial_module_name}") - pipeline = tutorial_module.pipeline - happy_path = tutorial_module.happy_path - check_happy_path(pipeline, replace_click_button(happy_path)) - - -@pytest.mark.asyncio -@pytest.mark.parametrize( - "tutorial_module_name", - [ - "1_basic", - "2_buttons", - "3_buttons_with_callback", - "4_conditions", - "5_conditions_with_media", - "7_polling_setup", - ], -) -@pytest.mark.telegram -async def test_client_tutorials(tutorial_module_name, api_credentials, bot_user, session_file): - tutorial_module = importlib.import_module(f"tutorials.{dot_path_to_addon}.{tutorial_module_name}") - pipeline = tutorial_module.pipeline - happy_path = tutorial_module.happy_path - test_helper = TelegramTesting( - pipeline=pipeline, api_credentials=api_credentials, session_file=session_file, bot=bot_user - ) - logging.info("Test start") - await test_helper.check_happy_path(happy_path) diff --git a/tests/messengers/telegram/test_types.py b/tests/messengers/telegram/test_types.py deleted file mode 100644 index 01f1b0063..000000000 --- a/tests/messengers/telegram/test_types.py +++ /dev/null @@ -1,204 +0,0 @@ -import json -from io import IOBase -from pathlib import Path - -import pytest - -try: - import telegram # noqa: F401 -except ImportError: - pytest.skip(reason="`telegram` is not available", allow_module_level=True) - -from pydantic import FilePath, HttpUrl, ValidationError - -from dff.script import Message -from dff.script.core.message import Button, DataAttachment, Keyboard, Image, Location, Audio, Video, Attachment, Document - -from dff.utils.testing.telegram import TelegramTesting - -_image_link = "https://gist.githubusercontent.com/scotthaleen/32f76a413e0dfd4b4d79c2a534d49c0b/raw/6c1036b1eca90b341caf06d4056d36f64fc11e88/tiny.jpg" -image = Image(source=HttpUrl(_image_link)) -audio = Audio(source=HttpUrl("https://github.com/mathiasbynens/small/raw/master/mp3.mp3")) -video = Video(source=HttpUrl("https://github.com/mathiasbynens/small/raw/master/Mpeg4.mp4")) -document = Document(source=HttpUrl("https://github.com/mathiasbynens/small/raw/master/pdf.pdf")) - - -@pytest.mark.asyncio -@pytest.mark.telegram -async def test_text(pipeline_instance, api_credentials, bot_user, session_file): - telegram_response = Message(text="test") - test_helper = TelegramTesting( - pipeline=pipeline_instance, api_credentials=api_credentials, session_file=session_file, bot=bot_user - ) - await test_helper.send_and_check(telegram_response) - - -@pytest.mark.asyncio -@pytest.mark.parametrize( - ["keyboard"], - [ - Keyboard( - buttons=[ - [ - Button(text="button 1", data=json.dumps({"text": "1", "other_prop": "4"})), - Button(text="button 2", data=json.dumps({"text": "2", "other_prop": "3"})), - ], - [ - Button(text="button 3", data=json.dumps({"text": "3", "other_prop": "2"})), - Button(text="button 4", data=json.dumps({"text": "4", "other_prop": "1"})), - ] - ] - ), - Keyboard( - buttons=[ - [ - Button(text="button 1"), - Button(text="button 2"), - ], - [ - Button(text="button 3"), - Button(text="button 4"), - ] - ], - ), - ], -) -@pytest.mark.telegram -async def test_buttons(keyboard: Keyboard, pipeline_instance, api_credentials, bot_user, session_file): - telegram_response = Message(text="test", attachments=[keyboard]) - test_helper = TelegramTesting( - pipeline=pipeline_instance, api_credentials=api_credentials, session_file=session_file, bot=bot_user - ) - await test_helper.send_and_check(telegram_response) - - -@pytest.mark.asyncio -@pytest.mark.parametrize( - ["generic_response"], - [ - ( - Message( - text="test", - attachments=[image], - ), - ), - ( - Message( - text="test", - attachments=[audio], - ), - ), - ( - Message( - text="test", - attachments=[video], - ), - ), - ( - Message( - text="test", - attachments=[document], - ), - ), - ], -) -@pytest.mark.telegram -async def test_telegram_attachment(generic_response, pipeline_instance, api_credentials, bot_user, session_file): - telegram_response = Message.model_validate(generic_response) - test_helper = TelegramTesting( - pipeline=pipeline_instance, api_credentials=api_credentials, session_file=session_file, bot=bot_user - ) - await test_helper.send_and_check(telegram_response) - - -@pytest.mark.asyncio -@pytest.mark.parametrize( - ["attachments"], - [ - ( - Message( - text="test", - attachments=list(tuple(2 * [image])), - ), - ), - ( - Message( - text="test", - attachments=list(tuple(2 * [audio])), - ), - ), - ( - Message( - text="test", - attachments=list(tuple(2 * [video])), - ), - ), - ( - Message( - text="test", - attachments=list(tuple(2 * [document])), - ), - ), - ], -) -@pytest.mark.telegram -async def test_attachments(attachments, pipeline_instance, api_credentials, bot_user, session_file): - telegram_response = Message.model_validate(attachments) - test_helper = TelegramTesting( - pipeline=pipeline_instance, api_credentials=api_credentials, session_file=session_file, bot=bot_user - ) - await test_helper.send_and_check(telegram_response) - - -@pytest.mark.asyncio -@pytest.mark.telegram -async def test_location(pipeline_instance, api_credentials, bot_user, session_file): - telegram_response = Message(text="location", attachments=[Location(longitude=39.0, latitude=43.0)]) - test_helper = TelegramTesting( - pipeline=pipeline_instance, api_credentials=api_credentials, session_file=session_file, bot=bot_user - ) - await test_helper.send_and_check(telegram_response) - - -@pytest.mark.asyncio -@pytest.mark.telegram -async def test_parsed_text(pipeline_instance, api_credentials, bot_user, session_file): - telegram_response = Message(text="[inline URL](http://www.example.com/)", parse_mode=ParseMode.MARKDOWN) - test_helper = TelegramTesting( - pipeline=pipeline_instance, api_credentials=api_credentials, session_file=session_file, bot=bot_user - ) - await test_helper.send_and_check(telegram_response) - - -def test_missing_error(): - with pytest.raises(ValidationError) as e: - _ = DataAttachment(source=HttpUrl("http://google.com"), id="123") - assert e - - with pytest.raises(ValidationError) as e: - _ = DataAttachment(source=FilePath("/etc/missing_file")) - assert e - - -def test_empty_keyboard(): - with pytest.raises(ValidationError) as e: - _ = Keyboard(buttons=[]) - assert e - - -def test_long_button_data(): - with pytest.raises(ValidationError) as error: - Button(text="", data="test" * 64) - assert error - - -def test_io(document): - tele_doc = types.InputMediaDocument(media=Path(document)) - opened_doc = open_io(tele_doc) - print(type(opened_doc.media)) - assert isinstance(opened_doc.media, IOBase) - close_io(opened_doc) - assert opened_doc.media.closed - with batch_open_io(tele_doc) as med: - assert isinstance(med.media, IOBase) - assert tele_doc.media.closed diff --git a/tests/script/core/test_message.py b/tests/script/core/test_message.py index b846c38dd..96134e6fb 100644 --- a/tests/script/core/test_message.py +++ b/tests/script/core/test_message.py @@ -1,6 +1,7 @@ import pytest +from pydantic import ValidationError, HttpUrl, FilePath -from dff.script.core.message import Location, Attachment +from dff.script.core.message import Location, DataAttachment, Keyboard, Button def test_location(): @@ -19,31 +20,53 @@ def test_location(): "attachment1,attachment2,equal", [ ( - Attachment(source="https://github.com/mathiasbynens/small/raw/master/pdf.pdf", title="File"), - Attachment(source="https://raw.githubusercontent.com/mathiasbynens/small/master/pdf.pdf", title="File"), + DataAttachment(source="https://github.com/mathiasbynens/small/raw/master/pdf.pdf", title="File"), + DataAttachment(source="https://raw.githubusercontent.com/mathiasbynens/small/master/pdf.pdf", title="File"), True, ), ( - Attachment(source="https://github.com/mathiasbynens/small/raw/master/pdf.pdf", title="1"), - Attachment(source="https://raw.githubusercontent.com/mathiasbynens/small/master/pdf.pdf", title="2"), + DataAttachment(source="https://github.com/mathiasbynens/small/raw/master/pdf.pdf", title="1"), + DataAttachment(source="https://raw.githubusercontent.com/mathiasbynens/small/master/pdf.pdf", title="2"), False, ), ( - Attachment(source=__file__, title="File"), - Attachment(source=__file__, title="File"), + DataAttachment(source=__file__, title="File"), + DataAttachment(source=__file__, title="File"), True, ), ( - Attachment(source=__file__, title="1"), - Attachment(source=__file__, title="2"), + DataAttachment(source=__file__, title="1"), + DataAttachment(source=__file__, title="2"), False, ), ( - Attachment(id="1", title="File"), - Attachment(id="2", title="File"), + DataAttachment(id="1", title="File"), + DataAttachment(id="2", title="File"), False, ), ], ) def test_attachment(attachment1, attachment2, equal): assert (attachment1 == attachment2) == equal + + +def test_missing_error(): + with pytest.raises(ValidationError) as e: + _ = DataAttachment(source=HttpUrl("http://google.com"), id="123") + assert e + + with pytest.raises(ValidationError) as e: + _ = DataAttachment(source=FilePath("/etc/missing_file")) + assert e + + +def test_empty_keyboard(): + with pytest.raises(ValidationError) as e: + _ = Keyboard(buttons=[]) + assert e + + +def test_long_button_data(): + with pytest.raises(ValidationError) as error: + Button(text="", data="test" * 64) + assert error From 25b8ac9a97b50ef4e62a514783f2a319fbef88c2 Mon Sep 17 00:00:00 2001 From: pseusys Date: Wed, 28 Feb 2024 01:03:43 +0100 Subject: [PATCH 011/140] buttons pulled out of attachments list --- dff/messengers/telegram.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dff/messengers/telegram.py b/dff/messengers/telegram.py index 60e68339d..24d0ea671 100644 --- a/dff/messengers/telegram.py +++ b/dff/messengers/telegram.py @@ -75,6 +75,9 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes buttons = list() if message.attachments is not None: files = list() + for attachment in message.attachments: + if isinstance(attachment, Keyboard): + buttons = attachment.buttons for attachment in message.attachments: if isinstance(attachment, Location): await bot.send_location(chat_id, attachment.latitude, attachment.longitude, reply_markup=self._create_keyboard(buttons)) @@ -105,8 +108,6 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: files += [InputMediaDocument(attachment_bytes)] - if isinstance(attachment, Keyboard): - buttons = attachment.buttons if len(files) > 0: await bot.send_media_group(chat_id, files, caption=message.text) return From b67b497d48943fb120432aaea73f315bbdee25a2 Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 12 Mar 2024 19:45:39 +0100 Subject: [PATCH 012/140] some errors fixed --- dff/messengers/common/conditions.py | 16 ++++++++++++ dff/messengers/telegram.py | 26 +++++-------------- tutorials/messengers/telegram/2_buttons.py | 5 ++-- .../telegram/3_buttons_with_callback.py | 5 ++-- 4 files changed, 28 insertions(+), 24 deletions(-) create mode 100644 dff/messengers/common/conditions.py diff --git a/dff/messengers/common/conditions.py b/dff/messengers/common/conditions.py new file mode 100644 index 000000000..f5534f993 --- /dev/null +++ b/dff/messengers/common/conditions.py @@ -0,0 +1,16 @@ +from dff.pipeline import Pipeline +from dff.script import Context + + +def has_callback_query(expected: str): + + def condition(ctx: Context, _: Pipeline, *__, **___) -> bool: # pragma: no cover + last_request = ctx.last_request + if last_request is None or last_request.attachments is None or len(last_request.attachments) == 0: + return False + callback_query = next((attachment for attachment in last_request.attachments if isinstance(attachment, CallbackQuery)), None) + if callback_query is None: + return False + return callback_query.query_string == expected + + return condition diff --git a/dff/messengers/telegram.py b/dff/messengers/telegram.py index 60e68339d..b8f76a318 100644 --- a/dff/messengers/telegram.py +++ b/dff/messengers/telegram.py @@ -127,8 +127,8 @@ async def on_message(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> None async def on_callback(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> None: await self._on_event(update, _, lambda u: Message(attachments=[CallbackQuery(query_string=u.callback_query.data)])) - async def connect(self, callback: PipelineRunnerFunction, *args, **kwargs): - self.callback = callback + async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs): + self.callback = pipeline_runner class PollingTelegramInterface(_AbstractTelegramInterface): # pragma: no cover @@ -137,8 +137,8 @@ def __init__(self, token: str, interval: int = 2, timeout: int = 20) -> None: self.interval = interval self.timeout = timeout - async def connect(self, callback: PipelineRunnerFunction, *args, **kwargs): - await super().connect(callback, *args, **kwargs) + async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs): + await super().connect(pipeline_runner, *args, **kwargs) self.application.run_polling(poll_interval=self.interval, timeout=self.timeout, allowed_updates=Update.ALL_TYPES) @@ -148,8 +148,8 @@ def __init__(self, token: str, host: str = "localhost", port: int = 844): self.listen = host self.port = port - async def connect(self, callback: PipelineRunnerFunction, *args, **kwargs): - await super().connect(callback, *args, **kwargs) + async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs): + await super().connect(pipeline_runner, *args, **kwargs) self.application.run_webhook(listen=self.listen, port=self.port, allowed_updates=Update.ALL_TYPES) @@ -163,17 +163,3 @@ def condition(ctx: Context, _: Pipeline, *__, **___): # pragma: no cover return func(original_message) return condition - - -def has_callback_query(expected: str): - - def condition(ctx: Context, _: Pipeline, *__, **___) -> bool: # pragma: no cover - last_request = ctx.last_request - if last_request is None or last_request.attachments is None or len(last_request.attachments) == 0: - return False - callback_query = next((attachment for attachment in last_request.attachments if isinstance(attachment, CallbackQuery)), None) - if callback_query is None: - return False - return callback_query.query_string == expected - - return condition diff --git a/tutorials/messengers/telegram/2_buttons.py b/tutorials/messengers/telegram/2_buttons.py index 431877ba4..2c1ae78ed 100644 --- a/tutorials/messengers/telegram/2_buttons.py +++ b/tutorials/messengers/telegram/2_buttons.py @@ -23,9 +23,10 @@ class is used to represent telegram message, import dff.script.conditions as cnd from dff.script import TRANSITIONS, RESPONSE -from dff.script.core.message import Button, Keyboard, Message, CallbackQuery +from dff.script.core.message import Button, Keyboard, Message from dff.pipeline import Pipeline -from dff.messengers.telegram import PollingTelegramInterface, has_callback_query +from dff.messengers.common.conditions import has_callback_query +from dff.messengers.telegram import PollingTelegramInterface from dff.utils.testing.common import is_interactive_mode diff --git a/tutorials/messengers/telegram/3_buttons_with_callback.py b/tutorials/messengers/telegram/3_buttons_with_callback.py index 0abe31d15..cd84c016f 100644 --- a/tutorials/messengers/telegram/3_buttons_with_callback.py +++ b/tutorials/messengers/telegram/3_buttons_with_callback.py @@ -25,8 +25,9 @@ class is used to represent telegram message, import dff.script.conditions as cnd from dff.script import TRANSITIONS, RESPONSE from dff.pipeline import Pipeline -from dff.script.core.message import Button, Keyboard, Message, CallbackQuery -from dff.messengers.telegram import PollingTelegramInterface, has_callback_query +from dff.script.core.message import Button, Keyboard, Message +from dff.messengers.common.conditions import has_callback_query +from dff.messengers.telegram import PollingTelegramInterface from dff.utils.testing.common import is_interactive_mode From 68b4a3725f93e467e133176edcddc75cf877f1b4 Mon Sep 17 00:00:00 2001 From: pseusys Date: Mon, 18 Mar 2024 10:28:48 +0100 Subject: [PATCH 013/140] review issues fixed --- dff/messengers/common/interface.py | 3 ++ dff/messengers/telegram.py | 28 ++++---------- dff/script/core/message.py | 5 ++- tutorials/messengers/telegram/4_conditions.py | 26 ++++--------- .../telegram/5_conditions_with_media.py | 37 +++++++------------ .../telegram/6_conditions_extras.py | 15 ++++---- 6 files changed, 43 insertions(+), 71 deletions(-) diff --git a/dff/messengers/common/interface.py b/dff/messengers/common/interface.py index 67d11cb1c..01ffedf67 100644 --- a/dff/messengers/common/interface.py +++ b/dff/messengers/common/interface.py @@ -27,6 +27,9 @@ class MessengerInterface(abc.ABC): It is responsible for connection between user and pipeline, as well as for request-response transactions. """ + accepts_attachments = tuple() + produces_attachments = tuple() + @abc.abstractmethod async def connect(self, pipeline_runner: PipelineRunnerFunction): """ diff --git a/dff/messengers/telegram.py b/dff/messengers/telegram.py index b8f76a318..edc783666 100644 --- a/dff/messengers/telegram.py +++ b/dff/messengers/telegram.py @@ -21,6 +21,9 @@ class _AbstractTelegramInterface(MessengerInterface): # pragma: no cover + accepts_attachments = (Location, Contact, Invoice, Poll, Audio, Video, Animation, Image, Document) + produces_attachments = (Location, Contact, Poll, Audio, Video, Animation, Image, Document, Keyboard) + def __init__(self, token: str) -> None: self.application = Application.builder().token(token).build() self.application.add_handler(MessageHandler(ALL, self.on_message)) @@ -50,15 +53,15 @@ async def extract_message_from_telegram(self, update: TelegramMessage) -> Messag if update.poll is not None: message.attachments += [Poll(question=update.poll.question, options=[PollOption(text=option.text, votes=option.voter_count) for option in update.poll.options])] if update.audio is not None: - message.attachments += [Audio(id=update.audio.file_id, title=update.audio.file_unique_id)] + message.attachments += [Audio(id=update.audio.file_id, title=update.audio.file_unique_id, from_messenger_interface=self)] if update.video is not None: - message.attachments += [Video(id=update.video.file_id, title=update.video.file_unique_id)] + message.attachments += [Video(id=update.video.file_id, title=update.video.file_unique_id, from_messenger_interface=self)] if update.animation is not None: - message.attachments += [Animation(id=update.animation.file_id, title=update.animation.file_unique_id)] + message.attachments += [Animation(id=update.animation.file_id, title=update.animation.file_unique_id, from_messenger_interface=self)] if len(update.photo) > 0: - message.attachments += [Image(id=picture.file_id, title=picture.file_unique_id) for picture in update.photo] + message.attachments += [Image(id=picture.file_id, title=picture.file_unique_id, from_messenger_interface=self) for picture in update.photo] if update.document is not None: - message.attachments += [Document(id=update.document.file_id, title=update.document.file_unique_id)] + message.attachments += [Document(id=update.document.file_id, title=update.document.file_unique_id, from_messenger_interface=self)] return message @@ -78,13 +81,10 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes for attachment in message.attachments: if isinstance(attachment, Location): await bot.send_location(chat_id, attachment.latitude, attachment.longitude, reply_markup=self._create_keyboard(buttons)) - return if isinstance(attachment, Contact): await bot.send_contact(chat_id, attachment.phone_number, attachment.first_name, attachment.last_name, reply_markup=self._create_keyboard(buttons)) - return if isinstance(attachment, Poll): await bot.send_poll(chat_id, attachment.question, [option.text for option in attachment.options], reply_markup=self._create_keyboard(buttons)) - return if isinstance(attachment, Audio): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: @@ -151,15 +151,3 @@ def __init__(self, token: str, host: str = "localhost", port: int = 844): async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs): await super().connect(pipeline_runner, *args, **kwargs) self.application.run_webhook(listen=self.listen, port=self.port, allowed_updates=Update.ALL_TYPES) - - -def telegram_condition(func: Callable[[Update], bool]): # pragma: no cover - - def condition(ctx: Context, _: Pipeline, *__, **___): # pragma: no cover - last_request = ctx.last_request - if last_request is None or last_request.original_message is None: - return False - original_message = cast(Update, last_request.original_message) - return func(original_message) - - return condition diff --git a/dff/script/core/message.py b/dff/script/core/message.py index 780b1daf5..95c4dccb9 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -104,10 +104,11 @@ class DataAttachment(Attachment): source: Optional[Union[HttpUrl, FilePath]] = None id: Optional[str] = None # id field is made separate to simplify type validation title: Optional[str] = None + from_messenger_interface: MessengerInterface = Field(exclude=True) - async def get_bytes(self, interface: MessengerInterface) -> Optional[bytes]: + async def get_bytes(self) -> Optional[bytes]: if self.source is None: - await interface.populate_attachment(self) + await self.from_messenger_interface.populate_attachment(self) if isinstance(self.source, Path): with open(self.source, "rb") as file: return file.read() diff --git a/tutorials/messengers/telegram/4_conditions.py b/tutorials/messengers/telegram/4_conditions.py index f32f791dd..69641a27c 100644 --- a/tutorials/messengers/telegram/4_conditions.py +++ b/tutorials/messengers/telegram/4_conditions.py @@ -5,7 +5,7 @@ This tutorial shows how to process Telegram updates in your script and reuse handler triggers from the `pytelegrambotapi` library. -Here, %mddoclink(api,messengers.telegram.messenger,telegram_condition) +Here, %mddoclink(api,messengers.telegram.messenger) function is used for graph navigation according to Telegram events. """ @@ -18,7 +18,7 @@ from dff.script import TRANSITIONS, RESPONSE import dff.script.conditions as cnd -from dff.messengers.telegram import PollingTelegramInterface, telegram_condition +from dff.messengers.telegram import PollingTelegramInterface from dff.pipeline import Pipeline from dff.script.core.message import Message from dff.utils.testing.common import is_interactive_mode @@ -29,15 +29,10 @@ In our Telegram module, we adopted the system of filters available in the `pytelegrambotapi` library. -You can use `telegram_condition` to filter -text messages from telegram in various ways. - - Setting the `update_type` will allow filtering by update type: if you want the condition to trigger only on updates of the type `edited_message`, set it to `UpdateType.EDITED_MESSAGE`. The field defaults to `message`. -- Setting the `command` argument will cause - the telegram_condition to only react to listed commands. - `func` argument on the other hand allows you to define arbitrary conditions. - `regexp` creates a regular expression filter, etc. @@ -73,21 +68,16 @@ TRANSITIONS: { "node2": cnd.regexp("fine") }, - # this is the same as - # TRANSITIONS: {"node2": telegram_condition(regexp="fine")}, }, "node2": { RESPONSE: Message( text="Good. What do you want to talk about?" ), TRANSITIONS: { - "node3": telegram_condition( - func=lambda upd: ( - upd.message is not None - and upd.message.text is not None - and "music" in upd.message.text - ) - ) + "node3": lambda ctx, _, __, ___: + ctx.last_request.original_message.message is not None + and ctx.last_request.original_message.message.text is not None + and "music" in ctx.last_request.original_message.message.text }, }, "node3": { @@ -95,13 +85,13 @@ text="Sorry, I can not talk about music now." ), TRANSITIONS: { - "node4": telegram_condition(func=lambda _: True) + "node4": lambda _, __, ___, ____: True }, # This condition is true for any type of update }, "node4": { RESPONSE: Message(text="bye"), - TRANSITIONS: {"node1": telegram_condition(func=lambda _: True)}, + TRANSITIONS: {"node1": lambda _, __, ___, ____: True}, # This condition is true if the last update is of type `message` }, "fallback_node": { diff --git a/tutorials/messengers/telegram/5_conditions_with_media.py b/tutorials/messengers/telegram/5_conditions_with_media.py index b39dbd84a..67ecaaebb 100644 --- a/tutorials/messengers/telegram/5_conditions_with_media.py +++ b/tutorials/messengers/telegram/5_conditions_with_media.py @@ -4,7 +4,7 @@ This tutorial shows how to use media-related logic in your script. -Here, %mddoclink(api,messengers.telegram.messenger,telegram_condition) +Here, %mddoclink(api,messengers.telegram.messenger) function is used for graph navigation according to Telegram events. Different %mddoclink(api,script.core.message,message) @@ -17,15 +17,13 @@ # %% import os -from typing import cast from pydantic import HttpUrl -from telegram import Update, Message as TelegramMessage import dff.script.conditions as cnd -from dff.script import Context, TRANSITIONS, RESPONSE +from dff.script import TRANSITIONS, RESPONSE from dff.script.core.message import Message, Image -from dff.messengers.telegram import PollingTelegramInterface, telegram_condition +from dff.messengers.telegram import PollingTelegramInterface from dff.pipeline import Pipeline from dff.utils.testing.common import is_interactive_mode @@ -38,7 +36,7 @@ # %% [markdown] """ To filter user messages depending on whether or not media files were sent, -you can use the `content_types` parameter of the `telegram_condition`. +you can use the `content_types` parameter of the `ctx.last_request.original_message.message.document`. """ @@ -83,25 +81,18 @@ # both in 'photo' and 'document' fields. # We should consider both cases # when we check the message for media. - telegram_condition( - func=lambda update: ( - update.message is not None - and len(update.message.photo) > 0 - ) - ), - telegram_condition( - func=lambda update: ( - # check attachments in message properties - update.message is not None - and update.message.document is not None - and update.message.document.mime_type == "image/jpeg" - ) - ), + lambda ctx, _, __, ___: + ctx.last_request.original_message.message is not None + and len(ctx.last_request.original_message.message.photo) > 0, + lambda ctx, _, __, ___: + ctx.last_request.original_message.message is not None + and ctx.last_request.original_message.message.document is not None + and ctx.last_request.original_message.message.document.mime_type == "image/jpeg", ] ), - ("pics", "send_many"): telegram_condition( - func=lambda upd: upd.message is not None and upd.message.text is not None - ), + ("pics", "send_many"): lambda ctx, _, __, ___: + ctx.last_request.original_message.message is not None + and ctx.last_request.original_message.message.text is not None, ("pics", "ask_picture"): cnd.true(), }, }, diff --git a/tutorials/messengers/telegram/6_conditions_extras.py b/tutorials/messengers/telegram/6_conditions_extras.py index ea2b8da15..7ed535e0a 100644 --- a/tutorials/messengers/telegram/6_conditions_extras.py +++ b/tutorials/messengers/telegram/6_conditions_extras.py @@ -5,7 +5,7 @@ This tutorial shows how to use additional update filters inherited from the `pytelegrambotapi` library. -%mddoclink(api,messengers.telegram.messenger,telegram_condition) +%mddoclink(api,messengers.telegram.messenger) function and different types of %mddoclink(api,messengers.telegram.messenger,UpdateType) are used for telegram message type checking. @@ -19,7 +19,7 @@ from dff.script import TRANSITIONS, RESPONSE, GLOBAL import dff.script.conditions as cnd -from dff.messengers.telegram import PollingTelegramInterface, telegram_condition +from dff.messengers.telegram import PollingTelegramInterface from dff.pipeline import Pipeline from dff.script.core.message import Message from dff.utils.testing.common import is_interactive_mode @@ -60,13 +60,12 @@ GLOBAL: { TRANSITIONS: { # say hi when someone enters the chat - ("greeting_flow", "node1"): telegram_condition( - func=lambda x: x.message is not None and x.message.new_chat_members is not None, - ), + ("greeting_flow", "node1"): lambda ctx, _, __, ___: + ctx.last_request.original_message.message is not None + and ctx.last_request.original_message.message.new_chat_members is not None, # send a message when inline query is received - ("greeting_flow", "node2"): telegram_condition( - func=lambda x: x.inline_query is not None, - ), + ("greeting_flow", "node2"): lambda ctx, _, __, ___: + ctx.last_request.original_message.inline_query is not None, }, }, "greeting_flow": { From 6f8de0b5a9dab17439760375c8669af8a34cd912 Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 19 Mar 2024 00:24:33 +0100 Subject: [PATCH 014/140] lock file updated --- poetry.lock | 1399 ++++++++++++++++++++++++--------------------------- 1 file changed, 662 insertions(+), 737 deletions(-) diff --git a/poetry.lock b/poetry.lock index 343d984ba..75fe27bb9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -570,34 +570,6 @@ files = [ {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, ] -[[package]] -name = "backports-zoneinfo" -version = "0.2.1" -description = "Backport of the standard library zoneinfo module" -optional = false -python-versions = ">=3.6" -files = [ - {file = "backports.zoneinfo-0.2.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:da6013fd84a690242c310d77ddb8441a559e9cb3d3d59ebac9aca1a57b2e18bc"}, - {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:89a48c0d158a3cc3f654da4c2de1ceba85263fafb861b98b59040a5086259722"}, - {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:1c5742112073a563c81f786e77514969acb58649bcdf6cdf0b4ed31a348d4546"}, - {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win32.whl", hash = "sha256:e8236383a20872c0cdf5a62b554b27538db7fa1bbec52429d8d106effbaeca08"}, - {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:8439c030a11780786a2002261569bdf362264f605dfa4d65090b64b05c9f79a7"}, - {file = "backports.zoneinfo-0.2.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:f04e857b59d9d1ccc39ce2da1021d196e47234873820cbeaad210724b1ee28ac"}, - {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:17746bd546106fa389c51dbea67c8b7c8f0d14b5526a579ca6ccf5ed72c526cf"}, - {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5c144945a7752ca544b4b78c8c41544cdfaf9786f25fe5ffb10e838e19a27570"}, - {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win32.whl", hash = "sha256:e55b384612d93be96506932a786bbcde5a2db7a9e6a4bb4bffe8b733f5b9036b"}, - {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a76b38c52400b762e48131494ba26be363491ac4f9a04c1b7e92483d169f6582"}, - {file = "backports.zoneinfo-0.2.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:8961c0f32cd0336fb8e8ead11a1f8cd99ec07145ec2931122faaac1c8f7fd987"}, - {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e81b76cace8eda1fca50e345242ba977f9be6ae3945af8d46326d776b4cf78d1"}, - {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7b0a64cda4145548fed9efc10322770f929b944ce5cee6c0dfe0c87bf4c0c8c9"}, - {file = "backports.zoneinfo-0.2.1-cp38-cp38-win32.whl", hash = "sha256:1b13e654a55cd45672cb54ed12148cd33628f672548f373963b0bff67b217328"}, - {file = "backports.zoneinfo-0.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:4a0f800587060bf8880f954dbef70de6c11bbe59c673c3d818921f042f9954a6"}, - {file = "backports.zoneinfo-0.2.1.tar.gz", hash = "sha256:fadbfe37f74051d024037f223b8e001611eac868b5c5b06144ef4d8b799862f2"}, -] - -[package.extras] -tzdata = ["tzdata"] - [[package]] name = "beautifulsoup4" version = "4.12.3" @@ -621,33 +593,33 @@ lxml = ["lxml"] [[package]] name = "black" -version = "24.2.0" +version = "24.3.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.8" files = [ - {file = "black-24.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6981eae48b3b33399c8757036c7f5d48a535b962a7c2310d19361edeef64ce29"}, - {file = "black-24.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d533d5e3259720fdbc1b37444491b024003e012c5173f7d06825a77508085430"}, - {file = "black-24.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61a0391772490ddfb8a693c067df1ef5227257e72b0e4108482b8d41b5aee13f"}, - {file = "black-24.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:992e451b04667116680cb88f63449267c13e1ad134f30087dec8527242e9862a"}, - {file = "black-24.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:163baf4ef40e6897a2a9b83890e59141cc8c2a98f2dda5080dc15c00ee1e62cd"}, - {file = "black-24.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e37c99f89929af50ffaf912454b3e3b47fd64109659026b678c091a4cd450fb2"}, - {file = "black-24.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9de21bafcba9683853f6c96c2d515e364aee631b178eaa5145fc1c61a3cc92"}, - {file = "black-24.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:9db528bccb9e8e20c08e716b3b09c6bdd64da0dd129b11e160bf082d4642ac23"}, - {file = "black-24.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d84f29eb3ee44859052073b7636533ec995bd0f64e2fb43aeceefc70090e752b"}, - {file = "black-24.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e08fb9a15c914b81dd734ddd7fb10513016e5ce7e6704bdd5e1251ceee51ac9"}, - {file = "black-24.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:810d445ae6069ce64030c78ff6127cd9cd178a9ac3361435708b907d8a04c693"}, - {file = "black-24.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ba15742a13de85e9b8f3239c8f807723991fbfae24bad92d34a2b12e81904982"}, - {file = "black-24.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7e53a8c630f71db01b28cd9602a1ada68c937cbf2c333e6ed041390d6968faf4"}, - {file = "black-24.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:93601c2deb321b4bad8f95df408e3fb3943d85012dddb6121336b8e24a0d1218"}, - {file = "black-24.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0057f800de6acc4407fe75bb147b0c2b5cbb7c3ed110d3e5999cd01184d53b0"}, - {file = "black-24.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:faf2ee02e6612577ba0181f4347bcbcf591eb122f7841ae5ba233d12c39dcb4d"}, - {file = "black-24.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:057c3dc602eaa6fdc451069bd027a1b2635028b575a6c3acfd63193ced20d9c8"}, - {file = "black-24.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:08654d0797e65f2423f850fc8e16a0ce50925f9337fb4a4a176a7aa4026e63f8"}, - {file = "black-24.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca610d29415ee1a30a3f30fab7a8f4144e9d34c89a235d81292a1edb2b55f540"}, - {file = "black-24.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:4dd76e9468d5536abd40ffbc7a247f83b2324f0c050556d9c371c2b9a9a95e31"}, - {file = "black-24.2.0-py3-none-any.whl", hash = "sha256:e8a6ae970537e67830776488bca52000eaa37fa63b9988e8c487458d9cd5ace6"}, - {file = "black-24.2.0.tar.gz", hash = "sha256:bce4f25c27c3435e4dace4815bcb2008b87e167e3bf4ee47ccdc5ce906eb4894"}, + {file = "black-24.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395"}, + {file = "black-24.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995"}, + {file = "black-24.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2af80566f43c85f5797365077fb64a393861a3730bd110971ab7a0c94e873e7"}, + {file = "black-24.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:4be5bb28e090456adfc1255e03967fb67ca846a03be7aadf6249096100ee32d0"}, + {file = "black-24.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4f1373a7808a8f135b774039f61d59e4be7eb56b2513d3d2f02a8b9365b8a8a9"}, + {file = "black-24.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aadf7a02d947936ee418777e0247ea114f78aff0d0959461057cae8a04f20597"}, + {file = "black-24.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c02e4ea2ae09d16314d30912a58ada9a5c4fdfedf9512d23326128ac08ac3d"}, + {file = "black-24.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf21b7b230718a5f08bd32d5e4f1db7fc8788345c8aea1d155fc17852b3410f5"}, + {file = "black-24.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:2818cf72dfd5d289e48f37ccfa08b460bf469e67fb7c4abb07edc2e9f16fb63f"}, + {file = "black-24.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4acf672def7eb1725f41f38bf6bf425c8237248bb0804faa3965c036f7672d11"}, + {file = "black-24.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7ed6668cbbfcd231fa0dc1b137d3e40c04c7f786e626b405c62bcd5db5857e4"}, + {file = "black-24.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:56f52cfbd3dabe2798d76dbdd299faa046a901041faf2cf33288bc4e6dae57b5"}, + {file = "black-24.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:79dcf34b33e38ed1b17434693763301d7ccbd1c5860674a8f871bd15139e7837"}, + {file = "black-24.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e19cb1c6365fd6dc38a6eae2dcb691d7d83935c10215aef8e6c38edee3f77abd"}, + {file = "black-24.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b76c275e4c1c5ce6e9870911384bff5ca31ab63d19c76811cb1fb162678213"}, + {file = "black-24.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:b5991d523eee14756f3c8d5df5231550ae8993e2286b8014e2fdea7156ed0959"}, + {file = "black-24.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c45f8dff244b3c431b36e3224b6be4a127c6aca780853574c00faf99258041eb"}, + {file = "black-24.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6905238a754ceb7788a73f02b45637d820b2f5478b20fec82ea865e4f5d4d9f7"}, + {file = "black-24.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7de8d330763c66663661a1ffd432274a2f92f07feeddd89ffd085b5744f85e7"}, + {file = "black-24.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:7bb041dca0d784697af4646d3b62ba4a6b028276ae878e53f6b4f74ddd6db99f"}, + {file = "black-24.3.0-py3-none-any.whl", hash = "sha256:41622020d7120e01d377f74249e677039d20e6344ff5851de8a10f11f513bf93"}, + {file = "black-24.3.0.tar.gz", hash = "sha256:a0c9c4a0771afc6919578cec71ce82a3e31e054904e7197deacbc9382671c41f"}, ] [package.dependencies] @@ -788,18 +760,18 @@ files = [ [[package]] name = "build" -version = "1.0.3" +version = "1.1.1" description = "A simple, correct Python build frontend" optional = false python-versions = ">= 3.7" files = [ - {file = "build-1.0.3-py3-none-any.whl", hash = "sha256:589bf99a67df7c9cf07ec0ac0e5e2ea5d4b37ac63301c4986d1acb126aa83f8f"}, - {file = "build-1.0.3.tar.gz", hash = "sha256:538aab1b64f9828977f84bc63ae570b060a8ed1be419e7870b8b4fc5e6ea553b"}, + {file = "build-1.1.1-py3-none-any.whl", hash = "sha256:8ed0851ee76e6e38adce47e4bee3b51c771d86c64cf578d0c2245567ee200e73"}, + {file = "build-1.1.1.tar.gz", hash = "sha256:8eea65bb45b1aac2e734ba2cc8dad3a6d97d97901a395bd0ed3e7b46953d2a31"}, ] [package.dependencies] colorama = {version = "*", markers = "os_name == \"nt\""} -importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} +importlib-metadata = {version = ">=4.6", markers = "python_full_version < \"3.10.2\""} packaging = ">=19.0" pyproject_hooks = "*" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} @@ -1058,13 +1030,13 @@ files = [ [[package]] name = "comm" -version = "0.2.1" +version = "0.2.2" description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc." optional = false python-versions = ">=3.8" files = [ - {file = "comm-0.2.1-py3-none-any.whl", hash = "sha256:87928485c0dfc0e7976fd89fc1e187023cf587e7c353e4a9b417555b44adf021"}, - {file = "comm-0.2.1.tar.gz", hash = "sha256:0bc91edae1344d39d3661dcbc36937181fdaddb304790458f8b044dbc064b89a"}, + {file = "comm-0.2.2-py3-none-any.whl", hash = "sha256:e6fb86cb70ff661ee8c9c14e7d36d6de3b4066f1441be4063df9c5009f0a64d3"}, + {file = "comm-0.2.2.tar.gz", hash = "sha256:3fd7a84065306e07bea1773df6eb8282de51ba82f77c72f9c85716ab11fe980e"}, ] [package.dependencies] @@ -1101,63 +1073,63 @@ files = [ [[package]] name = "coverage" -version = "7.4.3" +version = "7.4.4" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"}, - {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"}, - {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"}, - {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"}, - {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"}, - {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"}, - {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"}, - {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"}, - {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"}, - {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"}, - {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"}, - {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"}, - {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"}, - {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"}, - {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"}, - {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"}, - {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"}, - {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"}, - {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"}, - {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"}, - {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"}, - {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"}, - {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"}, - {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"}, - {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"}, - {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"}, - {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"}, - {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"}, - {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"}, - {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"}, - {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"}, - {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"}, - {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"}, - {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"}, - {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"}, - {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"}, - {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"}, - {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"}, - {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"}, - {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"}, - {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"}, - {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"}, - {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"}, - {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"}, - {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"}, - {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"}, - {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"}, - {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"}, - {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"}, - {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"}, - {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"}, - {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"}, + {file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"}, + {file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c"}, + {file = "coverage-7.4.4-cp310-cp310-win32.whl", hash = "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d"}, + {file = "coverage-7.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f"}, + {file = "coverage-7.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf"}, + {file = "coverage-7.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b"}, + {file = "coverage-7.4.4-cp311-cp311-win32.whl", hash = "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286"}, + {file = "coverage-7.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec"}, + {file = "coverage-7.4.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76"}, + {file = "coverage-7.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9"}, + {file = "coverage-7.4.4-cp312-cp312-win32.whl", hash = "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0"}, + {file = "coverage-7.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e"}, + {file = "coverage-7.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384"}, + {file = "coverage-7.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c"}, + {file = "coverage-7.4.4-cp38-cp38-win32.whl", hash = "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e"}, + {file = "coverage-7.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8"}, + {file = "coverage-7.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d"}, + {file = "coverage-7.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade"}, + {file = "coverage-7.4.4-cp39-cp39-win32.whl", hash = "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57"}, + {file = "coverage-7.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c"}, + {file = "coverage-7.4.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677"}, + {file = "coverage-7.4.4.tar.gz", hash = "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49"}, ] [package.dependencies] @@ -1755,119 +1727,119 @@ test = ["cffi (>=1.12.2)", "coverage (>=5.0)", "dnspython (>=1.16.0,<2.0)", "idn [[package]] name = "geventhttpclient" -version = "2.0.11" +version = "2.0.12" description = "http client library for gevent" optional = false python-versions = "*" files = [ - {file = "geventhttpclient-2.0.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f509176bc7754b1181375a25ec6909425a5997e58c98ea29a36fe8b6a376852f"}, - {file = "geventhttpclient-2.0.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cda51b46d8ab3993763a394ed6601137c32f70cff78dfe703edecb3dfa143009"}, - {file = "geventhttpclient-2.0.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:072f24198c0f179fcd8567e9270d5cb78ceea1d562a55b052cd083cf4c67feef"}, - {file = "geventhttpclient-2.0.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b97c84e9be76bdd726757437327be5446710eafb64f7097d8d86db9c0f7d280"}, - {file = "geventhttpclient-2.0.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:abb32554c1ad103ed1114cee3d75fa6a3c5d8a0898e4e64db68f3fc0f11fb0de"}, - {file = "geventhttpclient-2.0.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78a7e493e09d0aa4ba9651147d02fc555159371fecab0e4e96196c72f191322e"}, - {file = "geventhttpclient-2.0.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e521089a3a95c98e1742f1a1ea41568b029bc2528cc6fc7ab91bb5d416f1f2c"}, - {file = "geventhttpclient-2.0.11-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8329c60d94e688d75ec1c6f67a77ab96f726f8ea562a8d48afa1ed6470334a6f"}, - {file = "geventhttpclient-2.0.11-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:572364fc4acd7ff2e77641e6bd1e64cf315d899a7fc48953eac1dd3b6865fd99"}, - {file = "geventhttpclient-2.0.11-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:81e73ee32f4217072935825a0bad7264dc803b0d24cc4e2f4bfcac3fff49a899"}, - {file = "geventhttpclient-2.0.11-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d79ee0d7ab5d775b056400155cab1e3547a7fa6511f6098e25613ed8705ae8b8"}, - {file = "geventhttpclient-2.0.11-cp310-cp310-win32.whl", hash = "sha256:2911d3657e2426b6a2d59af0b52285c1a7c4a78d0e4d03ee4ec1d5195a25a09f"}, - {file = "geventhttpclient-2.0.11-cp310-cp310-win_amd64.whl", hash = "sha256:a489573a0a0559f8960b38795dc53d1e222bc0978b211763d1303b2f94e4c3e0"}, - {file = "geventhttpclient-2.0.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1e27a9521e0ad0d97d0ff81578fd4dd6ae9eee8095d46edb820dfda33c0bd233"}, - {file = "geventhttpclient-2.0.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d54b886ce042186a4f731dcbcb4ffa8d674b0542907fc72de20d0b5088adc252"}, - {file = "geventhttpclient-2.0.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f2337e10e2ad20970436f216d7b3b8d1503f8e4645d439173a98b4b418fe5768"}, - {file = "geventhttpclient-2.0.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f41bcdcec859264a1b6cc7c57bdb9411da8047f17b982cb62756bcc74a1b045b"}, - {file = "geventhttpclient-2.0.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f5d73be013a7a2a357eb27d18e5990c773365f63f50a43eaf357d6efb1fd46a6"}, - {file = "geventhttpclient-2.0.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4d86f042501a783e94188ef8b099f32bc4680f2423bbbb56f40158d4556a56b"}, - {file = "geventhttpclient-2.0.11-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaa2bc31a38dbb387c7539cfa03d3bafaa32151972d34b42f2f648b66778e128"}, - {file = "geventhttpclient-2.0.11-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3e24ff4c398f9e49c5c0740585f12fcf7033dc27a20ec884f3b2c729e2f47f14"}, - {file = "geventhttpclient-2.0.11-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b70f80528ae74518a16214261abba2a276739e6e35ce518fdbd8be2a3f42f93a"}, - {file = "geventhttpclient-2.0.11-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:efa467997f87d39f774ed1916a9e184c9a936f8fa90ab1a8ebf97aba2ee7ed63"}, - {file = "geventhttpclient-2.0.11-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4597ea18ddc9838dc0e6cb9d5efb812191f2ca65ab38c115a56894045c73ea40"}, - {file = "geventhttpclient-2.0.11-cp311-cp311-win32.whl", hash = "sha256:a4361c5a522d2a79d8a9047926b8f8926e0f797777da9f450d359bed9f33ac33"}, - {file = "geventhttpclient-2.0.11-cp311-cp311-win_amd64.whl", hash = "sha256:f430257a7b0a75e7f4c0d6f4f3f8960d45b5aae56b8eca7988963487501a52a0"}, - {file = "geventhttpclient-2.0.11-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a84f48f2eff42171cc446690baffa914122e88cea5b1de44cf6dd1c82b07623b"}, - {file = "geventhttpclient-2.0.11-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0a21dba9cf5e7511e76845f62dcf5072f4df7415bb8f20e47e0dfde675943a39"}, - {file = "geventhttpclient-2.0.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:99feb5581111c7ec44e1ce507b4420947b4c49b363b2fbc3edd543e2ac67a1e0"}, - {file = "geventhttpclient-2.0.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bc799d50db685e093b5819459889f356dd7478a82af66f880832a95fcfa37c3"}, - {file = "geventhttpclient-2.0.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:94a8be54ac74ff6cf4703d049766e6ed07787fa9b6a2dd538c46f81de72ffdde"}, - {file = "geventhttpclient-2.0.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:71a9e152bb3cb86552f61659f3c7bdc272d9baf21726b3caceb5ab5d0e703fe6"}, - {file = "geventhttpclient-2.0.11-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05a7699b49c9bc478b7ae165809ff97b21811a624791abe3927da5066128a10c"}, - {file = "geventhttpclient-2.0.11-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:598951751b2162b0697cd5b6a9edcc65ec30f34388b6e09caaa0c453fb08fb6e"}, - {file = "geventhttpclient-2.0.11-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4f0c773ceeeedfab56b24b97a0c8f04c58a716dfc7403e51ea898ad01599f1a6"}, - {file = "geventhttpclient-2.0.11-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:ee03ea884e6aa318078c0c7132d246fe92b51d587410532e63b864e6e61ea192"}, - {file = "geventhttpclient-2.0.11-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:98a25e30ddccd49f80e037d48f136050b8f3c24ed9c6a69df7a643989f29c4e8"}, - {file = "geventhttpclient-2.0.11-cp312-cp312-win32.whl", hash = "sha256:968587b59372e825411935e188b9a05dcdec6db6769be3eb3bba949cb414ae98"}, - {file = "geventhttpclient-2.0.11-cp312-cp312-win_amd64.whl", hash = "sha256:465e62fb055e2ca5907606d32d421970f93506309b11a33b367eef33d95a6b7a"}, - {file = "geventhttpclient-2.0.11-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ba597da51b59df28cf484326d7d59e33a57d3b32d7a4e1646c580f175354d6ce"}, - {file = "geventhttpclient-2.0.11-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c125a225188bcacd51f05878d6e62554116a5be6b3a203cd0ba2460857bc8cd3"}, - {file = "geventhttpclient-2.0.11-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f016093e8d26b724efdeda776968368fb591a57afbded2d86c408db8723e38ce"}, - {file = "geventhttpclient-2.0.11-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a25a7fc768791cf9fe590f1b4f231727441e8f7e9279e8ae2bee83e0f3b010f8"}, - {file = "geventhttpclient-2.0.11-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae71a7740526be78c6e899b03b63ab47a1a434332f7ca725dcdc916d938d46c6"}, - {file = "geventhttpclient-2.0.11-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:06914f401541681d8cb834652f53e65a8179ea17dd0e496fd52712fd3f548fbb"}, - {file = "geventhttpclient-2.0.11-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:6ccdebfd20ab07ace7aa4dcd020f094d1cae237b4eacfca08ac523cac64e02d3"}, - {file = "geventhttpclient-2.0.11-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:b2bea1386dbfd262571157da319e2285e20844fdbaabb22f95e784ca8b47d90c"}, - {file = "geventhttpclient-2.0.11-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:f468f88df7649bfcc6f74878182d0b7bcb3c23445a76be2b8b59e46224e2c244"}, - {file = "geventhttpclient-2.0.11-cp36-cp36m-win32.whl", hash = "sha256:d75c706f2a2401f703585cddf51cb0e43c28b7f12b1998c4a41fd6d14feec89b"}, - {file = "geventhttpclient-2.0.11-cp36-cp36m-win_amd64.whl", hash = "sha256:27f9e22a31451087854204f7f341bd4adc32050180580f74b5de75b61a3b405f"}, - {file = "geventhttpclient-2.0.11-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:105af48455d4eecb4e0f2b2b7f766131811aa1a9a1e768fb020b9ae0ba840ee4"}, - {file = "geventhttpclient-2.0.11-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb9e9c6f3fb902dd622964097df77e0ed9b249b8904b44fc3461734cc791b0aa"}, - {file = "geventhttpclient-2.0.11-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1b73c37fbecb26475fa6e2d018dab4b5a03c7ba08c8907598605c874a70ee79"}, - {file = "geventhttpclient-2.0.11-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09167de901f5b5273ddc14fd53512cc696495be07f02e3cb8a0335e1ecbff57e"}, - {file = "geventhttpclient-2.0.11-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52ac561df8d363fe2e00ba4cccea470745129a48bb86f665a1447d0d68abec54"}, - {file = "geventhttpclient-2.0.11-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ceb038cbf92105d124433066685c73e6a4a762c15885f00be2e25663468e4f29"}, - {file = "geventhttpclient-2.0.11-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:0b70eedf64c162067765ddfb30c8f52daeb875c717a3d25f81d5e411e5ac4367"}, - {file = "geventhttpclient-2.0.11-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:e87fb8bd748bf32e9902e9cbea3f20ff5456705d3f53f0a8ea0c4983594457a8"}, - {file = "geventhttpclient-2.0.11-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0ae01d50529ac739573bc9cbc192b71bf9a13c3fcdbf2054952947a25e9f75a3"}, - {file = "geventhttpclient-2.0.11-cp37-cp37m-win32.whl", hash = "sha256:beb3a99e7a0a5130fbed2453348d81a78f2ef7d6aa326b5799c7f3dde88cabea"}, - {file = "geventhttpclient-2.0.11-cp37-cp37m-win_amd64.whl", hash = "sha256:63fc49d73e70cab8316a4d0106c037a2a5d0f6650683af05d0d05d354b694d49"}, - {file = "geventhttpclient-2.0.11-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:106e2ba0ce34a3501651995dd46ed38b87e7b5ada0fb977142d952661853f36a"}, - {file = "geventhttpclient-2.0.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0edacd51cd9a6f0b88e25cb6c8744488ba6c7c22044b09de585b2a1224f2a7b9"}, - {file = "geventhttpclient-2.0.11-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2214352e01fef4218bbbc61bd84af6f101bb5a33244088f6db28ff6d1141797f"}, - {file = "geventhttpclient-2.0.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38384af2da776563a19951958df65e31ecc7b8d20788d43aff35ec909e4a115f"}, - {file = "geventhttpclient-2.0.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:33c4af3aa0312c27668171ea061d461f678848a09a32953b4d895f72a1bde0c9"}, - {file = "geventhttpclient-2.0.11-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d461cdac133d4a4d173e2c1cc213f3a9924e6e092aeebd49bf8924719a073e0b"}, - {file = "geventhttpclient-2.0.11-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ad49019e2828508526d35e7026b95a1fd9ef49ed0cdd2526a5cb3eb39583640"}, - {file = "geventhttpclient-2.0.11-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a59b164a68bbb1a6f7bee859d7e75ef148b1e9bd72c4810c712cd49603dc37cd"}, - {file = "geventhttpclient-2.0.11-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6cc44c57c02db1ded6f5a6bd4ccc385c4d13c7ae3528b831e70b5cc87e5b0ad1"}, - {file = "geventhttpclient-2.0.11-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:2d7318b3493c2e21df79429be3dbfefbc254c41a5b5c02c148a4521d59169ad6"}, - {file = "geventhttpclient-2.0.11-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:40df90cd9b5f5f7355526cc538e626466cb60c2e737e9cb8958569377d568e9f"}, - {file = "geventhttpclient-2.0.11-cp38-cp38-win32.whl", hash = "sha256:6f89edc316a8ff967a50c6f98277619786ed6abf2dd36ea905baf840a02b1b1b"}, - {file = "geventhttpclient-2.0.11-cp38-cp38-win_amd64.whl", hash = "sha256:b179a13c113a90c5501f1b1121bdc4c1f816d942280a9c3d2d46aff2bc97269a"}, - {file = "geventhttpclient-2.0.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:63826170b520894578bd269b54139bb2f0cc2d96ae1f4a49b3928fe01ffa22ff"}, - {file = "geventhttpclient-2.0.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1a6fcc3968ea1adf764bc11b0e7d01b94ffe27bdd21c5b1d9e55be56de6a53c3"}, - {file = "geventhttpclient-2.0.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4c61c02c2d32e1b5b1f73d2b201c1e088e956b73e431ed6b5589010faed88380"}, - {file = "geventhttpclient-2.0.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aec646409fa6eee277e33a1f4f1860d4c25e0448eedea149df92918d4502f38c"}, - {file = "geventhttpclient-2.0.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0b91290138518b201fba98bc82b062ef32e5e3da28843998902852298c354dcf"}, - {file = "geventhttpclient-2.0.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b523860ee558f752847b29ad6678d1b8a40154d06bc7a8973132991aff727fdd"}, - {file = "geventhttpclient-2.0.11-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5594bc889a686511039d1efd17473eecc4a91fa01d66a59bfa0a8cf04fb34551"}, - {file = "geventhttpclient-2.0.11-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e573b86999cfeae38c4dd881f05818b9a60245a6763bc77efb48fa136cefdfcc"}, - {file = "geventhttpclient-2.0.11-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a30bd715480ddbab0217764b516a65e36ecee2e81c9a04d074769eec6e0c1681"}, - {file = "geventhttpclient-2.0.11-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:49ff1c00e64e0820a02fadc6a72b49ae8cc69028caa40170873a3012de98d475"}, - {file = "geventhttpclient-2.0.11-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ea232981e29869524e85b5e6c79ad64abf40dd7b6dc01be6765b5e6bd191fd73"}, - {file = "geventhttpclient-2.0.11-cp39-cp39-win32.whl", hash = "sha256:a0b30fef1eb118927b5d8cab106198883f1bde021e9036277ea2f9e0020e0ad2"}, - {file = "geventhttpclient-2.0.11-cp39-cp39-win_amd64.whl", hash = "sha256:844b30e3694a4d9518fe6f0b167fa3ffc3ea3444563d9fdd7a18a961f6a77d9c"}, - {file = "geventhttpclient-2.0.11-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:94579ec289d46fca939b78cfe91732e82491f3dab03604f974a2e711654e7210"}, - {file = "geventhttpclient-2.0.11-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:955b04deac7ea09a3d5183ba92a3d2a81121ad71d10f1489cb56fd31d0cb4ac4"}, - {file = "geventhttpclient-2.0.11-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7947aae2d7123a970669ebd763a09ef0c85104cda414689dd77b5e5a5c1f2a40"}, - {file = "geventhttpclient-2.0.11-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c483daa1deda0c52a77ed7af2906a38657c15120cb3240bf589dfb139255921"}, - {file = "geventhttpclient-2.0.11-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bc9634e025f17dc25987ebd5b0461659178ca57052ec70ad65052d0495111a74"}, - {file = "geventhttpclient-2.0.11-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9dca243f58f245872458647b0b6da4be9ce8d707639d76a50d2e8d3f4abb1659"}, - {file = "geventhttpclient-2.0.11-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64d36604974bc2b2ed0166bc666cead87f3c0f2d9487ef73d4e11df9ba6ebcc8"}, - {file = "geventhttpclient-2.0.11-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46677a56fa9f2f650be74024601b3a1968cfc58a434f5819fc2fc227bb292836"}, - {file = "geventhttpclient-2.0.11-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:989a1ed8dbdaf683dd5701087b736b93e6bacb3c29f4090014e64033cc8620e2"}, - {file = "geventhttpclient-2.0.11-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:9b406ef64382a9c42b88331cdd6639a2b998e8034dbb1b702264d27c01f3ad5d"}, - {file = "geventhttpclient-2.0.11-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:713530c8f67a08ce0d5a4af80045112213c63eacefa1c08b76beebf780c755b0"}, - {file = "geventhttpclient-2.0.11-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd894ec63974fe4e916a1bf6efd35307b86ef53bd88e8fbe61020a289fee2f7c"}, - {file = "geventhttpclient-2.0.11-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e18e622171d09f068b26304b7d3c484d55952813e09eec5b3db1012dc53795de"}, - {file = "geventhttpclient-2.0.11-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ce8421aa0a2307edf04a7086236e7e9f9188ab349154c409d723744032746eb"}, - {file = "geventhttpclient-2.0.11-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:237eba77682553253040588f136a2980dfcd71307202422a17b716e9d8be5614"}, - {file = "geventhttpclient-2.0.11-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:60641b8ff7077a57bb68f1189c8ae8ffc6f14ae238ba6a81748659c30894d580"}, - {file = "geventhttpclient-2.0.11-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5939bca6ab38a482352be8a7141570464d4d18281d8a3a2e2f7a82a0d8c38c4"}, - {file = "geventhttpclient-2.0.11-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:025026620e5a369844b576981ddab25d60e7e3bb0e0657c1fe9360a52769eb9d"}, - {file = "geventhttpclient-2.0.11-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b48b10e2a812b9297ad5c43e7a1a088220940060bbfb84fb721b17ab3012e0d"}, - {file = "geventhttpclient-2.0.11-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e572e63e51fde06c30beabf8021e7d3f93e198a9c241ef2f3ed16d7828966768"}, - {file = "geventhttpclient-2.0.11.tar.gz", hash = "sha256:549d0f3af08420b9ad2beeda211153c7605b5ba409b228db7f1b81c8bfbec6b4"}, + {file = "geventhttpclient-2.0.12-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6d0fafc15bbd93b1f271b4c14b327d15c6930c8d78d8ee0d8a55c9cd3e34c18f"}, + {file = "geventhttpclient-2.0.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3f429ece7b6612ef333e9bbeb205513cec33a178f545b3612530a9c5c36a0310"}, + {file = "geventhttpclient-2.0.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:20ffc5a2b9cb5557d529d9296ffdaa5057a23e6bb439a905160a787079ec78a2"}, + {file = "geventhttpclient-2.0.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80a96670c5ab668f52dcaf705640b442faeafb2bfd2e54d5f08ac29ac80aab12"}, + {file = "geventhttpclient-2.0.12-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4766aff690198119c998474d9c20c1b3ffaff337d0d62a6d8b19cc871c3a276d"}, + {file = "geventhttpclient-2.0.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6d15f459737178e2b9a1d37b32161955a7d72062a3fc473d88c9e9f146cff22"}, + {file = "geventhttpclient-2.0.12-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a75007314fc15947fd94f154e139a6f78a4d40ed70d52dbb1724e7ea2d732ca7"}, + {file = "geventhttpclient-2.0.12-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:16e440152ea4f943dfc476462c1c3f29d47d583e679b58bccac9bfaa33eedcfd"}, + {file = "geventhttpclient-2.0.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e1d9c9b533b6c0b5a7eac23f68b25c8d3db1d38b8e504756c53366d2622a24a5"}, + {file = "geventhttpclient-2.0.12-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:758dd4a3172f740255f755fd393f0888e879a7102a537bba98a35a417be30d3e"}, + {file = "geventhttpclient-2.0.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:933426c92e85d8f6717c4d61f2c6c99fbb7d84c91373400eaf381052a35ea414"}, + {file = "geventhttpclient-2.0.12-cp310-cp310-win32.whl", hash = "sha256:e70247c900c4e4413af155e49f342055af9eb20c141735cce36d8a9dc10dc314"}, + {file = "geventhttpclient-2.0.12-cp310-cp310-win_amd64.whl", hash = "sha256:8dac40240fe446b94dd8645e2691d077b98b1e109ed945d2c91923c300c6f66d"}, + {file = "geventhttpclient-2.0.12-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3e3b3b2857ed48dd8af15c8e09539c8e0bf3721f515c0a8f3cfcbe0090196cc4"}, + {file = "geventhttpclient-2.0.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:114cfa7f4db7dcb5603ade4744bc6f5d6d168c94b05aca052e2fc84c906d2009"}, + {file = "geventhttpclient-2.0.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:904aaab04a8c4ebf52217930242693509cfbbd90f2b2afc454e14da82710367f"}, + {file = "geventhttpclient-2.0.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56935ebec38a7c9ccc3dcadaebf2624601567504cd3a44794dc9262aca147040"}, + {file = "geventhttpclient-2.0.12-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bff88eededd1f915cd86de5e8a891e1988b6d42093cc07be5fe3133f8baf170c"}, + {file = "geventhttpclient-2.0.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:212014f4133938ba6453dbfa6d3a643c915dd4873d1de1d6172e4c6c178e4a6c"}, + {file = "geventhttpclient-2.0.12-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d471e79a55d37ad021a4832b0895bccb638f70664040a29230e156a0b92b23d"}, + {file = "geventhttpclient-2.0.12-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:599c4d37d928323b5f0353434f73de9e88f305f59a5472ffc7f5c131a2485512"}, + {file = "geventhttpclient-2.0.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:fddf2b3c4d5d99b826561173be04adbc92cab52081ba142c2158e0ba3b08b762"}, + {file = "geventhttpclient-2.0.12-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:5075c7f15e0a39b7ceae6afcb0b3a66c0ab9364a9eb589b7f51b567835fae5d7"}, + {file = "geventhttpclient-2.0.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:58a6f9d93ef2b1a09479564c951bc7b058350bd757628a32945f274cd314fb98"}, + {file = "geventhttpclient-2.0.12-cp311-cp311-win32.whl", hash = "sha256:a0bb5a35b199356b0c9b5ec3c3152ebfe4ecbd79e00d486d461920a9d96d1fd2"}, + {file = "geventhttpclient-2.0.12-cp311-cp311-win_amd64.whl", hash = "sha256:972a92f4a453a3ef4737e79e7e64f3089a06f385e13493fa19381486e893bd98"}, + {file = "geventhttpclient-2.0.12-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0bee74f32eed6278f3837e07983b5a6b186920c7eb3b35bc6e97360697632655"}, + {file = "geventhttpclient-2.0.12-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fb85d8ed42cc389e5cdac06221f16cb6bca9dbbf5219c44d0731f742a6bffc09"}, + {file = "geventhttpclient-2.0.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c302a16328406003cf4d7d71f59cafc2d42f13d5dc9ea4c8bd248390de985a86"}, + {file = "geventhttpclient-2.0.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3266ef4da21a47d0181d4e3cb5209494e3ce6e4d4cc71414ea74b3a1f7e0e921"}, + {file = "geventhttpclient-2.0.12-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:acb7a257e8f4f0c0335a259f2e9eae527fa042db9ea2e4580a381e9c01fc49f4"}, + {file = "geventhttpclient-2.0.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4741d66098b2b289f584effa7de3ae7bf1efb06e2d83abdbbc468a0a4dec6b3a"}, + {file = "geventhttpclient-2.0.12-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ef2b523043ab9c6057ed19993f629e3fa47f8f92a319f5682de05e604ed8cc9"}, + {file = "geventhttpclient-2.0.12-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:235058a6e420b2aae196a4ba7e23f81ebc2dc3acf6baa9d85dc99963b3e0f0cf"}, + {file = "geventhttpclient-2.0.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:c918d731e0fe676b4c06f53081526f4d3f4836b7a72be7b46c90603a280260fa"}, + {file = "geventhttpclient-2.0.12-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:9e7696a61b384f8d2a075cca9633fdcc897c43eabbcf70fca492206241fa1a3b"}, + {file = "geventhttpclient-2.0.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:105a1aa161223079dbd669b4334cd765964b5917ca4f3da8c5b59c4ef36a0adf"}, + {file = "geventhttpclient-2.0.12-cp312-cp312-win32.whl", hash = "sha256:09e13c05633d1eeb2cba68835618f4ee25c5a7b466c47cfdb01174f61e51a23d"}, + {file = "geventhttpclient-2.0.12-cp312-cp312-win_amd64.whl", hash = "sha256:f853438a1850d45fb434e42ffbb06be81af558e5dd9243d530c2cdc5f804627f"}, + {file = "geventhttpclient-2.0.12-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:62ad2ac321967ff92768e93ea18cb59f8920fbae5b42340b93e7cf11ee4f35d3"}, + {file = "geventhttpclient-2.0.12-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de8b6618a354bded39224def8b6e8b939c468f0edeb2570fdacd9003fd14c57c"}, + {file = "geventhttpclient-2.0.12-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:902ba66284d40dd97a693e952e4bb2f59528806ee40ecd586471fd5bca7fb295"}, + {file = "geventhttpclient-2.0.12-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ef6c9acff6ce379c8a851554954eee6481c35531d82888a46ccada0ea17a791"}, + {file = "geventhttpclient-2.0.12-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e8abf4ccd59d58f7aa91f4c68760d82534bac5c5c9b7d2ccb4c0a5fc69585ff"}, + {file = "geventhttpclient-2.0.12-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:bdeed563faa09fd51ee4606b92f69ecd42b67277ed590f2921816941ed031177"}, + {file = "geventhttpclient-2.0.12-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5a7d9b7e2dbc962f143938cdef8a657af1ccf423b2443a194b86ba0e85735c23"}, + {file = "geventhttpclient-2.0.12-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:a9a7ea4665418abe093e48576769181ae3c75a97cafe107c0463a169af755b2c"}, + {file = "geventhttpclient-2.0.12-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:19488a212c858792fd4fa83be568d4cdbbd4c1267b03b10b6a8a654fd862d2f9"}, + {file = "geventhttpclient-2.0.12-cp36-cp36m-win32.whl", hash = "sha256:445b80548062ef6c1b30e5e1b4ec471937fda64b783da953462972f48c2038de"}, + {file = "geventhttpclient-2.0.12-cp36-cp36m-win_amd64.whl", hash = "sha256:bf283bdbb4b935bfef114e1af19535a602e717ae9a7e8408ab61775d06a463b4"}, + {file = "geventhttpclient-2.0.12-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:370086ced942449f9b60590d215eec7f81fe54d7e3ee3add6b2f014ccac4f38d"}, + {file = "geventhttpclient-2.0.12-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e405735db8261ca99d9b80fda3f46d457f04b98a7ce0e49bb35ca32c2a5bbb2d"}, + {file = "geventhttpclient-2.0.12-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f4680b0ed5e588437797026f25829eb9681954ac64470dc8014436910b2fb09"}, + {file = "geventhttpclient-2.0.12-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad053e7b4ac2f9fcdb02a5d9b99fd72acf28265ba8be7392a25235bb631d2511"}, + {file = "geventhttpclient-2.0.12-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64232158542f2adfde24f41c1e3ed731cca67e469e653ac7634815af168551b4"}, + {file = "geventhttpclient-2.0.12-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9be5c3f68e4f41aceccae38508029a70b1fb3f9fc840b7c55971f5fbe099d7e4"}, + {file = "geventhttpclient-2.0.12-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:06b4276075f4f3eeb30a3c1476f40d53030159450def58c1d8c3b724411d8ed9"}, + {file = "geventhttpclient-2.0.12-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b311beb0657394b5df09af05ec5d84058f3531f3176ab1a0f7f4eae7b56bc315"}, + {file = "geventhttpclient-2.0.12-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b6a9d00b58527328d9f7a0a20b715d4e780a990b0fb75b556085417c22d73dd0"}, + {file = "geventhttpclient-2.0.12-cp37-cp37m-win32.whl", hash = "sha256:987ef3bd0d7e3b01cafc8e135ab6e8f977b60eeda096ead2cb5504124896b1a2"}, + {file = "geventhttpclient-2.0.12-cp37-cp37m-win_amd64.whl", hash = "sha256:dca64867b2d79433eb8557db00e74e17a2f0d444a9a90fb6f49cadaeddf703a5"}, + {file = "geventhttpclient-2.0.12-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:707467d6e8ad19749e7130b7c7bcb3a446c8e4f66454e1d47f4dfffa974683da"}, + {file = "geventhttpclient-2.0.12-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c2e436a2c41c71db17fd46df4925eb5e4d3856eb1b5fda0ce6b1137a6c6c87fa"}, + {file = "geventhttpclient-2.0.12-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f88d2f3a20afa999548622b31dbc3db5aa355c3032f3ae96a4195c5f938fee92"}, + {file = "geventhttpclient-2.0.12-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31a6581b8de9fa4b44916dcfabdc608409cfcf02fac39a62d40f6bcf6af726ad"}, + {file = "geventhttpclient-2.0.12-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c91e0ee50f8a1ea3a268f06c5bd44efe86b7f57961d7c923602038fcc010c3c"}, + {file = "geventhttpclient-2.0.12-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e3031817b8f2411086765de4bb1080c755b009ee8dc4a6111ad74f6ff4a363f"}, + {file = "geventhttpclient-2.0.12-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ff9a95e2d2035c1f5ac726166a598ea4071412c304a74a8cd5d2d8dfbf40b5e"}, + {file = "geventhttpclient-2.0.12-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:04f41d8f14e241e8d0c828ff59634674e98f96f39f6d12f43009a7332c4e2c82"}, + {file = "geventhttpclient-2.0.12-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:bea7376205629e8964f624b08d6836892e8d17ed8b8a57d5d2edbd7983440652"}, + {file = "geventhttpclient-2.0.12-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:fd9baf30e2bdd3110394365998037a45b43f86804b8f3c77f194f64eddc7dc54"}, + {file = "geventhttpclient-2.0.12-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:23c27b04ad25258959c088c0d87832befc7be2b09c5c35fdd76e417f5b546da0"}, + {file = "geventhttpclient-2.0.12-cp38-cp38-win32.whl", hash = "sha256:792e154009f6f63e3fbbe4b3109780ada275c4ed29659430c06dc8e1b2ed03ef"}, + {file = "geventhttpclient-2.0.12-cp38-cp38-win_amd64.whl", hash = "sha256:7b41a0510297a8ebbeffbef082e0896ecf37d5302999a3b58d208193c3c3e362"}, + {file = "geventhttpclient-2.0.12-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5006e34586bba4ebd5a7a5482f9e7743e1b3b9ff50c994105fb45e43044c38c9"}, + {file = "geventhttpclient-2.0.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d812074192822f57603973d6bcced0f02c6cc371cf63e729793f193c874f30ce"}, + {file = "geventhttpclient-2.0.12-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2a64bd8bce446be4fe869b64af310cd218d2329aa4e9d85b6a060da93c62296b"}, + {file = "geventhttpclient-2.0.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7fc536f2972c75da85f9360d0a3e5433baf6d777442a013052f9a501311ddcd"}, + {file = "geventhttpclient-2.0.12-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a60dec2ac44f494af9e42889dd7f7d653545b4c4892da4acbe383c0ffc305a1"}, + {file = "geventhttpclient-2.0.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa2ef1b92662ee9866bda52123f6f266ff4479437e7b5037a6427cf09e071e25"}, + {file = "geventhttpclient-2.0.12-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b8215e9a018a3634bdef4891634ceb9b10f47292b0091a1d96c363d8d5d7fdd"}, + {file = "geventhttpclient-2.0.12-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:90d5c0974518d35514a8896529d113e778e9d42d10699ac6051cd3e8f1ff81f6"}, + {file = "geventhttpclient-2.0.12-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:83c28617b02b6ab53653324b2a9ff2d4a4b1f1582fbc4e70f47d2ef9fe6ab1f7"}, + {file = "geventhttpclient-2.0.12-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:d8c7dfa2bcd15988a350e90b32c6b5426924f2ffd0ce75f52ca2d5ef540b3fbc"}, + {file = "geventhttpclient-2.0.12-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ded99bdbe7953f0734720d9908ed6f808fd12e97de05508822695a87b69f10f2"}, + {file = "geventhttpclient-2.0.12-cp39-cp39-win32.whl", hash = "sha256:ebcd7311901e52929d2bd3af9442970fdd12b200285d9a55d52994e033e73050"}, + {file = "geventhttpclient-2.0.12-cp39-cp39-win_amd64.whl", hash = "sha256:204c3976b2a4fcefe8f157fe303da45b85fc31147bdfce7b53b1098f05f1cad2"}, + {file = "geventhttpclient-2.0.12-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c651d22fae3084502afc876e8c73d521496f512e16828939333f17cad64ea47f"}, + {file = "geventhttpclient-2.0.12-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c111addb5b27431805a8ad544dec292a7153cc44b68df28e782821431970d8"}, + {file = "geventhttpclient-2.0.12-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:14cb7f4854d77c63506e31677fb548d137b20cbe34a11b5442f065b1e46c2246"}, + {file = "geventhttpclient-2.0.12-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8ac257aa714999b523282c0da6faf4d333d44131cea3b15fe802e00d35dd5c2"}, + {file = "geventhttpclient-2.0.12-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d97a41f959cd331eb8a633ed8edf6cc002a2a41a21e94876db833729b803924f"}, + {file = "geventhttpclient-2.0.12-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6ecb9a600a2da862b079ef3ebdffc9acec089c914bebc0c54614049584bfbb94"}, + {file = "geventhttpclient-2.0.12-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:662bb04e99466c25a1bf8b47351f79b339b6627721bb357bf3bc0d263c394176"}, + {file = "geventhttpclient-2.0.12-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80a6b4c9e1ade3ae090b7b679d5b691d0c87460612983d4ab951043f859adffb"}, + {file = "geventhttpclient-2.0.12-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13a359605dab2b92df4ef1bab7f1bec26e82acdc4253828a508f55375af50b48"}, + {file = "geventhttpclient-2.0.12-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:fc17f57be8254329715702d00536a443c29b52f2ef750bc0650554fb3b7e33e7"}, + {file = "geventhttpclient-2.0.12-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b58096bcaaa259e8d107749539b1d3804fc6ec395e91dec8040d448d298861c8"}, + {file = "geventhttpclient-2.0.12-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9eb66bff9ed4d4f0bced3498746d86c949bf99e2440ceb968e6e7c542b3982b0"}, + {file = "geventhttpclient-2.0.12-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0158f45fd611b585c54380d981181c303313f3e059395310112805f53998d061"}, + {file = "geventhttpclient-2.0.12-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13129723ba3568f0a373cbd612130e2d78b3f284cf6a62385e26a92d7627a570"}, + {file = "geventhttpclient-2.0.12-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:571be0c875503ef5088cb417e84b707c922e3e2bd5e302e609d25e008cf037eb"}, + {file = "geventhttpclient-2.0.12-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:46e1706e3a44bb3423fc8d10b44e71c8a52c6535e22d483519dde008723c4f25"}, + {file = "geventhttpclient-2.0.12-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9de259de7ccc19b47537e21b47a74442ad64d1a1a262b971713d6af8cc8f16f9"}, + {file = "geventhttpclient-2.0.12-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4d777dced8a8e04fd8e0811c3b764d9a476b6a4c865f10079cc4f27b95b37196"}, + {file = "geventhttpclient-2.0.12-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcd4f45055a2e2f66e67016599d3fac33bc67b3bd67b672c1503a5de7543c1b6"}, + {file = "geventhttpclient-2.0.12-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:61b078cfc4b34a0d50224adf80c7eeae8e23fe6d8cb35926ccd3f3a6b86f921f"}, + {file = "geventhttpclient-2.0.12.tar.gz", hash = "sha256:ebea08e79c1aa7d03b43936b347c0f87356e6fb1c6845735a11f23c949c655f7"}, ] [package.dependencies] @@ -1909,13 +1881,13 @@ test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre [[package]] name = "googleapis-common-protos" -version = "1.62.0" +version = "1.63.0" description = "Common protobufs used in Google APIs" optional = true python-versions = ">=3.7" files = [ - {file = "googleapis-common-protos-1.62.0.tar.gz", hash = "sha256:83f0ece9f94e5672cced82f592d2a5edf527a96ed1794f0bab36d5735c996277"}, - {file = "googleapis_common_protos-1.62.0-py2.py3-none-any.whl", hash = "sha256:4750113612205514f9f6aa4cb00d523a94f3e8c06c5ad2fee466387dc4875f07"}, + {file = "googleapis-common-protos-1.63.0.tar.gz", hash = "sha256:17ad01b11d5f1d0171c06d3ba5c04c54474e883b66b949722b4938ee2694ef4e"}, + {file = "googleapis_common_protos-1.63.0-py2.py3-none-any.whl", hash = "sha256:ae45f75702f7c08b541f750854a678bd8f534a1a6bace6afe975f1d0a82d6632"}, ] [package.dependencies] @@ -1997,69 +1969,69 @@ test = ["objgraph", "psutil"] [[package]] name = "grpcio" -version = "1.62.0" +version = "1.62.1" description = "HTTP/2-based RPC framework" optional = true python-versions = ">=3.7" files = [ - {file = "grpcio-1.62.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:136ffd79791b1eddda8d827b607a6285474ff8a1a5735c4947b58c481e5e4271"}, - {file = "grpcio-1.62.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:d6a56ba703be6b6267bf19423d888600c3f574ac7c2cc5e6220af90662a4d6b0"}, - {file = "grpcio-1.62.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:4cd356211579043fce9f52acc861e519316fff93980a212c8109cca8f47366b6"}, - {file = "grpcio-1.62.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e803e9b58d8f9b4ff0ea991611a8d51b31c68d2e24572cd1fe85e99e8cc1b4f8"}, - {file = "grpcio-1.62.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4c04fe33039b35b97c02d2901a164bbbb2f21fb9c4e2a45a959f0b044c3512c"}, - {file = "grpcio-1.62.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:95370c71b8c9062f9ea033a0867c4c73d6f0ff35113ebd2618171ec1f1e903e0"}, - {file = "grpcio-1.62.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c912688acc05e4ff012c8891803659d6a8a8b5106f0f66e0aed3fb7e77898fa6"}, - {file = "grpcio-1.62.0-cp310-cp310-win32.whl", hash = "sha256:821a44bd63d0f04e33cf4ddf33c14cae176346486b0df08b41a6132b976de5fc"}, - {file = "grpcio-1.62.0-cp310-cp310-win_amd64.whl", hash = "sha256:81531632f93fece32b2762247c4c169021177e58e725494f9a746ca62c83acaa"}, - {file = "grpcio-1.62.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:3fa15850a6aba230eed06b236287c50d65a98f05054a0f01ccedf8e1cc89d57f"}, - {file = "grpcio-1.62.0-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:36df33080cd7897623feff57831eb83c98b84640b016ce443305977fac7566fb"}, - {file = "grpcio-1.62.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:7a195531828b46ea9c4623c47e1dc45650fc7206f8a71825898dd4c9004b0928"}, - {file = "grpcio-1.62.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ab140a3542bbcea37162bdfc12ce0d47a3cda3f2d91b752a124cc9fe6776a9e2"}, - {file = "grpcio-1.62.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f9d6c3223914abb51ac564dc9c3782d23ca445d2864321b9059d62d47144021"}, - {file = "grpcio-1.62.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:fbe0c20ce9a1cff75cfb828b21f08d0a1ca527b67f2443174af6626798a754a4"}, - {file = "grpcio-1.62.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38f69de9c28c1e7a8fd24e4af4264726637b72f27c2099eaea6e513e7142b47e"}, - {file = "grpcio-1.62.0-cp311-cp311-win32.whl", hash = "sha256:ce1aafdf8d3f58cb67664f42a617af0e34555fe955450d42c19e4a6ad41c84bd"}, - {file = "grpcio-1.62.0-cp311-cp311-win_amd64.whl", hash = "sha256:eef1d16ac26c5325e7d39f5452ea98d6988c700c427c52cbc7ce3201e6d93334"}, - {file = "grpcio-1.62.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:8aab8f90b2a41208c0a071ec39a6e5dbba16fd827455aaa070fec241624ccef8"}, - {file = "grpcio-1.62.0-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:62aa1659d8b6aad7329ede5d5b077e3d71bf488d85795db517118c390358d5f6"}, - {file = "grpcio-1.62.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:0d7ae7fc7dbbf2d78d6323641ded767d9ec6d121aaf931ec4a5c50797b886532"}, - {file = "grpcio-1.62.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f359d635ee9428f0294bea062bb60c478a8ddc44b0b6f8e1f42997e5dc12e2ee"}, - {file = "grpcio-1.62.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d48e5b1f8f4204889f1acf30bb57c30378e17c8d20df5acbe8029e985f735c"}, - {file = "grpcio-1.62.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:662d3df5314ecde3184cf87ddd2c3a66095b3acbb2d57a8cada571747af03873"}, - {file = "grpcio-1.62.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:92cdb616be44c8ac23a57cce0243af0137a10aa82234f23cd46e69e115071388"}, - {file = "grpcio-1.62.0-cp312-cp312-win32.whl", hash = "sha256:0b9179478b09ee22f4a36b40ca87ad43376acdccc816ce7c2193a9061bf35701"}, - {file = "grpcio-1.62.0-cp312-cp312-win_amd64.whl", hash = "sha256:614c3ed234208e76991992342bab725f379cc81c7dd5035ee1de2f7e3f7a9842"}, - {file = "grpcio-1.62.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:7e1f51e2a460b7394670fdb615e26d31d3260015154ea4f1501a45047abe06c9"}, - {file = "grpcio-1.62.0-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:bcff647e7fe25495e7719f779cc219bbb90b9e79fbd1ce5bda6aae2567f469f2"}, - {file = "grpcio-1.62.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:56ca7ba0b51ed0de1646f1735154143dcbdf9ec2dbe8cc6645def299bb527ca1"}, - {file = "grpcio-1.62.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e84bfb2a734e4a234b116be208d6f0214e68dcf7804306f97962f93c22a1839"}, - {file = "grpcio-1.62.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c1488b31a521fbba50ae86423f5306668d6f3a46d124f7819c603979fc538c4"}, - {file = "grpcio-1.62.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:98d8f4eb91f1ce0735bf0b67c3b2a4fea68b52b2fd13dc4318583181f9219b4b"}, - {file = "grpcio-1.62.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b3d3d755cfa331d6090e13aac276d4a3fb828bf935449dc16c3d554bf366136b"}, - {file = "grpcio-1.62.0-cp37-cp37m-win_amd64.whl", hash = "sha256:a33f2bfd8a58a02aab93f94f6c61279be0f48f99fcca20ebaee67576cd57307b"}, - {file = "grpcio-1.62.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:5e709f7c8028ce0443bddc290fb9c967c1e0e9159ef7a030e8c21cac1feabd35"}, - {file = "grpcio-1.62.0-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:2f3d9a4d0abb57e5f49ed5039d3ed375826c2635751ab89dcc25932ff683bbb6"}, - {file = "grpcio-1.62.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:62ccb92f594d3d9fcd00064b149a0187c246b11e46ff1b7935191f169227f04c"}, - {file = "grpcio-1.62.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:921148f57c2e4b076af59a815467d399b7447f6e0ee10ef6d2601eb1e9c7f402"}, - {file = "grpcio-1.62.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f897b16190b46bc4d4aaf0a32a4b819d559a37a756d7c6b571e9562c360eed72"}, - {file = "grpcio-1.62.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1bc8449084fe395575ed24809752e1dc4592bb70900a03ca42bf236ed5bf008f"}, - {file = "grpcio-1.62.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:81d444e5e182be4c7856cd33a610154fe9ea1726bd071d07e7ba13fafd202e38"}, - {file = "grpcio-1.62.0-cp38-cp38-win32.whl", hash = "sha256:88f41f33da3840b4a9bbec68079096d4caf629e2c6ed3a72112159d570d98ebe"}, - {file = "grpcio-1.62.0-cp38-cp38-win_amd64.whl", hash = "sha256:fc2836cb829895ee190813446dce63df67e6ed7b9bf76060262c55fcd097d270"}, - {file = "grpcio-1.62.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:fcc98cff4084467839d0a20d16abc2a76005f3d1b38062464d088c07f500d170"}, - {file = "grpcio-1.62.0-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:0d3dee701e48ee76b7d6fbbba18ba8bc142e5b231ef7d3d97065204702224e0e"}, - {file = "grpcio-1.62.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:b7a6be562dd18e5d5bec146ae9537f20ae1253beb971c0164f1e8a2f5a27e829"}, - {file = "grpcio-1.62.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:29cb592c4ce64a023712875368bcae13938c7f03e99f080407e20ffe0a9aa33b"}, - {file = "grpcio-1.62.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1eda79574aec8ec4d00768dcb07daba60ed08ef32583b62b90bbf274b3c279f7"}, - {file = "grpcio-1.62.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7eea57444a354ee217fda23f4b479a4cdfea35fb918ca0d8a0e73c271e52c09c"}, - {file = "grpcio-1.62.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0e97f37a3b7c89f9125b92d22e9c8323f4e76e7993ba7049b9f4ccbe8bae958a"}, - {file = "grpcio-1.62.0-cp39-cp39-win32.whl", hash = "sha256:39cd45bd82a2e510e591ca2ddbe22352e8413378852ae814549c162cf3992a93"}, - {file = "grpcio-1.62.0-cp39-cp39-win_amd64.whl", hash = "sha256:b71c65427bf0ec6a8b48c68c17356cb9fbfc96b1130d20a07cb462f4e4dcdcd5"}, - {file = "grpcio-1.62.0.tar.gz", hash = "sha256:748496af9238ac78dcd98cce65421f1adce28c3979393e3609683fcd7f3880d7"}, -] - -[package.extras] -protobuf = ["grpcio-tools (>=1.62.0)"] + {file = "grpcio-1.62.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:179bee6f5ed7b5f618844f760b6acf7e910988de77a4f75b95bbfaa8106f3c1e"}, + {file = "grpcio-1.62.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:48611e4fa010e823ba2de8fd3f77c1322dd60cb0d180dc6630a7e157b205f7ea"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:b2a0e71b0a2158aa4bce48be9f8f9eb45cbd17c78c7443616d00abbe2a509f6d"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbe80577c7880911d3ad65e5ecc997416c98f354efeba2f8d0f9112a67ed65a5"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58f6c693d446964e3292425e1d16e21a97a48ba9172f2d0df9d7b640acb99243"}, + {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:77c339403db5a20ef4fed02e4d1a9a3d9866bf9c0afc77a42234677313ea22f3"}, + {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b5a4ea906db7dec694098435d84bf2854fe158eb3cd51e1107e571246d4d1d70"}, + {file = "grpcio-1.62.1-cp310-cp310-win32.whl", hash = "sha256:4187201a53f8561c015bc745b81a1b2d278967b8de35f3399b84b0695e281d5f"}, + {file = "grpcio-1.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:844d1f3fb11bd1ed362d3fdc495d0770cfab75761836193af166fee113421d66"}, + {file = "grpcio-1.62.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:833379943d1728a005e44103f17ecd73d058d37d95783eb8f0b28ddc1f54d7b2"}, + {file = "grpcio-1.62.1-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:c7fcc6a32e7b7b58f5a7d27530669337a5d587d4066060bcb9dee7a8c833dfb7"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:fa7d28eb4d50b7cbe75bb8b45ed0da9a1dc5b219a0af59449676a29c2eed9698"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48f7135c3de2f298b833be8b4ae20cafe37091634e91f61f5a7eb3d61ec6f660"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71f11fd63365ade276c9d4a7b7df5c136f9030e3457107e1791b3737a9b9ed6a"}, + {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4b49fd8fe9f9ac23b78437da94c54aa7e9996fbb220bac024a67469ce5d0825f"}, + {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:482ae2ae78679ba9ed5752099b32e5fe580443b4f798e1b71df412abf43375db"}, + {file = "grpcio-1.62.1-cp311-cp311-win32.whl", hash = "sha256:1faa02530b6c7426404372515fe5ddf66e199c2ee613f88f025c6f3bd816450c"}, + {file = "grpcio-1.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bd90b8c395f39bc82a5fb32a0173e220e3f401ff697840f4003e15b96d1befc"}, + {file = "grpcio-1.62.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:b134d5d71b4e0837fff574c00e49176051a1c532d26c052a1e43231f252d813b"}, + {file = "grpcio-1.62.1-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:d1f6c96573dc09d50dbcbd91dbf71d5cf97640c9427c32584010fbbd4c0e0037"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:359f821d4578f80f41909b9ee9b76fb249a21035a061a327f91c953493782c31"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a485f0c2010c696be269184bdb5ae72781344cb4e60db976c59d84dd6354fac9"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b50b09b4dc01767163d67e1532f948264167cd27f49e9377e3556c3cba1268e1"}, + {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3227c667dccbe38f2c4d943238b887bac588d97c104815aecc62d2fd976e014b"}, + {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3952b581eb121324853ce2b191dae08badb75cd493cb4e0243368aa9e61cfd41"}, + {file = "grpcio-1.62.1-cp312-cp312-win32.whl", hash = "sha256:83a17b303425104d6329c10eb34bba186ffa67161e63fa6cdae7776ff76df73f"}, + {file = "grpcio-1.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:6696ffe440333a19d8d128e88d440f91fb92c75a80ce4b44d55800e656a3ef1d"}, + {file = "grpcio-1.62.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:e3393b0823f938253370ebef033c9fd23d27f3eae8eb9a8f6264900c7ea3fb5a"}, + {file = "grpcio-1.62.1-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:83e7ccb85a74beaeae2634f10eb858a0ed1a63081172649ff4261f929bacfd22"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:882020c87999d54667a284c7ddf065b359bd00251fcd70279ac486776dbf84ec"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a10383035e864f386fe096fed5c47d27a2bf7173c56a6e26cffaaa5a361addb1"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:960edebedc6b9ada1ef58e1c71156f28689978188cd8cff3b646b57288a927d9"}, + {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:23e2e04b83f347d0aadde0c9b616f4726c3d76db04b438fd3904b289a725267f"}, + {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:978121758711916d34fe57c1f75b79cdfc73952f1481bb9583399331682d36f7"}, + {file = "grpcio-1.62.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9084086190cc6d628f282e5615f987288b95457292e969b9205e45b442276407"}, + {file = "grpcio-1.62.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:22bccdd7b23c420a27fd28540fb5dcbc97dc6be105f7698cb0e7d7a420d0e362"}, + {file = "grpcio-1.62.1-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:8999bf1b57172dbc7c3e4bb3c732658e918f5c333b2942243f10d0d653953ba9"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:d9e52558b8b8c2f4ac05ac86344a7417ccdd2b460a59616de49eb6933b07a0bd"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1714e7bc935780bc3de1b3fcbc7674209adf5208ff825799d579ffd6cd0bd505"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8842ccbd8c0e253c1f189088228f9b433f7a93b7196b9e5b6f87dba393f5d5d"}, + {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1f1e7b36bdff50103af95a80923bf1853f6823dd62f2d2a2524b66ed74103e49"}, + {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bba97b8e8883a8038606480d6b6772289f4c907f6ba780fa1f7b7da7dfd76f06"}, + {file = "grpcio-1.62.1-cp38-cp38-win32.whl", hash = "sha256:a7f615270fe534548112a74e790cd9d4f5509d744dd718cd442bf016626c22e4"}, + {file = "grpcio-1.62.1-cp38-cp38-win_amd64.whl", hash = "sha256:e6c8c8693df718c5ecbc7babb12c69a4e3677fd11de8886f05ab22d4e6b1c43b"}, + {file = "grpcio-1.62.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:73db2dc1b201d20ab7083e7041946910bb991e7e9761a0394bbc3c2632326483"}, + {file = "grpcio-1.62.1-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:407b26b7f7bbd4f4751dbc9767a1f0716f9fe72d3d7e96bb3ccfc4aace07c8de"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:f8de7c8cef9261a2d0a62edf2ccea3d741a523c6b8a6477a340a1f2e417658de"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd5c8a1af40ec305d001c60236308a67e25419003e9bb3ebfab5695a8d0b369"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be0477cb31da67846a33b1a75c611f88bfbcd427fe17701b6317aefceee1b96f"}, + {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:60dcd824df166ba266ee0cfaf35a31406cd16ef602b49f5d4dfb21f014b0dedd"}, + {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:973c49086cabab773525f6077f95e5a993bfc03ba8fc32e32f2c279497780585"}, + {file = "grpcio-1.62.1-cp39-cp39-win32.whl", hash = "sha256:12859468e8918d3bd243d213cd6fd6ab07208195dc140763c00dfe901ce1e1b4"}, + {file = "grpcio-1.62.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7209117bbeebdfa5d898205cc55153a51285757902dd73c47de498ad4d11332"}, + {file = "grpcio-1.62.1.tar.gz", hash = "sha256:6c455e008fa86d9e9a9d85bb76da4277c0d7d9668a3bfa70dbe86e9f3c759947"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.62.1)"] [[package]] name = "h11" @@ -2174,13 +2146,13 @@ testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs [[package]] name = "importlib-resources" -version = "6.1.2" +version = "6.3.1" description = "Read resources from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_resources-6.1.2-py3-none-any.whl", hash = "sha256:9a0a862501dc38b68adebc82970140c9e4209fc99601782925178f8386339938"}, - {file = "importlib_resources-6.1.2.tar.gz", hash = "sha256:308abf8474e2dba5f867d279237cd4076482c3de7104a40b41426370e891549b"}, + {file = "importlib_resources-6.3.1-py3-none-any.whl", hash = "sha256:4811639ca7fa830abdb8e9ca0a104dc6ad13de691d9fe0d3173a71304f068159"}, + {file = "importlib_resources-6.3.1.tar.gz", hash = "sha256:29a3d16556e330c3c8fb8202118c5ff41241cc34cbfb25989bbad226d99b7995"}, ] [package.dependencies] @@ -2188,7 +2160,7 @@ zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] +testing = ["jaraco.collections", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] [[package]] name = "iniconfig" @@ -2415,18 +2387,15 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "json5" -version = "0.9.17" +version = "0.9.24" description = "A Python implementation of the JSON5 data format." optional = false python-versions = ">=3.8" files = [ - {file = "json5-0.9.17-py2.py3-none-any.whl", hash = "sha256:f8ec1ecf985951d70f780f6f877c4baca6a47b6e61e02c4cd190138d10a7805a"}, - {file = "json5-0.9.17.tar.gz", hash = "sha256:717d99d657fa71b7094877b1d921b1cce40ab444389f6d770302563bb7dfd9ae"}, + {file = "json5-0.9.24-py3-none-any.whl", hash = "sha256:4ca101fd5c7cb47960c055ef8f4d0e31e15a7c6c48c3b6f1473fc83b6c462a13"}, + {file = "json5-0.9.24.tar.gz", hash = "sha256:0c638399421da959a20952782800e5c1a78c14e08e1dc9738fa10d8ec14d58c8"}, ] -[package.extras] -dev = ["hypothesis"] - [[package]] name = "jsonpointer" version = "2.4" @@ -2506,13 +2475,13 @@ qtconsole = "*" [[package]] name = "jupyter-client" -version = "8.6.0" +version = "8.6.1" description = "Jupyter protocol implementation and client libraries" optional = false python-versions = ">=3.8" files = [ - {file = "jupyter_client-8.6.0-py3-none-any.whl", hash = "sha256:909c474dbe62582ae62b758bca86d6518c85234bdee2d908c778db6d72f39d99"}, - {file = "jupyter_client-8.6.0.tar.gz", hash = "sha256:0642244bb83b4764ae60d07e010e15f0e2d275ec4e918a8f7b80fbbef3ca60c7"}, + {file = "jupyter_client-8.6.1-py3-none-any.whl", hash = "sha256:3b7bd22f058434e3b9a7ea4b1500ed47de2713872288c0d511d19926f99b459f"}, + {file = "jupyter_client-8.6.1.tar.gz", hash = "sha256:e842515e2bab8e19186d89fdfea7abd15e39dd581f94e399f00e2af5a1652d3f"}, ] [package.dependencies] @@ -2553,13 +2522,13 @@ test = ["flaky", "pexpect", "pytest"] [[package]] name = "jupyter-core" -version = "5.7.1" +version = "5.7.2" description = "Jupyter core package. A base package on which Jupyter projects rely." optional = false python-versions = ">=3.8" files = [ - {file = "jupyter_core-5.7.1-py3-none-any.whl", hash = "sha256:c65c82126453a723a2804aa52409930434598fd9d35091d63dfb919d2b765bb7"}, - {file = "jupyter_core-5.7.1.tar.gz", hash = "sha256:de61a9d7fc71240f688b2fb5ab659fbb56979458dc66a71decd098e03c79e218"}, + {file = "jupyter_core-5.7.2-py3-none-any.whl", hash = "sha256:4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409"}, + {file = "jupyter_core-5.7.2.tar.gz", hash = "sha256:aa5f8d32bbf6b431ac830496da7392035d6f61b4f54872f15c4bd2a9c3f536d9"}, ] [package.dependencies] @@ -2569,17 +2538,17 @@ traitlets = ">=5.3" [package.extras] docs = ["myst-parser", "pydata-sphinx-theme", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "traitlets"] -test = ["ipykernel", "pre-commit", "pytest", "pytest-cov", "pytest-timeout"] +test = ["ipykernel", "pre-commit", "pytest (<8)", "pytest-cov", "pytest-timeout"] [[package]] name = "jupyter-events" -version = "0.9.0" +version = "0.10.0" description = "Jupyter Event System library" optional = false python-versions = ">=3.8" files = [ - {file = "jupyter_events-0.9.0-py3-none-any.whl", hash = "sha256:d853b3c10273ff9bc8bb8b30076d65e2c9685579db736873de6c2232dde148bf"}, - {file = "jupyter_events-0.9.0.tar.gz", hash = "sha256:81ad2e4bc710881ec274d31c6c50669d71bbaa5dd9d01e600b56faa85700d399"}, + {file = "jupyter_events-0.10.0-py3-none-any.whl", hash = "sha256:4b72130875e59d57716d327ea70d3ebc3af1944d3717e5a498b8a06c6c159960"}, + {file = "jupyter_events-0.10.0.tar.gz", hash = "sha256:670b8229d3cc882ec782144ed22e0d29e1c2d639263f92ca8383e66682845e22"}, ] [package.dependencies] @@ -2598,13 +2567,13 @@ test = ["click", "pre-commit", "pytest (>=7.0)", "pytest-asyncio (>=0.19.0)", "p [[package]] name = "jupyter-lsp" -version = "2.2.3" +version = "2.2.4" description = "Multi-Language Server WebSocket proxy for Jupyter Notebook/Lab server" optional = false python-versions = ">=3.8" files = [ - {file = "jupyter-lsp-2.2.3.tar.gz", hash = "sha256:33dbcbc5df24237ff5c8b696b04ff4689fcd316cb8d4957d620fe5504d7d2c3f"}, - {file = "jupyter_lsp-2.2.3-py3-none-any.whl", hash = "sha256:57dd90d0a2e2530831793550846168c81c952b49e187aa339e455027a5f0fd2e"}, + {file = "jupyter-lsp-2.2.4.tar.gz", hash = "sha256:5e50033149344065348e688608f3c6d654ef06d9856b67655bd7b6bac9ee2d59"}, + {file = "jupyter_lsp-2.2.4-py3-none-any.whl", hash = "sha256:da61cb63a16b6dff5eac55c2699cc36eac975645adee02c41bdfc03bf4802e77"}, ] [package.dependencies] @@ -2613,13 +2582,13 @@ jupyter-server = ">=1.1.2" [[package]] name = "jupyter-server" -version = "2.12.5" +version = "2.13.0" description = "The backend—i.e. core services, APIs, and REST endpoints—to Jupyter web applications." optional = false python-versions = ">=3.8" files = [ - {file = "jupyter_server-2.12.5-py3-none-any.whl", hash = "sha256:184a0f82809a8522777cfb6b760ab6f4b1bb398664c5860a27cec696cb884923"}, - {file = "jupyter_server-2.12.5.tar.gz", hash = "sha256:0edb626c94baa22809be1323f9770cf1c00a952b17097592e40d03e6a3951689"}, + {file = "jupyter_server-2.13.0-py3-none-any.whl", hash = "sha256:77b2b49c3831fbbfbdb5048cef4350d12946191f833a24e5f83e5f8f4803e97b"}, + {file = "jupyter_server-2.13.0.tar.gz", hash = "sha256:c80bfb049ea20053c3d9641c2add4848b38073bf79f1729cea1faed32fc1c78e"}, ] [package.dependencies] @@ -2645,17 +2614,17 @@ websocket-client = "*" [package.extras] docs = ["ipykernel", "jinja2", "jupyter-client", "jupyter-server", "myst-parser", "nbformat", "prometheus-client", "pydata-sphinx-theme", "send2trash", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-openapi (>=0.8.0)", "sphinxcontrib-spelling", "sphinxemoji", "tornado", "typing-extensions"] -test = ["flaky", "ipykernel", "pre-commit", "pytest (>=7.0)", "pytest-console-scripts", "pytest-jupyter[server] (>=0.4)", "pytest-timeout", "requests"] +test = ["flaky", "ipykernel", "pre-commit", "pytest (>=7.0)", "pytest-console-scripts", "pytest-jupyter[server] (>=0.7)", "pytest-timeout", "requests"] [[package]] name = "jupyter-server-terminals" -version = "0.5.2" +version = "0.5.3" description = "A Jupyter Server Extension Providing Terminals." optional = false python-versions = ">=3.8" files = [ - {file = "jupyter_server_terminals-0.5.2-py3-none-any.whl", hash = "sha256:1b80c12765da979513c42c90215481bbc39bd8ae7c0350b4f85bc3eb58d0fa80"}, - {file = "jupyter_server_terminals-0.5.2.tar.gz", hash = "sha256:396b5ccc0881e550bf0ee7012c6ef1b53edbde69e67cab1d56e89711b46052e8"}, + {file = "jupyter_server_terminals-0.5.3-py3-none-any.whl", hash = "sha256:41ee0d7dc0ebf2809c668e0fc726dfaf258fcd3e769568996ca731b6194ae9aa"}, + {file = "jupyter_server_terminals-0.5.3.tar.gz", hash = "sha256:5ae0295167220e9ace0edcfdb212afd2b01ee8d179fe6f23c899590e9b8a5269"}, ] [package.dependencies] @@ -2668,13 +2637,13 @@ test = ["jupyter-server (>=2.0.0)", "pytest (>=7.0)", "pytest-jupyter[server] (> [[package]] name = "jupyterlab" -version = "4.1.2" +version = "4.1.5" description = "JupyterLab computational environment" optional = false python-versions = ">=3.8" files = [ - {file = "jupyterlab-4.1.2-py3-none-any.whl", hash = "sha256:aa88193f03cf4d3555f6712f04d74112b5eb85edd7d222c588c7603a26d33c5b"}, - {file = "jupyterlab-4.1.2.tar.gz", hash = "sha256:5d6348b3ed4085181499f621b7dfb6eb0b1f57f3586857aadfc8e3bf4c4885f9"}, + {file = "jupyterlab-4.1.5-py3-none-any.whl", hash = "sha256:3bc843382a25e1ab7bc31d9e39295a9f0463626692b7995597709c0ab236ab2c"}, + {file = "jupyterlab-4.1.5.tar.gz", hash = "sha256:c9ad75290cb10bfaff3624bf3fbb852319b4cce4c456613f8ebbaa98d03524db"}, ] [package.dependencies] @@ -2713,13 +2682,13 @@ files = [ [[package]] name = "jupyterlab-server" -version = "2.25.3" +version = "2.25.4" description = "A set of server components for JupyterLab and JupyterLab like applications." optional = false python-versions = ">=3.8" files = [ - {file = "jupyterlab_server-2.25.3-py3-none-any.whl", hash = "sha256:c48862519fded9b418c71645d85a49b2f0ec50d032ba8316738e9276046088c1"}, - {file = "jupyterlab_server-2.25.3.tar.gz", hash = "sha256:846f125a8a19656611df5b03e5912c8393cea6900859baa64fa515eb64a8dc40"}, + {file = "jupyterlab_server-2.25.4-py3-none-any.whl", hash = "sha256:eb645ecc8f9b24bac5decc7803b6d5363250e16ec5af814e516bc2c54dd88081"}, + {file = "jupyterlab_server-2.25.4.tar.gz", hash = "sha256:2098198e1e82e0db982440f9b5136175d73bea2cd42a6480aa6fd502cb23c4f9"}, ] [package.dependencies] @@ -2735,7 +2704,7 @@ requests = ">=2.31" [package.extras] docs = ["autodoc-traits", "jinja2 (<3.2.0)", "mistune (<4)", "myst-parser", "pydata-sphinx-theme", "sphinx", "sphinx-copybutton", "sphinxcontrib-openapi (>0.8)"] openapi = ["openapi-core (>=0.18.0,<0.19.0)", "ruamel-yaml"] -test = ["hatch", "ipykernel", "openapi-core (>=0.18.0,<0.19.0)", "openapi-spec-validator (>=0.6.0,<0.8.0)", "pytest (>=7.0)", "pytest-console-scripts", "pytest-cov", "pytest-jupyter[server] (>=0.6.2)", "pytest-timeout", "requests-mock", "ruamel-yaml", "sphinxcontrib-spelling", "strict-rfc3339", "werkzeug"] +test = ["hatch", "ipykernel", "openapi-core (>=0.18.0,<0.19.0)", "openapi-spec-validator (>=0.6.0,<0.8.0)", "pytest (>=7.0,<8)", "pytest-console-scripts", "pytest-cov", "pytest-jupyter[server] (>=0.6.2)", "pytest-timeout", "requests-mock", "ruamel-yaml", "sphinxcontrib-spelling", "strict-rfc3339", "werkzeug"] [[package]] name = "jupyterlab-widgets" @@ -2803,13 +2772,13 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-ena [[package]] name = "locust" -version = "2.23.1" +version = "2.24.0" description = "Developer friendly load testing framework" optional = false python-versions = ">=3.8" files = [ - {file = "locust-2.23.1-py3-none-any.whl", hash = "sha256:96013a460a4b4d6d4fd46c70e6ff1fd2b6e03b48ddb1b48d1513d3134ba2cecf"}, - {file = "locust-2.23.1.tar.gz", hash = "sha256:6cc729729e5ebf5852fc9d845302cfcf0ab0132f198e68b3eb0c88b438b6a863"}, + {file = "locust-2.24.0-py3-none-any.whl", hash = "sha256:1b6b878b4fd0108fec956120815e69775d2616c8f4d1e9f365c222a7a5c17d9a"}, + {file = "locust-2.24.0.tar.gz", hash = "sha256:6cffa378d995244a7472af6be1d6139331f19aee44e907deee73e0281252804d"}, ] [package.dependencies] @@ -2825,6 +2794,7 @@ pywin32 = {version = "*", markers = "platform_system == \"Windows\""} pyzmq = ">=25.0.0" requests = ">=2.26.0" roundrobin = ">=0.0.2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} Werkzeug = ">=2.0.0" [[package]] @@ -3039,67 +3009,67 @@ zstd = ["pymongo[zstd] (>=4.5,<5)"] [[package]] name = "msgpack" -version = "1.0.7" +version = "1.0.8" description = "MessagePack serializer" optional = false python-versions = ">=3.8" files = [ - {file = "msgpack-1.0.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:04ad6069c86e531682f9e1e71b71c1c3937d6014a7c3e9edd2aa81ad58842862"}, - {file = "msgpack-1.0.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cca1b62fe70d761a282496b96a5e51c44c213e410a964bdffe0928e611368329"}, - {file = "msgpack-1.0.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e50ebce52f41370707f1e21a59514e3375e3edd6e1832f5e5235237db933c98b"}, - {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b4f35de6a304b5533c238bee86b670b75b03d31b7797929caa7a624b5dda6"}, - {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28efb066cde83c479dfe5a48141a53bc7e5f13f785b92ddde336c716663039ee"}, - {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cb14ce54d9b857be9591ac364cb08dc2d6a5c4318c1182cb1d02274029d590d"}, - {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b573a43ef7c368ba4ea06050a957c2a7550f729c31f11dd616d2ac4aba99888d"}, - {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ccf9a39706b604d884d2cb1e27fe973bc55f2890c52f38df742bc1d79ab9f5e1"}, - {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cb70766519500281815dfd7a87d3a178acf7ce95390544b8c90587d76b227681"}, - {file = "msgpack-1.0.7-cp310-cp310-win32.whl", hash = "sha256:b610ff0f24e9f11c9ae653c67ff8cc03c075131401b3e5ef4b82570d1728f8a9"}, - {file = "msgpack-1.0.7-cp310-cp310-win_amd64.whl", hash = "sha256:a40821a89dc373d6427e2b44b572efc36a2778d3f543299e2f24eb1a5de65415"}, - {file = "msgpack-1.0.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:576eb384292b139821c41995523654ad82d1916da6a60cff129c715a6223ea84"}, - {file = "msgpack-1.0.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:730076207cb816138cf1af7f7237b208340a2c5e749707457d70705715c93b93"}, - {file = "msgpack-1.0.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:85765fdf4b27eb5086f05ac0491090fc76f4f2b28e09d9350c31aac25a5aaff8"}, - {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3476fae43db72bd11f29a5147ae2f3cb22e2f1a91d575ef130d2bf49afd21c46"}, - {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d4c80667de2e36970ebf74f42d1088cc9ee7ef5f4e8c35eee1b40eafd33ca5b"}, - {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b0bf0effb196ed76b7ad883848143427a73c355ae8e569fa538365064188b8e"}, - {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f9a7c509542db4eceed3dcf21ee5267ab565a83555c9b88a8109dcecc4709002"}, - {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:84b0daf226913133f899ea9b30618722d45feffa67e4fe867b0b5ae83a34060c"}, - {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ec79ff6159dffcc30853b2ad612ed572af86c92b5168aa3fc01a67b0fa40665e"}, - {file = "msgpack-1.0.7-cp311-cp311-win32.whl", hash = "sha256:3e7bf4442b310ff154b7bb9d81eb2c016b7d597e364f97d72b1acc3817a0fdc1"}, - {file = "msgpack-1.0.7-cp311-cp311-win_amd64.whl", hash = "sha256:3f0c8c6dfa6605ab8ff0611995ee30d4f9fcff89966cf562733b4008a3d60d82"}, - {file = "msgpack-1.0.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f0936e08e0003f66bfd97e74ee530427707297b0d0361247e9b4f59ab78ddc8b"}, - {file = "msgpack-1.0.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98bbd754a422a0b123c66a4c341de0474cad4a5c10c164ceed6ea090f3563db4"}, - {file = "msgpack-1.0.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b291f0ee7961a597cbbcc77709374087fa2a9afe7bdb6a40dbbd9b127e79afee"}, - {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebbbba226f0a108a7366bf4b59bf0f30a12fd5e75100c630267d94d7f0ad20e5"}, - {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e2d69948e4132813b8d1131f29f9101bc2c915f26089a6d632001a5c1349672"}, - {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdf38ba2d393c7911ae989c3bbba510ebbcdf4ecbdbfec36272abe350c454075"}, - {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:993584fc821c58d5993521bfdcd31a4adf025c7d745bbd4d12ccfecf695af5ba"}, - {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:52700dc63a4676669b341ba33520f4d6e43d3ca58d422e22ba66d1736b0a6e4c"}, - {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e45ae4927759289c30ccba8d9fdce62bb414977ba158286b5ddaf8df2cddb5c5"}, - {file = "msgpack-1.0.7-cp312-cp312-win32.whl", hash = "sha256:27dcd6f46a21c18fa5e5deed92a43d4554e3df8d8ca5a47bf0615d6a5f39dbc9"}, - {file = "msgpack-1.0.7-cp312-cp312-win_amd64.whl", hash = "sha256:7687e22a31e976a0e7fc99c2f4d11ca45eff652a81eb8c8085e9609298916dcf"}, - {file = "msgpack-1.0.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5b6ccc0c85916998d788b295765ea0e9cb9aac7e4a8ed71d12e7d8ac31c23c95"}, - {file = "msgpack-1.0.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:235a31ec7db685f5c82233bddf9858748b89b8119bf4538d514536c485c15fe0"}, - {file = "msgpack-1.0.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cab3db8bab4b7e635c1c97270d7a4b2a90c070b33cbc00c99ef3f9be03d3e1f7"}, - {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bfdd914e55e0d2c9e1526de210f6fe8ffe9705f2b1dfcc4aecc92a4cb4b533d"}, - {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36e17c4592231a7dbd2ed09027823ab295d2791b3b1efb2aee874b10548b7524"}, - {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38949d30b11ae5f95c3c91917ee7a6b239f5ec276f271f28638dec9156f82cfc"}, - {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ff1d0899f104f3921d94579a5638847f783c9b04f2d5f229392ca77fba5b82fc"}, - {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:dc43f1ec66eb8440567186ae2f8c447d91e0372d793dfe8c222aec857b81a8cf"}, - {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dd632777ff3beaaf629f1ab4396caf7ba0bdd075d948a69460d13d44357aca4c"}, - {file = "msgpack-1.0.7-cp38-cp38-win32.whl", hash = "sha256:4e71bc4416de195d6e9b4ee93ad3f2f6b2ce11d042b4d7a7ee00bbe0358bd0c2"}, - {file = "msgpack-1.0.7-cp38-cp38-win_amd64.whl", hash = "sha256:8f5b234f567cf76ee489502ceb7165c2a5cecec081db2b37e35332b537f8157c"}, - {file = "msgpack-1.0.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfef2bb6ef068827bbd021017a107194956918ab43ce4d6dc945ffa13efbc25f"}, - {file = "msgpack-1.0.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:484ae3240666ad34cfa31eea7b8c6cd2f1fdaae21d73ce2974211df099a95d81"}, - {file = "msgpack-1.0.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3967e4ad1aa9da62fd53e346ed17d7b2e922cba5ab93bdd46febcac39be636fc"}, - {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dd178c4c80706546702c59529ffc005681bd6dc2ea234c450661b205445a34d"}, - {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6ffbc252eb0d229aeb2f9ad051200668fc3a9aaa8994e49f0cb2ffe2b7867e7"}, - {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:822ea70dc4018c7e6223f13affd1c5c30c0f5c12ac1f96cd8e9949acddb48a61"}, - {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:384d779f0d6f1b110eae74cb0659d9aa6ff35aaf547b3955abf2ab4c901c4819"}, - {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f64e376cd20d3f030190e8c32e1c64582eba56ac6dc7d5b0b49a9d44021b52fd"}, - {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5ed82f5a7af3697b1c4786053736f24a0efd0a1b8a130d4c7bfee4b9ded0f08f"}, - {file = "msgpack-1.0.7-cp39-cp39-win32.whl", hash = "sha256:f26a07a6e877c76a88e3cecac8531908d980d3d5067ff69213653649ec0f60ad"}, - {file = "msgpack-1.0.7-cp39-cp39-win_amd64.whl", hash = "sha256:1dc93e8e4653bdb5910aed79f11e165c85732067614f180f70534f056da97db3"}, - {file = "msgpack-1.0.7.tar.gz", hash = "sha256:572efc93db7a4d27e404501975ca6d2d9775705c2d922390d878fcf768d92c87"}, + {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:505fe3d03856ac7d215dbe005414bc28505d26f0c128906037e66d98c4e95868"}, + {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b7842518a63a9f17107eb176320960ec095a8ee3b4420b5f688e24bf50c53c"}, + {file = "msgpack-1.0.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:376081f471a2ef24828b83a641a02c575d6103a3ad7fd7dade5486cad10ea659"}, + {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e390971d082dba073c05dbd56322427d3280b7cc8b53484c9377adfbae67dc2"}, + {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e073efcba9ea99db5acef3959efa45b52bc67b61b00823d2a1a6944bf45982"}, + {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82d92c773fbc6942a7a8b520d22c11cfc8fd83bba86116bfcf962c2f5c2ecdaa"}, + {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9ee32dcb8e531adae1f1ca568822e9b3a738369b3b686d1477cbc643c4a9c128"}, + {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e3aa7e51d738e0ec0afbed661261513b38b3014754c9459508399baf14ae0c9d"}, + {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69284049d07fce531c17404fcba2bb1df472bc2dcdac642ae71a2d079d950653"}, + {file = "msgpack-1.0.8-cp310-cp310-win32.whl", hash = "sha256:13577ec9e247f8741c84d06b9ece5f654920d8365a4b636ce0e44f15e07ec693"}, + {file = "msgpack-1.0.8-cp310-cp310-win_amd64.whl", hash = "sha256:e532dbd6ddfe13946de050d7474e3f5fb6ec774fbb1a188aaf469b08cf04189a"}, + {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9517004e21664f2b5a5fd6333b0731b9cf0817403a941b393d89a2f1dc2bd836"}, + {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d16a786905034e7e34098634b184a7d81f91d4c3d246edc6bd7aefb2fd8ea6ad"}, + {file = "msgpack-1.0.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2872993e209f7ed04d963e4b4fbae72d034844ec66bc4ca403329db2074377b"}, + {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c330eace3dd100bdb54b5653b966de7f51c26ec4a7d4e87132d9b4f738220ba"}, + {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b5c044f3eff2a6534768ccfd50425939e7a8b5cf9a7261c385de1e20dcfc85"}, + {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1876b0b653a808fcd50123b953af170c535027bf1d053b59790eebb0aeb38950"}, + {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dfe1f0f0ed5785c187144c46a292b8c34c1295c01da12e10ccddfc16def4448a"}, + {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3528807cbbb7f315bb81959d5961855e7ba52aa60a3097151cb21956fbc7502b"}, + {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e2f879ab92ce502a1e65fce390eab619774dda6a6ff719718069ac94084098ce"}, + {file = "msgpack-1.0.8-cp311-cp311-win32.whl", hash = "sha256:26ee97a8261e6e35885c2ecd2fd4a6d38252246f94a2aec23665a4e66d066305"}, + {file = "msgpack-1.0.8-cp311-cp311-win_amd64.whl", hash = "sha256:eadb9f826c138e6cf3c49d6f8de88225a3c0ab181a9b4ba792e006e5292d150e"}, + {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:114be227f5213ef8b215c22dde19532f5da9652e56e8ce969bf0a26d7c419fee"}, + {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d661dc4785affa9d0edfdd1e59ec056a58b3dbb9f196fa43587f3ddac654ac7b"}, + {file = "msgpack-1.0.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d56fd9f1f1cdc8227d7b7918f55091349741904d9520c65f0139a9755952c9e8"}, + {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0726c282d188e204281ebd8de31724b7d749adebc086873a59efb8cf7ae27df3"}, + {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8db8e423192303ed77cff4dce3a4b88dbfaf43979d280181558af5e2c3c71afc"}, + {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99881222f4a8c2f641f25703963a5cefb076adffd959e0558dc9f803a52d6a58"}, + {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b5505774ea2a73a86ea176e8a9a4a7c8bf5d521050f0f6f8426afe798689243f"}, + {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ef254a06bcea461e65ff0373d8a0dd1ed3aa004af48839f002a0c994a6f72d04"}, + {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e1dd7839443592d00e96db831eddb4111a2a81a46b028f0facd60a09ebbdd543"}, + {file = "msgpack-1.0.8-cp312-cp312-win32.whl", hash = "sha256:64d0fcd436c5683fdd7c907eeae5e2cbb5eb872fafbc03a43609d7941840995c"}, + {file = "msgpack-1.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:74398a4cf19de42e1498368c36eed45d9528f5fd0155241e82c4082b7e16cffd"}, + {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ceea77719d45c839fd73abcb190b8390412a890df2f83fb8cf49b2a4b5c2f40"}, + {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ab0bbcd4d1f7b6991ee7c753655b481c50084294218de69365f8f1970d4c151"}, + {file = "msgpack-1.0.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1cce488457370ffd1f953846f82323cb6b2ad2190987cd4d70b2713e17268d24"}, + {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3923a1778f7e5ef31865893fdca12a8d7dc03a44b33e2a5f3295416314c09f5d"}, + {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a22e47578b30a3e199ab067a4d43d790249b3c0587d9a771921f86250c8435db"}, + {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd739c9251d01e0279ce729e37b39d49a08c0420d3fee7f2a4968c0576678f77"}, + {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d3420522057ebab1728b21ad473aa950026d07cb09da41103f8e597dfbfaeb13"}, + {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5845fdf5e5d5b78a49b826fcdc0eb2e2aa7191980e3d2cfd2a30303a74f212e2"}, + {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a0e76621f6e1f908ae52860bdcb58e1ca85231a9b0545e64509c931dd34275a"}, + {file = "msgpack-1.0.8-cp38-cp38-win32.whl", hash = "sha256:374a8e88ddab84b9ada695d255679fb99c53513c0a51778796fcf0944d6c789c"}, + {file = "msgpack-1.0.8-cp38-cp38-win_amd64.whl", hash = "sha256:f3709997b228685fe53e8c433e2df9f0cdb5f4542bd5114ed17ac3c0129b0480"}, + {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f51bab98d52739c50c56658cc303f190785f9a2cd97b823357e7aeae54c8f68a"}, + {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:73ee792784d48aa338bba28063e19a27e8d989344f34aad14ea6e1b9bd83f596"}, + {file = "msgpack-1.0.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f9904e24646570539a8950400602d66d2b2c492b9010ea7e965025cb71d0c86d"}, + {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e75753aeda0ddc4c28dce4c32ba2f6ec30b1b02f6c0b14e547841ba5b24f753f"}, + {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dbf059fb4b7c240c873c1245ee112505be27497e90f7c6591261c7d3c3a8228"}, + {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4916727e31c28be8beaf11cf117d6f6f188dcc36daae4e851fee88646f5b6b18"}, + {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7938111ed1358f536daf311be244f34df7bf3cdedb3ed883787aca97778b28d8"}, + {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:493c5c5e44b06d6c9268ce21b302c9ca055c1fd3484c25ba41d34476c76ee746"}, + {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"}, + {file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"}, + {file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"}, + {file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"}, ] [[package]] @@ -3203,38 +3173,38 @@ files = [ [[package]] name = "mypy" -version = "1.8.0" +version = "1.9.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3"}, - {file = "mypy-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4"}, - {file = "mypy-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d"}, - {file = "mypy-1.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9"}, - {file = "mypy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410"}, - {file = "mypy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae"}, - {file = "mypy-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3"}, - {file = "mypy-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817"}, - {file = "mypy-1.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d"}, - {file = "mypy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835"}, - {file = "mypy-1.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd"}, - {file = "mypy-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55"}, - {file = "mypy-1.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218"}, - {file = "mypy-1.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3"}, - {file = "mypy-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e"}, - {file = "mypy-1.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6"}, - {file = "mypy-1.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66"}, - {file = "mypy-1.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6"}, - {file = "mypy-1.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d"}, - {file = "mypy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02"}, - {file = "mypy-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8"}, - {file = "mypy-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259"}, - {file = "mypy-1.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b"}, - {file = "mypy-1.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592"}, - {file = "mypy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a"}, - {file = "mypy-1.8.0-py3-none-any.whl", hash = "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d"}, - {file = "mypy-1.8.0.tar.gz", hash = "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07"}, + {file = "mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f"}, + {file = "mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed"}, + {file = "mypy-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150"}, + {file = "mypy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374"}, + {file = "mypy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03"}, + {file = "mypy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3"}, + {file = "mypy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc"}, + {file = "mypy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129"}, + {file = "mypy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612"}, + {file = "mypy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3"}, + {file = "mypy-1.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd"}, + {file = "mypy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6"}, + {file = "mypy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185"}, + {file = "mypy-1.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913"}, + {file = "mypy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"}, + {file = "mypy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b"}, + {file = "mypy-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2"}, + {file = "mypy-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e"}, + {file = "mypy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04"}, + {file = "mypy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89"}, + {file = "mypy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02"}, + {file = "mypy-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4"}, + {file = "mypy-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d"}, + {file = "mypy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf"}, + {file = "mypy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9"}, + {file = "mypy-1.9.0-py3-none-any.whl", hash = "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e"}, + {file = "mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974"}, ] [package.dependencies] @@ -3261,13 +3231,13 @@ files = [ [[package]] name = "nbclient" -version = "0.9.0" +version = "0.10.0" description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." optional = false python-versions = ">=3.8.0" files = [ - {file = "nbclient-0.9.0-py3-none-any.whl", hash = "sha256:a3a1ddfb34d4a9d17fc744d655962714a866639acd30130e9be84191cd97cd15"}, - {file = "nbclient-0.9.0.tar.gz", hash = "sha256:4b28c207877cf33ef3a9838cdc7a54c5ceff981194a82eac59d558f05487295e"}, + {file = "nbclient-0.10.0-py3-none-any.whl", hash = "sha256:f13e3529332a1f1f81d82a53210322476a168bb7090a0289c795fe9cc11c9d3f"}, + {file = "nbclient-0.10.0.tar.gz", hash = "sha256:4b3f1b7dba531e498449c4db4f53da339c91d449dc11e9af3a43b4eb5c5abb09"}, ] [package.dependencies] @@ -3279,17 +3249,17 @@ traitlets = ">=5.4" [package.extras] dev = ["pre-commit"] docs = ["autodoc-traits", "mock", "moto", "myst-parser", "nbclient[test]", "sphinx (>=1.7)", "sphinx-book-theme", "sphinxcontrib-spelling"] -test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>=7.0.0)", "pytest (>=7.0)", "pytest-asyncio", "pytest-cov (>=4.0)", "testpath", "xmltodict"] +test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>=7.0.0)", "pytest (>=7.0,<8)", "pytest-asyncio", "pytest-cov (>=4.0)", "testpath", "xmltodict"] [[package]] name = "nbconvert" -version = "7.16.1" +version = "7.16.2" description = "Converting Jupyter Notebooks (.ipynb files) to other formats. Output formats include asciidoc, html, latex, markdown, pdf, py, rst, script. nbconvert can be used both as a Python library (`import nbconvert`) or as a command line tool (invoked as `jupyter nbconvert ...`)." optional = false python-versions = ">=3.8" files = [ - {file = "nbconvert-7.16.1-py3-none-any.whl", hash = "sha256:3188727dffadfdc9c6a1c7250729063d7bc78b355ad7aa023138afa030d1cd07"}, - {file = "nbconvert-7.16.1.tar.gz", hash = "sha256:e79e6a074f49ba3ed29428ed86487bf51509d9aab613bd8522ac08f6d28fd7fd"}, + {file = "nbconvert-7.16.2-py3-none-any.whl", hash = "sha256:0c01c23981a8de0220255706822c40b751438e32467d6a686e26be08ba784382"}, + {file = "nbconvert-7.16.2.tar.gz", hash = "sha256:8310edd41e1c43947e4ecf16614c61469ebc024898eb808cce0999860fc9fb16"}, ] [package.dependencies] @@ -3321,13 +3291,13 @@ webpdf = ["playwright"] [[package]] name = "nbformat" -version = "5.9.2" +version = "5.10.3" description = "The Jupyter Notebook format" optional = false python-versions = ">=3.8" files = [ - {file = "nbformat-5.9.2-py3-none-any.whl", hash = "sha256:1c5172d786a41b82bcfd0c23f9e6b6f072e8fb49c39250219e4acfff1efe89e9"}, - {file = "nbformat-5.9.2.tar.gz", hash = "sha256:5f98b5ba1997dff175e77e0c17d5c10a96eaed2cbd1de3533d1fc35d5e111192"}, + {file = "nbformat-5.10.3-py3-none-any.whl", hash = "sha256:d9476ca28676799af85385f409b49d95e199951477a159a576ef2a675151e5e8"}, + {file = "nbformat-5.10.3.tar.gz", hash = "sha256:60ed5e910ef7c6264b87d644f276b1b49e24011930deef54605188ddeb211685"}, ] [package.dependencies] @@ -3372,13 +3342,13 @@ files = [ [[package]] name = "notebook" -version = "7.1.1" +version = "7.1.2" description = "Jupyter Notebook - A web-based notebook environment for interactive computing" optional = false python-versions = ">=3.8" files = [ - {file = "notebook-7.1.1-py3-none-any.whl", hash = "sha256:197d8e0595acabf4005851c8716e952a81b405f7aefb648067a761fbde267ce7"}, - {file = "notebook-7.1.1.tar.gz", hash = "sha256:818e7420fa21f402e726afb9f02df7f3c10f294c02e383ed19852866c316108b"}, + {file = "notebook-7.1.2-py3-none-any.whl", hash = "sha256:fc6c24b9aef18d0cd57157c9c47e95833b9b0bdc599652639acf0bdb61dc7d5f"}, + {file = "notebook-7.1.2.tar.gz", hash = "sha256:efc2c80043909e0faa17fce9e9b37c059c03af0ec99a4d4db84cb21d9d2e936a"}, ] [package.dependencies] @@ -3902,17 +3872,17 @@ xmp = ["defusedxml"] [[package]] name = "pkginfo" -version = "1.9.6" +version = "1.10.0" description = "Query metadata from sdists / bdists / installed packages." optional = false python-versions = ">=3.6" files = [ - {file = "pkginfo-1.9.6-py3-none-any.whl", hash = "sha256:4b7a555a6d5a22169fcc9cf7bfd78d296b0361adad412a346c1226849af5e546"}, - {file = "pkginfo-1.9.6.tar.gz", hash = "sha256:8fd5896e8718a4372f0ea9cc9d96f6417c9b986e23a4d116dda26b62cc29d046"}, + {file = "pkginfo-1.10.0-py3-none-any.whl", hash = "sha256:889a6da2ed7ffc58ab5b900d888ddce90bce912f2d2de1dc1c26f4cb9fe65097"}, + {file = "pkginfo-1.10.0.tar.gz", hash = "sha256:5df73835398d10db79f8eecd5cd86b1f6d29317589ea70796994d49399af6297"}, ] [package.extras] -testing = ["pytest", "pytest-cov"] +testing = ["pytest", "pytest-cov", "wheel"] [[package]] name = "pkgutil-resolve-name" @@ -3976,13 +3946,13 @@ poetry-plugin = ["poetry (>=1.0,<2.0)"] [[package]] name = "poetry" -version = "1.8.1" +version = "1.8.2" description = "Python dependency management and packaging made easy." optional = false python-versions = ">=3.8,<4.0" files = [ - {file = "poetry-1.8.1-py3-none-any.whl", hash = "sha256:cf133946661025822672422769567980f8e489ed5b6fc170d1025a4d6c9aac29"}, - {file = "poetry-1.8.1.tar.gz", hash = "sha256:23519cc45eb3cf48e899145bc762425a141e3afd52ecc53ec443ca635122327f"}, + {file = "poetry-1.8.2-py3-none-any.whl", hash = "sha256:b42b400d9a803af6e788a30a6f3e9998020b77860e28df20647eb10b6f414910"}, + {file = "poetry-1.8.2.tar.gz", hash = "sha256:49cceb3838104647c3e1021f3a4f13c6053704cc18d33f849a90fe687a29cb73"}, ] [package.dependencies] @@ -4024,17 +3994,17 @@ files = [ [[package]] name = "poetry-plugin-export" -version = "1.6.0" +version = "1.7.0" description = "Poetry plugin to export the dependencies to various formats" optional = false python-versions = ">=3.8,<4.0" files = [ - {file = "poetry_plugin_export-1.6.0-py3-none-any.whl", hash = "sha256:2dce6204c9318f1f6509a11a03921fb3f461b201840b59f1c237b6ab454dabcf"}, - {file = "poetry_plugin_export-1.6.0.tar.gz", hash = "sha256:091939434984267a91abf2f916a26b00cff4eee8da63ec2a24ba4b17cf969a59"}, + {file = "poetry_plugin_export-1.7.0-py3-none-any.whl", hash = "sha256:2283b28e0209f9f9598c6fe44f30586ec91329ea1558f908708261e0516bf427"}, + {file = "poetry_plugin_export-1.7.0.tar.gz", hash = "sha256:e73f207fc0e08c2f59ead82cbe39ef259b351a318d5c0bddcec13990bcd324a7"}, ] [package.dependencies] -poetry = ">=1.6.0,<2.0.0" +poetry = ">=1.8.0,<2.0.0" poetry-core = ">=1.7.0,<2.0.0" [[package]] @@ -4150,47 +4120,47 @@ files = [ [[package]] name = "pyarrow" -version = "15.0.0" +version = "15.0.2" description = "Python library for Apache Arrow" optional = false python-versions = ">=3.8" files = [ - {file = "pyarrow-15.0.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:0a524532fd6dd482edaa563b686d754c70417c2f72742a8c990b322d4c03a15d"}, - {file = "pyarrow-15.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:60a6bdb314affa9c2e0d5dddf3d9cbb9ef4a8dddaa68669975287d47ece67642"}, - {file = "pyarrow-15.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:66958fd1771a4d4b754cd385835e66a3ef6b12611e001d4e5edfcef5f30391e2"}, - {file = "pyarrow-15.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f500956a49aadd907eaa21d4fff75f73954605eaa41f61cb94fb008cf2e00c6"}, - {file = "pyarrow-15.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6f87d9c4f09e049c2cade559643424da84c43a35068f2a1c4653dc5b1408a929"}, - {file = "pyarrow-15.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:85239b9f93278e130d86c0e6bb455dcb66fc3fd891398b9d45ace8799a871a1e"}, - {file = "pyarrow-15.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:5b8d43e31ca16aa6e12402fcb1e14352d0d809de70edd185c7650fe80e0769e3"}, - {file = "pyarrow-15.0.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:fa7cd198280dbd0c988df525e50e35b5d16873e2cdae2aaaa6363cdb64e3eec5"}, - {file = "pyarrow-15.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8780b1a29d3c8b21ba6b191305a2a607de2e30dab399776ff0aa09131e266340"}, - {file = "pyarrow-15.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe0ec198ccc680f6c92723fadcb97b74f07c45ff3fdec9dd765deb04955ccf19"}, - {file = "pyarrow-15.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:036a7209c235588c2f07477fe75c07e6caced9b7b61bb897c8d4e52c4b5f9555"}, - {file = "pyarrow-15.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:2bd8a0e5296797faf9a3294e9fa2dc67aa7f10ae2207920dbebb785c77e9dbe5"}, - {file = "pyarrow-15.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:e8ebed6053dbe76883a822d4e8da36860f479d55a762bd9e70d8494aed87113e"}, - {file = "pyarrow-15.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:17d53a9d1b2b5bd7d5e4cd84d018e2a45bc9baaa68f7e6e3ebed45649900ba99"}, - {file = "pyarrow-15.0.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:9950a9c9df24090d3d558b43b97753b8f5867fb8e521f29876aa021c52fda351"}, - {file = "pyarrow-15.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:003d680b5e422d0204e7287bb3fa775b332b3fce2996aa69e9adea23f5c8f970"}, - {file = "pyarrow-15.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f75fce89dad10c95f4bf590b765e3ae98bcc5ba9f6ce75adb828a334e26a3d40"}, - {file = "pyarrow-15.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca9cb0039923bec49b4fe23803807e4ef39576a2bec59c32b11296464623dc2"}, - {file = "pyarrow-15.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:9ed5a78ed29d171d0acc26a305a4b7f83c122d54ff5270810ac23c75813585e4"}, - {file = "pyarrow-15.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6eda9e117f0402dfcd3cd6ec9bfee89ac5071c48fc83a84f3075b60efa96747f"}, - {file = "pyarrow-15.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:9a3a6180c0e8f2727e6f1b1c87c72d3254cac909e609f35f22532e4115461177"}, - {file = "pyarrow-15.0.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:19a8918045993349b207de72d4576af0191beef03ea655d8bdb13762f0cd6eac"}, - {file = "pyarrow-15.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d0ec076b32bacb6666e8813a22e6e5a7ef1314c8069d4ff345efa6246bc38593"}, - {file = "pyarrow-15.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5db1769e5d0a77eb92344c7382d6543bea1164cca3704f84aa44e26c67e320fb"}, - {file = "pyarrow-15.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2617e3bf9df2a00020dd1c1c6dce5cc343d979efe10bc401c0632b0eef6ef5b"}, - {file = "pyarrow-15.0.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:d31c1d45060180131caf10f0f698e3a782db333a422038bf7fe01dace18b3a31"}, - {file = "pyarrow-15.0.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:c8c287d1d479de8269398b34282e206844abb3208224dbdd7166d580804674b7"}, - {file = "pyarrow-15.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:07eb7f07dc9ecbb8dace0f58f009d3a29ee58682fcdc91337dfeb51ea618a75b"}, - {file = "pyarrow-15.0.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:47af7036f64fce990bb8a5948c04722e4e3ea3e13b1007ef52dfe0aa8f23cf7f"}, - {file = "pyarrow-15.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:93768ccfff85cf044c418bfeeafce9a8bb0cee091bd8fd19011aff91e58de540"}, - {file = "pyarrow-15.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6ee87fd6892700960d90abb7b17a72a5abb3b64ee0fe8db6c782bcc2d0dc0b4"}, - {file = "pyarrow-15.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:001fca027738c5f6be0b7a3159cc7ba16a5c52486db18160909a0831b063c4e4"}, - {file = "pyarrow-15.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:d1c48648f64aec09accf44140dccb92f4f94394b8d79976c426a5b79b11d4fa7"}, - {file = "pyarrow-15.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:972a0141be402bb18e3201448c8ae62958c9c7923dfaa3b3d4530c835ac81aed"}, - {file = "pyarrow-15.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:f01fc5cf49081426429127aa2d427d9d98e1cb94a32cb961d583a70b7c4504e6"}, - {file = "pyarrow-15.0.0.tar.gz", hash = "sha256:876858f549d540898f927eba4ef77cd549ad8d24baa3207cf1b72e5788b50e83"}, + {file = "pyarrow-15.0.2-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:88b340f0a1d05b5ccc3d2d986279045655b1fe8e41aba6ca44ea28da0d1455d8"}, + {file = "pyarrow-15.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eaa8f96cecf32da508e6c7f69bb8401f03745c050c1dd42ec2596f2e98deecac"}, + {file = "pyarrow-15.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23c6753ed4f6adb8461e7c383e418391b8d8453c5d67e17f416c3a5d5709afbd"}, + {file = "pyarrow-15.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f639c059035011db8c0497e541a8a45d98a58dbe34dc8fadd0ef128f2cee46e5"}, + {file = "pyarrow-15.0.2-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:290e36a59a0993e9a5224ed2fb3e53375770f07379a0ea03ee2fce2e6d30b423"}, + {file = "pyarrow-15.0.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:06c2bb2a98bc792f040bef31ad3e9be6a63d0cb39189227c08a7d955db96816e"}, + {file = "pyarrow-15.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:f7a197f3670606a960ddc12adbe8075cea5f707ad7bf0dffa09637fdbb89f76c"}, + {file = "pyarrow-15.0.2-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:5f8bc839ea36b1f99984c78e06e7a06054693dc2af8920f6fb416b5bca9944e4"}, + {file = "pyarrow-15.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f5e81dfb4e519baa6b4c80410421528c214427e77ca0ea9461eb4097c328fa33"}, + {file = "pyarrow-15.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a4f240852b302a7af4646c8bfe9950c4691a419847001178662a98915fd7ee7"}, + {file = "pyarrow-15.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e7d9cfb5a1e648e172428c7a42b744610956f3b70f524aa3a6c02a448ba853e"}, + {file = "pyarrow-15.0.2-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:2d4f905209de70c0eb5b2de6763104d5a9a37430f137678edfb9a675bac9cd98"}, + {file = "pyarrow-15.0.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:90adb99e8ce5f36fbecbbc422e7dcbcbed07d985eed6062e459e23f9e71fd197"}, + {file = "pyarrow-15.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:b116e7fd7889294cbd24eb90cd9bdd3850be3738d61297855a71ac3b8124ee38"}, + {file = "pyarrow-15.0.2-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:25335e6f1f07fdaa026a61c758ee7d19ce824a866b27bba744348fa73bb5a440"}, + {file = "pyarrow-15.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:90f19e976d9c3d8e73c80be84ddbe2f830b6304e4c576349d9360e335cd627fc"}, + {file = "pyarrow-15.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a22366249bf5fd40ddacc4f03cd3160f2d7c247692945afb1899bab8a140ddfb"}, + {file = "pyarrow-15.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2a335198f886b07e4b5ea16d08ee06557e07db54a8400cc0d03c7f6a22f785f"}, + {file = "pyarrow-15.0.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:3e6d459c0c22f0b9c810a3917a1de3ee704b021a5fb8b3bacf968eece6df098f"}, + {file = "pyarrow-15.0.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:033b7cad32198754d93465dcfb71d0ba7cb7cd5c9afd7052cab7214676eec38b"}, + {file = "pyarrow-15.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:29850d050379d6e8b5a693098f4de7fd6a2bea4365bfd073d7c57c57b95041ee"}, + {file = "pyarrow-15.0.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:7167107d7fb6dcadb375b4b691b7e316f4368f39f6f45405a05535d7ad5e5058"}, + {file = "pyarrow-15.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e85241b44cc3d365ef950432a1b3bd44ac54626f37b2e3a0cc89c20e45dfd8bf"}, + {file = "pyarrow-15.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:248723e4ed3255fcd73edcecc209744d58a9ca852e4cf3d2577811b6d4b59818"}, + {file = "pyarrow-15.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ff3bdfe6f1b81ca5b73b70a8d482d37a766433823e0c21e22d1d7dde76ca33f"}, + {file = "pyarrow-15.0.2-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:f3d77463dee7e9f284ef42d341689b459a63ff2e75cee2b9302058d0d98fe142"}, + {file = "pyarrow-15.0.2-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:8c1faf2482fb89766e79745670cbca04e7018497d85be9242d5350cba21357e1"}, + {file = "pyarrow-15.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:28f3016958a8e45a1069303a4a4f6a7d4910643fc08adb1e2e4a7ff056272ad3"}, + {file = "pyarrow-15.0.2-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:89722cb64286ab3d4daf168386f6968c126057b8c7ec3ef96302e81d8cdb8ae4"}, + {file = "pyarrow-15.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cd0ba387705044b3ac77b1b317165c0498299b08261d8122c96051024f953cd5"}, + {file = "pyarrow-15.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad2459bf1f22b6a5cdcc27ebfd99307d5526b62d217b984b9f5c974651398832"}, + {file = "pyarrow-15.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58922e4bfece8b02abf7159f1f53a8f4d9f8e08f2d988109126c17c3bb261f22"}, + {file = "pyarrow-15.0.2-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:adccc81d3dc0478ea0b498807b39a8d41628fa9210729b2f718b78cb997c7c91"}, + {file = "pyarrow-15.0.2-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:8bd2baa5fe531571847983f36a30ddbf65261ef23e496862ece83bdceb70420d"}, + {file = "pyarrow-15.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6669799a1d4ca9da9c7e06ef48368320f5856f36f9a4dd31a11839dda3f6cc8c"}, + {file = "pyarrow-15.0.2.tar.gz", hash = "sha256:9c9bc803cb3b7bfacc1e96ffbfd923601065d9d3f911179d81e72d99fd74a3d9"}, ] [package.dependencies] @@ -4231,13 +4201,13 @@ files = [ [[package]] name = "pydantic" -version = "2.6.3" +version = "2.6.4" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.6.3-py3-none-any.whl", hash = "sha256:72c6034df47f46ccdf81869fddb81aade68056003900a8724a4f160700016a2a"}, - {file = "pydantic-2.6.3.tar.gz", hash = "sha256:e07805c4c7f5c6826e33a1d4c9d47950d7eaf34868e2690f8594d2e30241f11f"}, + {file = "pydantic-2.6.4-py3-none-any.whl", hash = "sha256:cc46fce86607580867bdc3361ad462bab9c222ef042d3da86f2fb333e1d916c5"}, + {file = "pydantic-2.6.4.tar.gz", hash = "sha256:b1704e0847db01817624a6b86766967f552dd9dbf3afba4004409f908dcc84e6"}, ] [package.dependencies] @@ -4539,41 +4509,15 @@ files = [ [package.dependencies] tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -[[package]] -name = "pytelegrambotapi" -version = "4.16.1" -description = "Python Telegram bot api." -optional = true -python-versions = ">=3.8" -files = [ - {file = "pytelegrambotapi-4.16.1-py3-none-any.whl", hash = "sha256:85451b4fa1a47d99318a50aef361d177d2eb580c5ea6429fadb071cfad17fb35"}, - {file = "pytelegrambotapi-4.16.1.tar.gz", hash = "sha256:7ecfdac60de4a4c254059fc0f1997ceeb5da72803ffe4f35544b3ec9e9629edb"}, -] - -[package.dependencies] -requests = "*" - -[package.extras] -aiohttp = ["aiohttp"] -aioredis = ["aioredis"] -coloredlogs = ["coloredlogs"] -fastapi = ["fastapi"] -json = ["ujson"] -pil = ["pillow"] -psutil = ["psutil"] -redis = ["redis (>=3.4.1)"] -uvicorn = ["uvicorn"] -watchdog = ["watchdog"] - [[package]] name = "pytest" -version = "8.0.2" +version = "8.1.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.0.2-py3-none-any.whl", hash = "sha256:edfaaef32ce5172d5466b5127b42e0d6d35ebbe4453f0e3505d96afd93f6b096"}, - {file = "pytest-8.0.2.tar.gz", hash = "sha256:d4051d623a2e0b7e51960ba963193b09ce6daeb9759a451844a21e4ddedfc1bd"}, + {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, + {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, ] [package.dependencies] @@ -4581,21 +4525,21 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" -pluggy = ">=1.3.0,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} +pluggy = ">=1.4,<2.0" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] -testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-asyncio" -version = "0.23.5" +version = "0.23.5.post1" description = "Pytest support for asyncio" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-asyncio-0.23.5.tar.gz", hash = "sha256:3a048872a9c4ba14c3e90cc1aa20cbc2def7d01c7c8db3777ec281ba9c057675"}, - {file = "pytest_asyncio-0.23.5-py3-none-any.whl", hash = "sha256:4e7093259ba018d58ede7d5315131d21923a60f8a6e9ee266ce1589685c89eac"}, + {file = "pytest-asyncio-0.23.5.post1.tar.gz", hash = "sha256:b9a8806bea78c21276bc34321bbf234ba1b2ea5b30d9f0ce0f2dea45e4685813"}, + {file = "pytest_asyncio-0.23.5.post1-py3-none-any.whl", hash = "sha256:30f54d27774e79ac409778889880242b0403d09cabd65b727ce90fe92dd5d80e"}, ] [package.dependencies] @@ -4685,13 +4629,13 @@ tests = ["mock"] [[package]] name = "python-dateutil" -version = "2.8.2" +version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, ] [package.dependencies] @@ -4724,13 +4668,13 @@ files = [ [[package]] name = "python-on-whales" -version = "0.69.0" +version = "0.70.0" description = "A Docker client for Python, designed to be fun and intuitive!" optional = false python-versions = "<4,>=3.8" files = [ - {file = "python-on-whales-0.69.0.tar.gz", hash = "sha256:86bef044568e6abd381e63731e03a6536709be129c5934020da403b99f489b5e"}, - {file = "python_on_whales-0.69.0-py3-none-any.whl", hash = "sha256:73cf63377fbcbfee1f4a5e58148a1a0293f2ba153c2b862b830d0d9fedd5e223"}, + {file = "python-on-whales-0.70.0.tar.gz", hash = "sha256:bb89e91c86e049f9c04e2636f2d40faa000ff5b17d54f71e68581201e449eda5"}, + {file = "python_on_whales-0.70.0-py3-none-any.whl", hash = "sha256:492325387d7686adc6669e911820bd4da1cd672bc5a7fb6d54c32ee849bfe7e6"}, ] [package.dependencies] @@ -4743,6 +4687,31 @@ typing-extensions = "*" [package.extras] test = ["pytest"] +[[package]] +name = "python-telegram-bot" +version = "21.0.1" +description = "We have made you a wrapper you can't refuse" +optional = true +python-versions = ">=3.8" +files = [ + {file = "python-telegram-bot-21.0.1.tar.gz", hash = "sha256:3e005962c9fda01b09480044c49b3dd70870ee0c63340374bf3d5191e3910be9"}, + {file = "python_telegram_bot-21.0.1-py3-none-any.whl", hash = "sha256:b282544d1a51bf228b868e2ce0285b8448982878e2362175836429722d6f8795"}, +] + +[package.dependencies] +httpx = ">=0.27,<1.0" + +[package.extras] +all = ["APScheduler (>=3.10.4,<3.11.0)", "aiolimiter (>=1.1.0,<1.2.0)", "cachetools (>=5.3.3,<5.4.0)", "cryptography (>=39.0.1)", "httpx[http2]", "httpx[socks]", "pytz (>=2018.6)", "tornado (>=6.4,<7.0)"] +callback-data = ["cachetools (>=5.3.3,<5.4.0)"] +ext = ["APScheduler (>=3.10.4,<3.11.0)", "aiolimiter (>=1.1.0,<1.2.0)", "cachetools (>=5.3.3,<5.4.0)", "pytz (>=2018.6)", "tornado (>=6.4,<7.0)"] +http2 = ["httpx[http2]"] +job-queue = ["APScheduler (>=3.10.4,<3.11.0)", "pytz (>=2018.6)"] +passport = ["cryptography (>=39.0.1)"] +rate-limiter = ["aiolimiter (>=1.1.0,<1.2.0)"] +socks = ["httpx[socks]"] +webhooks = ["tornado (>=6.4,<7.0)"] + [[package]] name = "pytz" version = "2024.1" @@ -5011,101 +4980,101 @@ test = ["pytest (>=6,!=7.0.0,!=7.0.1)", "pytest-cov (>=3.0.0)", "pytest-qt"] [[package]] name = "rapidfuzz" -version = "3.6.1" +version = "3.6.2" description = "rapid fuzzy string matching" optional = false python-versions = ">=3.8" files = [ - {file = "rapidfuzz-3.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ac434fc71edda30d45db4a92ba5e7a42c7405e1a54cb4ec01d03cc668c6dcd40"}, - {file = "rapidfuzz-3.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2a791168e119cfddf4b5a40470620c872812042f0621e6a293983a2d52372db0"}, - {file = "rapidfuzz-3.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5a2f3e9df346145c2be94e4d9eeffb82fab0cbfee85bd4a06810e834fe7c03fa"}, - {file = "rapidfuzz-3.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23de71e7f05518b0bbeef55d67b5dbce3bcd3e2c81e7e533051a2e9401354eb0"}, - {file = "rapidfuzz-3.6.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d056e342989248d2bdd67f1955bb7c3b0ecfa239d8f67a8dfe6477b30872c607"}, - {file = "rapidfuzz-3.6.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01835d02acd5d95c1071e1da1bb27fe213c84a013b899aba96380ca9962364bc"}, - {file = "rapidfuzz-3.6.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed0f712e0bb5fea327e92aec8a937afd07ba8de4c529735d82e4c4124c10d5a0"}, - {file = "rapidfuzz-3.6.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96cd19934f76a1264e8ecfed9d9f5291fde04ecb667faef5f33bdbfd95fe2d1f"}, - {file = "rapidfuzz-3.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e06c4242a1354cf9d48ee01f6f4e6e19c511d50bb1e8d7d20bcadbb83a2aea90"}, - {file = "rapidfuzz-3.6.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d73dcfe789d37c6c8b108bf1e203e027714a239e50ad55572ced3c004424ed3b"}, - {file = "rapidfuzz-3.6.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:06e98ff000e2619e7cfe552d086815671ed09b6899408c2c1b5103658261f6f3"}, - {file = "rapidfuzz-3.6.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:08b6fb47dd889c69fbc0b915d782aaed43e025df6979b6b7f92084ba55edd526"}, - {file = "rapidfuzz-3.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a1788ebb5f5b655a15777e654ea433d198f593230277e74d51a2a1e29a986283"}, - {file = "rapidfuzz-3.6.1-cp310-cp310-win32.whl", hash = "sha256:c65f92881753aa1098c77818e2b04a95048f30edbe9c3094dc3707d67df4598b"}, - {file = "rapidfuzz-3.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:4243a9c35667a349788461aae6471efde8d8800175b7db5148a6ab929628047f"}, - {file = "rapidfuzz-3.6.1-cp310-cp310-win_arm64.whl", hash = "sha256:f59d19078cc332dbdf3b7b210852ba1f5db8c0a2cd8cc4c0ed84cc00c76e6802"}, - {file = "rapidfuzz-3.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fbc07e2e4ac696497c5f66ec35c21ddab3fc7a406640bffed64c26ab2f7ce6d6"}, - {file = "rapidfuzz-3.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:40cced1a8852652813f30fb5d4b8f9b237112a0bbaeebb0f4cc3611502556764"}, - {file = "rapidfuzz-3.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:82300e5f8945d601c2daaaac139d5524d7c1fdf719aa799a9439927739917460"}, - {file = "rapidfuzz-3.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edf97c321fd641fea2793abce0e48fa4f91f3c202092672f8b5b4e781960b891"}, - {file = "rapidfuzz-3.6.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7420e801b00dee4a344ae2ee10e837d603461eb180e41d063699fb7efe08faf0"}, - {file = "rapidfuzz-3.6.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:060bd7277dc794279fa95522af355034a29c90b42adcb7aa1da358fc839cdb11"}, - {file = "rapidfuzz-3.6.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7e3375e4f2bfec77f907680328e4cd16cc64e137c84b1886d547ab340ba6928"}, - {file = "rapidfuzz-3.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a490cd645ef9d8524090551016f05f052e416c8adb2d8b85d35c9baa9d0428ab"}, - {file = "rapidfuzz-3.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2e03038bfa66d2d7cffa05d81c2f18fd6acbb25e7e3c068d52bb7469e07ff382"}, - {file = "rapidfuzz-3.6.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:2b19795b26b979c845dba407fe79d66975d520947b74a8ab6cee1d22686f7967"}, - {file = "rapidfuzz-3.6.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:064c1d66c40b3a0f488db1f319a6e75616b2e5fe5430a59f93a9a5e40a656d15"}, - {file = "rapidfuzz-3.6.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3c772d04fb0ebeece3109d91f6122b1503023086a9591a0b63d6ee7326bd73d9"}, - {file = "rapidfuzz-3.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:841eafba6913c4dfd53045835545ba01a41e9644e60920c65b89c8f7e60c00a9"}, - {file = "rapidfuzz-3.6.1-cp311-cp311-win32.whl", hash = "sha256:266dd630f12696ea7119f31d8b8e4959ef45ee2cbedae54417d71ae6f47b9848"}, - {file = "rapidfuzz-3.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:d79aec8aeee02ab55d0ddb33cea3ecd7b69813a48e423c966a26d7aab025cdfe"}, - {file = "rapidfuzz-3.6.1-cp311-cp311-win_arm64.whl", hash = "sha256:484759b5dbc5559e76fefaa9170147d1254468f555fd9649aea3bad46162a88b"}, - {file = "rapidfuzz-3.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b2ef4c0fd3256e357b70591ffb9e8ed1d439fb1f481ba03016e751a55261d7c1"}, - {file = "rapidfuzz-3.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:588c4b20fa2fae79d60a4e438cf7133d6773915df3cc0a7f1351da19eb90f720"}, - {file = "rapidfuzz-3.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7142ee354e9c06e29a2636b9bbcb592bb00600a88f02aa5e70e4f230347b373e"}, - {file = "rapidfuzz-3.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1dfc557c0454ad22382373ec1b7df530b4bbd974335efe97a04caec936f2956a"}, - {file = "rapidfuzz-3.6.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:03f73b381bdeccb331a12c3c60f1e41943931461cdb52987f2ecf46bfc22f50d"}, - {file = "rapidfuzz-3.6.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b0ccc2ec1781c7e5370d96aef0573dd1f97335343e4982bdb3a44c133e27786"}, - {file = "rapidfuzz-3.6.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da3e8c9f7e64bb17faefda085ff6862ecb3ad8b79b0f618a6cf4452028aa2222"}, - {file = "rapidfuzz-3.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fde9b14302a31af7bdafbf5cfbb100201ba21519be2b9dedcf4f1048e4fbe65d"}, - {file = "rapidfuzz-3.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c1a23eee225dfb21c07f25c9fcf23eb055d0056b48e740fe241cbb4b22284379"}, - {file = "rapidfuzz-3.6.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e49b9575d16c56c696bc7b06a06bf0c3d4ef01e89137b3ddd4e2ce709af9fe06"}, - {file = "rapidfuzz-3.6.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:0a9fc714b8c290261669f22808913aad49553b686115ad0ee999d1cb3df0cd66"}, - {file = "rapidfuzz-3.6.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:a3ee4f8f076aa92184e80308fc1a079ac356b99c39408fa422bbd00145be9854"}, - {file = "rapidfuzz-3.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f056ba42fd2f32e06b2c2ba2443594873cfccc0c90c8b6327904fc2ddf6d5799"}, - {file = "rapidfuzz-3.6.1-cp312-cp312-win32.whl", hash = "sha256:5d82b9651e3d34b23e4e8e201ecd3477c2baa17b638979deeabbb585bcb8ba74"}, - {file = "rapidfuzz-3.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:dad55a514868dae4543ca48c4e1fc0fac704ead038dafedf8f1fc0cc263746c1"}, - {file = "rapidfuzz-3.6.1-cp312-cp312-win_arm64.whl", hash = "sha256:3c84294f4470fcabd7830795d754d808133329e0a81d62fcc2e65886164be83b"}, - {file = "rapidfuzz-3.6.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e19d519386e9db4a5335a4b29f25b8183a1c3f78cecb4c9c3112e7f86470e37f"}, - {file = "rapidfuzz-3.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:01eb03cd880a294d1bf1a583fdd00b87169b9cc9c9f52587411506658c864d73"}, - {file = "rapidfuzz-3.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:be368573255f8fbb0125a78330a1a40c65e9ba3c5ad129a426ff4289099bfb41"}, - {file = "rapidfuzz-3.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3e5af946f419c30f5cb98b69d40997fe8580efe78fc83c2f0f25b60d0e56efb"}, - {file = "rapidfuzz-3.6.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f382f7ffe384ce34345e1c0b2065451267d3453cadde78946fbd99a59f0cc23c"}, - {file = "rapidfuzz-3.6.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be156f51f3a4f369e758505ed4ae64ea88900dcb2f89d5aabb5752676d3f3d7e"}, - {file = "rapidfuzz-3.6.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1936d134b6c513fbe934aeb668b0fee1ffd4729a3c9d8d373f3e404fbb0ce8a0"}, - {file = "rapidfuzz-3.6.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12ff8eaf4a9399eb2bebd838f16e2d1ded0955230283b07376d68947bbc2d33d"}, - {file = "rapidfuzz-3.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae598a172e3a95df3383634589660d6b170cc1336fe7578115c584a99e0ba64d"}, - {file = "rapidfuzz-3.6.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cd4ba4c18b149da11e7f1b3584813159f189dc20833709de5f3df8b1342a9759"}, - {file = "rapidfuzz-3.6.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:0402f1629e91a4b2e4aee68043a30191e5e1b7cd2aa8dacf50b1a1bcf6b7d3ab"}, - {file = "rapidfuzz-3.6.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:1e12319c6b304cd4c32d5db00b7a1e36bdc66179c44c5707f6faa5a889a317c0"}, - {file = "rapidfuzz-3.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0bbfae35ce4de4c574b386c43c78a0be176eeddfdae148cb2136f4605bebab89"}, - {file = "rapidfuzz-3.6.1-cp38-cp38-win32.whl", hash = "sha256:7fec74c234d3097612ea80f2a80c60720eec34947066d33d34dc07a3092e8105"}, - {file = "rapidfuzz-3.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:a553cc1a80d97459d587529cc43a4c7c5ecf835f572b671107692fe9eddf3e24"}, - {file = "rapidfuzz-3.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:757dfd7392ec6346bd004f8826afb3bf01d18a723c97cbe9958c733ab1a51791"}, - {file = "rapidfuzz-3.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2963f4a3f763870a16ee076796be31a4a0958fbae133dbc43fc55c3968564cf5"}, - {file = "rapidfuzz-3.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d2f0274595cc5b2b929c80d4e71b35041104b577e118cf789b3fe0a77b37a4c5"}, - {file = "rapidfuzz-3.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f211e366e026de110a4246801d43a907cd1a10948082f47e8a4e6da76fef52"}, - {file = "rapidfuzz-3.6.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a59472b43879012b90989603aa5a6937a869a72723b1bf2ff1a0d1edee2cc8e6"}, - {file = "rapidfuzz-3.6.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a03863714fa6936f90caa7b4b50ea59ea32bb498cc91f74dc25485b3f8fccfe9"}, - {file = "rapidfuzz-3.6.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5dd95b6b7bfb1584f806db89e1e0c8dbb9d25a30a4683880c195cc7f197eaf0c"}, - {file = "rapidfuzz-3.6.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7183157edf0c982c0b8592686535c8b3e107f13904b36d85219c77be5cefd0d8"}, - {file = "rapidfuzz-3.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ad9d74ef7c619b5b0577e909582a1928d93e07d271af18ba43e428dc3512c2a1"}, - {file = "rapidfuzz-3.6.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b53137d81e770c82189e07a8f32722d9e4260f13a0aec9914029206ead38cac3"}, - {file = "rapidfuzz-3.6.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:49b9ed2472394d306d5dc967a7de48b0aab599016aa4477127b20c2ed982dbf9"}, - {file = "rapidfuzz-3.6.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:dec307b57ec2d5054d77d03ee4f654afcd2c18aee00c48014cb70bfed79597d6"}, - {file = "rapidfuzz-3.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4381023fa1ff32fd5076f5d8321249a9aa62128eb3f21d7ee6a55373e672b261"}, - {file = "rapidfuzz-3.6.1-cp39-cp39-win32.whl", hash = "sha256:8d7a072f10ee57c8413c8ab9593086d42aaff6ee65df4aa6663eecdb7c398dca"}, - {file = "rapidfuzz-3.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:ebcfb5bfd0a733514352cfc94224faad8791e576a80ffe2fd40b2177bf0e7198"}, - {file = "rapidfuzz-3.6.1-cp39-cp39-win_arm64.whl", hash = "sha256:1c47d592e447738744905c18dda47ed155620204714e6df20eb1941bb1ba315e"}, - {file = "rapidfuzz-3.6.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:eef8b346ab331bec12bbc83ac75641249e6167fab3d84d8f5ca37fd8e6c7a08c"}, - {file = "rapidfuzz-3.6.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53251e256017e2b87f7000aee0353ba42392c442ae0bafd0f6b948593d3f68c6"}, - {file = "rapidfuzz-3.6.1-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6dede83a6b903e3ebcd7e8137e7ff46907ce9316e9d7e7f917d7e7cdc570ee05"}, - {file = "rapidfuzz-3.6.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e4da90e4c2b444d0a171d7444ea10152e07e95972bb40b834a13bdd6de1110c"}, - {file = "rapidfuzz-3.6.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:ca3dfcf74f2b6962f411c33dd95b0adf3901266e770da6281bc96bb5a8b20de9"}, - {file = "rapidfuzz-3.6.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bcc957c0a8bde8007f1a8a413a632a1a409890f31f73fe764ef4eac55f59ca87"}, - {file = "rapidfuzz-3.6.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:692c9a50bea7a8537442834f9bc6b7d29d8729a5b6379df17c31b6ab4df948c2"}, - {file = "rapidfuzz-3.6.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c23ceaea27e790ddd35ef88b84cf9d721806ca366199a76fd47cfc0457a81b"}, - {file = "rapidfuzz-3.6.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b155e67fff215c09f130555002e42f7517d0ea72cbd58050abb83cb7c880cec"}, - {file = "rapidfuzz-3.6.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3028ee8ecc48250607fa8a0adce37b56275ec3b1acaccd84aee1f68487c8557b"}, - {file = "rapidfuzz-3.6.1.tar.gz", hash = "sha256:35660bee3ce1204872574fa041c7ad7ec5175b3053a4cb6e181463fc07013de7"}, + {file = "rapidfuzz-3.6.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a5637e6bf11b15b5aff6ee818c76bdec99ad208511b78985e6209ba648a6e3ee"}, + {file = "rapidfuzz-3.6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:380586664f2f63807050ddb95e7702888b4f0b425abf17655940c411f39287ad"}, + {file = "rapidfuzz-3.6.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3168ff565d4b8c239cf11fb604dd2507d30e9bcaac76a4077c0ac23cf2c866ed"}, + {file = "rapidfuzz-3.6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be69f7fd46b5c6467fe5e2fd4cff3816b0c03048eed8a4becb9a73e6000960e7"}, + {file = "rapidfuzz-3.6.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cbd5894f23fdf5697499cf759523639838ac822bd1600e343fdce7313baa02ae"}, + {file = "rapidfuzz-3.6.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:85a5b6e026393fe39fb61146b9c17c5af66fffbe1410e992c4bb06d9ec327bd3"}, + {file = "rapidfuzz-3.6.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab269adfc64480f209e99f253391a10735edd5c09046e04899adab5fb132f20e"}, + {file = "rapidfuzz-3.6.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35aeac852bca06023d6bbd50c1fc504ca5a9a3613d5e75a140f0be7601fa34ef"}, + {file = "rapidfuzz-3.6.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e706f302c6a3ae0d74edd0d6ace46aee1ae07c563b436ccf5ff04db2b3571e60"}, + {file = "rapidfuzz-3.6.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bec353f022011e6e5cd28ccb8700fbd2a33918197af0d4e0abb3c3f4845cc864"}, + {file = "rapidfuzz-3.6.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ef3925daaa93eed20401012e219f569ff0c039ed5bf4ce2d3737b4f75d441622"}, + {file = "rapidfuzz-3.6.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6ee98d88ae9ccc77ff61992ed33b2496478def5dc0da55c9a9aa06fcb725a352"}, + {file = "rapidfuzz-3.6.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:423c7c588b09d618601097b7a0017dfcb91132a2076bef29023c5f3cd2dc3de1"}, + {file = "rapidfuzz-3.6.2-cp310-cp310-win32.whl", hash = "sha256:c17c5efee347a40a6f4c1eec59e3d7d1e22f7613a97f8b8a07733ef723483a04"}, + {file = "rapidfuzz-3.6.2-cp310-cp310-win_amd64.whl", hash = "sha256:4209816626d8d6ff8ae7dc248061c6059e618b70c6e6f6e4d7444ae3740b2b85"}, + {file = "rapidfuzz-3.6.2-cp310-cp310-win_arm64.whl", hash = "sha256:1c54d3c85e522d3ac9ee39415f183c8fa184c4f87e7e5a37938f15a6d50e853a"}, + {file = "rapidfuzz-3.6.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e06f6d270112f5db001f1cba5a97e1a48aee3d3dbdcbea3ec027c230462dbf9b"}, + {file = "rapidfuzz-3.6.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:080cb71b50cb6aff11d1c6aeb157f273e2da0b2bdb3f9d7b01257e49e69a8576"}, + {file = "rapidfuzz-3.6.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a7895e04a22d6515bc91a850e0831f2405547605aa311d1ffec51e4818abc3c1"}, + {file = "rapidfuzz-3.6.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd82f9838519136b7083dd1e3149ee80344521f3dc37f744f227505ff0883efb"}, + {file = "rapidfuzz-3.6.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a945567c2b0b6e069454c9782d5234b0b6795718adf7a9f868bd3144afa6a023"}, + {file = "rapidfuzz-3.6.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:673ba2c343644805acdae1cb949c6a4de71aa2f62a998978551ebea59603af3f"}, + {file = "rapidfuzz-3.6.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9d457c89bac1471442002e70551e8268e639b3870b4a4521eae363c07253be87"}, + {file = "rapidfuzz-3.6.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:495c0d8e14e6f12520eb7fc71b9ba9fcaafb47fc23a654e6e89b6c7985ec0020"}, + {file = "rapidfuzz-3.6.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6d67b649bf3e1b1722d04eca44d37919aef88305ce7ad05564502d013cf550fd"}, + {file = "rapidfuzz-3.6.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e48dde8ca83d11daa00900cf6a5d281a1297aef9b7bfa73801af6e8822be5019"}, + {file = "rapidfuzz-3.6.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:824cc381cf81cbf8d158f6935664ec2a69e6ac3b1d39fa201988bf81a257f775"}, + {file = "rapidfuzz-3.6.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:1dfe4c24957474ce0ac75d886387e30e292b4be39228a6d71f76de414dc187db"}, + {file = "rapidfuzz-3.6.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d57b98013b802621bbc8b12a46bfc9d36ac552ab51ca207f7ce167ad46adabeb"}, + {file = "rapidfuzz-3.6.2-cp311-cp311-win32.whl", hash = "sha256:9a07dffac439223b4f1025dbfc68f4445a3460a859309c9858c2a3fa29617cdc"}, + {file = "rapidfuzz-3.6.2-cp311-cp311-win_amd64.whl", hash = "sha256:95a49c6b8bf1229743ae585dd5b7d57f0d15a7eb6e826866d5c9965ba958503c"}, + {file = "rapidfuzz-3.6.2-cp311-cp311-win_arm64.whl", hash = "sha256:af7c19ec86e11488539380d3db1755be5d561a3c0e7b04ff9d07abd7f9a8e9d8"}, + {file = "rapidfuzz-3.6.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:de8adc12161bf282c60f12dc9233bb31632f71d446a010fe7469a69b8153427f"}, + {file = "rapidfuzz-3.6.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:337e357f693130c4c6be740652542b260e36f622c59e01fa33d58f1d2750c930"}, + {file = "rapidfuzz-3.6.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6468f8bc8c3c50604f43631550ef9cfec873515dba5023ca34d461be94669fc8"}, + {file = "rapidfuzz-3.6.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74c6773b11445b5e5cf93ca383171cd0ac0cdeafea11a7b2a5688f8bf8d813e6"}, + {file = "rapidfuzz-3.6.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1507fc5769aa109dda4de3a15f822a0f6a03e18d627bd0ba3ddbb253cf70e07"}, + {file = "rapidfuzz-3.6.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:617949a70150e6fffdaed19253dd49f7a53528411dc8bf7663d499ba21e0f61e"}, + {file = "rapidfuzz-3.6.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f8b77779174b1b40aa70827692571ab457061897846255ad7d5d559e2edb1932"}, + {file = "rapidfuzz-3.6.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80e51b22a7da83f9c87a97e92df07ed0612c74c35496590255f4b5d5b4212dfe"}, + {file = "rapidfuzz-3.6.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3ae7c86914cb6673e97e187ba431b9c4cf4177d9ae77f8a1e5b2ba9a5628839e"}, + {file = "rapidfuzz-3.6.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ddc380ffaa90f204cc9ddcb779114b9ab6f015246d549de9d47871a97ef9f18a"}, + {file = "rapidfuzz-3.6.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:3c1dc078ef371fce09f9f3eec2ca4eaa2a8cd412ec53941015b4f39f14d34407"}, + {file = "rapidfuzz-3.6.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:9a74102fc5a2534fe91f7507838623e1f3a149d8e05648389c42bb42e14b1c3f"}, + {file = "rapidfuzz-3.6.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:48e1eaea8fcd522fca7f04f0480663f0f0cfb77957092cce60a93f4462864996"}, + {file = "rapidfuzz-3.6.2-cp312-cp312-win32.whl", hash = "sha256:66b008bf2972740cd2dda5d382eb8bdb87265cd88198e71c7797bdc0d1f79d20"}, + {file = "rapidfuzz-3.6.2-cp312-cp312-win_amd64.whl", hash = "sha256:87ac3a87f2251ae2e95fc9478ca5c759de6d141d04c84d3fec9f9cdcfc167b33"}, + {file = "rapidfuzz-3.6.2-cp312-cp312-win_arm64.whl", hash = "sha256:b593cc51aed887e93b78c2f94dfae9008be2b23d17afd3b1f1d3eb3913b58f26"}, + {file = "rapidfuzz-3.6.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:7d830bc7a9b586a374147ec60b08b1f9ae5996b43f75cc514f37faef3866b519"}, + {file = "rapidfuzz-3.6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:dbee7f5ff11872b76505cbd87c814abc823e8757f11c69062eb3b25130a283da"}, + {file = "rapidfuzz-3.6.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:28c011fb31f2c3f82f503aedd6097d3d3854e574e327a119a3b7eb2cf90b79ca"}, + {file = "rapidfuzz-3.6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cda81d0e0ce0c13abfa46b24e10c1e85f9c6acb628f0a9a948f5779f9c2076a2"}, + {file = "rapidfuzz-3.6.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c279928651ce0e9e5220dcb25a00cc53b65e592a0861336a38299bcdca3a596"}, + {file = "rapidfuzz-3.6.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35bd4bc9c40e6994c5d6edea4b9319388b4d9711c13c66d543bb4c37624b4184"}, + {file = "rapidfuzz-3.6.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d07899506a5a8760448d9df036d528b55a554bf571714173635c79eef4a86e58"}, + {file = "rapidfuzz-3.6.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb2e51d01b9c6d6954a3e055c57a80d4685b4fc82719db5519fc153566bcd6bb"}, + {file = "rapidfuzz-3.6.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:153d065e353371cc0aeff32b99999a5758266a64e958d1364189367c1c9f6814"}, + {file = "rapidfuzz-3.6.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4edcceebb85ebfa49a3ddcde20ad891d36c08dc0fd592efdab0e7d313a4e36af"}, + {file = "rapidfuzz-3.6.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3549123fca5bb817341025f98e8e49ca99f84596c7c4f92b658f8e5836040d4a"}, + {file = "rapidfuzz-3.6.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:84c1032ae42628465b7a5cc35249906061e18a8193c9c27cbd2db54e9823a9a6"}, + {file = "rapidfuzz-3.6.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9bcc91ebd8fc69a6bd3b5711c8250f5f4e70606b4da75ef415f57ad209978205"}, + {file = "rapidfuzz-3.6.2-cp38-cp38-win32.whl", hash = "sha256:f3a70f341c4c111bad910d2df69c78577a98af140319a996af24c9385939335d"}, + {file = "rapidfuzz-3.6.2-cp38-cp38-win_amd64.whl", hash = "sha256:354ad5fe655beb7b279390cb58334903931c5452ecbad1b1666ffb06786498e2"}, + {file = "rapidfuzz-3.6.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1b86b93d93020c2b3edc1665d75c8855784845fc0a739b312c26c3a4bf0c80d5"}, + {file = "rapidfuzz-3.6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:28243086ed0e50808bb56632e5442c457241646aeafafd501ac87901f40a3237"}, + {file = "rapidfuzz-3.6.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ed52461ae5a9ea4c400d38e2649c74a413f1a6d8fb8308b66f1fbd122514732f"}, + {file = "rapidfuzz-3.6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a46220f86a5f9cb016af31525e0d0865cad437d02239aa0d8aed2ab8bff1f1c"}, + {file = "rapidfuzz-3.6.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:81a630ed2fc3ec5fc7400eb66bab1f87e282b4d47f0abe3e48c6634dfa13b5e4"}, + {file = "rapidfuzz-3.6.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8e5a437b9089df6242a718d9c31ab1742989e9400a0977af012ef483b63b4c2"}, + {file = "rapidfuzz-3.6.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16270b5529de83b7bae7457e952e4d9cf3fbf029a837dd32d415bb9e0eb8e599"}, + {file = "rapidfuzz-3.6.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5378c04102c7f084cde30a100154fa6d7e2baf0d51a6bdd2f912545559c1fb35"}, + {file = "rapidfuzz-3.6.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7f18397c8d6a65fc0b288d2fc29bc7baeea6ba91eeb95163a3cd98f23cd3bc85"}, + {file = "rapidfuzz-3.6.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2acd2514defce81e6ff4bbff50252d5e7df8e85a731442c4b83e44c86cf1c916"}, + {file = "rapidfuzz-3.6.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:1df2faf80201952e252413b6fac6f3e146080dcebb87bb1bb722508e67558ed8"}, + {file = "rapidfuzz-3.6.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6440ed0b3007c1c9286b0b88fe2ab2d9e83edd60cd62293b3dfabb732b4e8a30"}, + {file = "rapidfuzz-3.6.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4fcfa23b5553b27f4016df77c53172ea743454cf12c28cfa7c35a309a2be93b3"}, + {file = "rapidfuzz-3.6.2-cp39-cp39-win32.whl", hash = "sha256:2d580d937146e803c8e5e1b87916cab8d6f84013b6392713e201efcda335c7d8"}, + {file = "rapidfuzz-3.6.2-cp39-cp39-win_amd64.whl", hash = "sha256:fe2a68be734e8e88af23385c68d6467e15818b6b1df1cbfebf7bff577226c957"}, + {file = "rapidfuzz-3.6.2-cp39-cp39-win_arm64.whl", hash = "sha256:6478f7803efebf5f644d0b758439c5b25728550fdfbb19783d150004c46a75a9"}, + {file = "rapidfuzz-3.6.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:36ce7b68a7b90b787cdd73480a68d2f1ca63c31a3a9d5a79a8736f978e1e9344"}, + {file = "rapidfuzz-3.6.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53597fd72a9340bcdd80d3620f4957c2b92f9b569313b969a3abdaffd193aae6"}, + {file = "rapidfuzz-3.6.2-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4f6de745fe6ce46a422d353ee10599013631d7d714a36d025f164b2d4e8c000"}, + {file = "rapidfuzz-3.6.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62df2136068e2515ed8beb01756381ff62c29384d785e3bf46e3111d4ea3ba1e"}, + {file = "rapidfuzz-3.6.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7382c90170f60c846c81a07ddd80bb2e8c43c8383754486fa37f67391a571897"}, + {file = "rapidfuzz-3.6.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f31314fd2e2f3dc3e519e6f93669462ce7953df2def1c344aa8f5345976d0eb2"}, + {file = "rapidfuzz-3.6.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:012221629d54d3bee954148247f711eb86d4d390b589ebfe03172ea0b37a7531"}, + {file = "rapidfuzz-3.6.2-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d41dd59a70decfce6595315367a2fea2af660d92a9d144acc6479030501014d7"}, + {file = "rapidfuzz-3.6.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f9fa14136a5b0cba1ec42531f7c3e0b0d3edb7fd6bc5e5ae7b498541f3855ab"}, + {file = "rapidfuzz-3.6.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:259364199cbfeca33b1af369fc7951f71717aa285184a3fa5a7b1772da1b89db"}, + {file = "rapidfuzz-3.6.2.tar.gz", hash = "sha256:cf911e792ab0c431694c9bf2648afabfd92099103f2e31492893e078ddca5e1a"}, ] [package.extras] @@ -5113,17 +5082,17 @@ full = ["numpy"] [[package]] name = "redis" -version = "5.0.2" +version = "5.0.3" description = "Python client for Redis database and key-value store" optional = true python-versions = ">=3.7" files = [ - {file = "redis-5.0.2-py3-none-any.whl", hash = "sha256:4caa8e1fcb6f3c0ef28dba99535101d80934b7d4cd541bbb47f4a3826ee472d1"}, - {file = "redis-5.0.2.tar.gz", hash = "sha256:3f82cc80d350e93042c8e6e7a5d0596e4dd68715babffba79492733e1f367037"}, + {file = "redis-5.0.3-py3-none-any.whl", hash = "sha256:5da9b8fe9e1254293756c16c008e8620b3d15fcc6dde6babde9541850e72a32d"}, + {file = "redis-5.0.3.tar.gz", hash = "sha256:4973bae7444c0fbed64a06b87446f79361cb7e4ec1538c022d696ed7a5015580"}, ] [package.dependencies] -async-timeout = ">=4.0.3" +async-timeout = {version = ">=4.0.3", markers = "python_full_version < \"3.11.3\""} [package.extras] hiredis = ["hiredis (>=1.0.0)"] @@ -5131,13 +5100,13 @@ ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)" [[package]] name = "referencing" -version = "0.33.0" +version = "0.34.0" description = "JSON Referencing + Python" optional = false python-versions = ">=3.8" files = [ - {file = "referencing-0.33.0-py3-none-any.whl", hash = "sha256:39240f2ecc770258f28b642dd47fd74bc8b02484de54e1882b74b35ebd779bd5"}, - {file = "referencing-0.33.0.tar.gz", hash = "sha256:c775fedf74bc0f9189c2a3be1c12fd03e8c23f4d371dce795df44e06c5b412f7"}, + {file = "referencing-0.34.0-py3-none-any.whl", hash = "sha256:d53ae300ceddd3169f1ffa9caf2cb7b769e92657e4fafb23d34b93679116dfd4"}, + {file = "referencing-0.34.0.tar.gz", hash = "sha256:5773bd84ef41799a5a8ca72dc34590c041eb01bf9aa02632b4a973fb0181a844"}, ] [package.dependencies] @@ -5388,18 +5357,18 @@ win32 = ["pywin32"] [[package]] name = "setuptools" -version = "69.1.1" +version = "69.2.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.1.1-py3-none-any.whl", hash = "sha256:02fa291a0471b3a18b2b2481ed902af520c69e8ae0919c13da936542754b4c56"}, - {file = "setuptools-69.1.1.tar.gz", hash = "sha256:5c0806c7d9af348e6dd3777b4f4dbb42c7ad85b190104837488eab9a7c945cf8"}, + {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, + {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] @@ -5715,60 +5684,60 @@ test = ["pytest"] [[package]] name = "sqlalchemy" -version = "2.0.27" +version = "2.0.28" description = "Database Abstraction Library" optional = true python-versions = ">=3.7" files = [ - {file = "SQLAlchemy-2.0.27-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d04e579e911562f1055d26dab1868d3e0bb905db3bccf664ee8ad109f035618a"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fa67d821c1fd268a5a87922ef4940442513b4e6c377553506b9db3b83beebbd8"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c7a596d0be71b7baa037f4ac10d5e057d276f65a9a611c46970f012752ebf2d"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:954d9735ee9c3fa74874c830d089a815b7b48df6f6b6e357a74130e478dbd951"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5cd20f58c29bbf2680039ff9f569fa6d21453fbd2fa84dbdb4092f006424c2e6"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:03f448ffb731b48323bda68bcc93152f751436ad6037f18a42b7e16af9e91c07"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-win32.whl", hash = "sha256:d997c5938a08b5e172c30583ba6b8aad657ed9901fc24caf3a7152eeccb2f1b4"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-win_amd64.whl", hash = "sha256:eb15ef40b833f5b2f19eeae65d65e191f039e71790dd565c2af2a3783f72262f"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6c5bad7c60a392850d2f0fee8f355953abaec878c483dd7c3836e0089f046bf6"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3012ab65ea42de1be81fff5fb28d6db893ef978950afc8130ba707179b4284a"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbcd77c4d94b23e0753c5ed8deba8c69f331d4fd83f68bfc9db58bc8983f49cd"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d177b7e82f6dd5e1aebd24d9c3297c70ce09cd1d5d37b43e53f39514379c029c"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:680b9a36029b30cf063698755d277885d4a0eab70a2c7c6e71aab601323cba45"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1306102f6d9e625cebaca3d4c9c8f10588735ef877f0360b5cdb4fdfd3fd7131"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-win32.whl", hash = "sha256:5b78aa9f4f68212248aaf8943d84c0ff0f74efc65a661c2fc68b82d498311fd5"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-win_amd64.whl", hash = "sha256:15e19a84b84528f52a68143439d0c7a3a69befcd4f50b8ef9b7b69d2628ae7c4"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0de1263aac858f288a80b2071990f02082c51d88335a1db0d589237a3435fe71"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce850db091bf7d2a1f2fdb615220b968aeff3849007b1204bf6e3e50a57b3d32"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dfc936870507da96aebb43e664ae3a71a7b96278382bcfe84d277b88e379b18"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4fbe6a766301f2e8a4519f4500fe74ef0a8509a59e07a4085458f26228cd7cc"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4535c49d961fe9a77392e3a630a626af5baa967172d42732b7a43496c8b28876"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0fb3bffc0ced37e5aa4ac2416f56d6d858f46d4da70c09bb731a246e70bff4d5"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-win32.whl", hash = "sha256:7f470327d06400a0aa7926b375b8e8c3c31d335e0884f509fe272b3c700a7254"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-win_amd64.whl", hash = "sha256:f9374e270e2553653d710ece397df67db9d19c60d2647bcd35bfc616f1622dcd"}, - {file = "SQLAlchemy-2.0.27-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e97cf143d74a7a5a0f143aa34039b4fecf11343eed66538610debc438685db4a"}, - {file = "SQLAlchemy-2.0.27-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7b5a3e2120982b8b6bd1d5d99e3025339f7fb8b8267551c679afb39e9c7c7f1"}, - {file = "SQLAlchemy-2.0.27-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e36aa62b765cf9f43a003233a8c2d7ffdeb55bc62eaa0a0380475b228663a38f"}, - {file = "SQLAlchemy-2.0.27-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5ada0438f5b74c3952d916c199367c29ee4d6858edff18eab783b3978d0db16d"}, - {file = "SQLAlchemy-2.0.27-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b1d9d1bfd96eef3c3faedb73f486c89e44e64e40e5bfec304ee163de01cf996f"}, - {file = "SQLAlchemy-2.0.27-cp37-cp37m-win32.whl", hash = "sha256:ca891af9f3289d24a490a5fde664ea04fe2f4984cd97e26de7442a4251bd4b7c"}, - {file = "SQLAlchemy-2.0.27-cp37-cp37m-win_amd64.whl", hash = "sha256:fd8aafda7cdff03b905d4426b714601c0978725a19efc39f5f207b86d188ba01"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec1f5a328464daf7a1e4e385e4f5652dd9b1d12405075ccba1df842f7774b4fc"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ad862295ad3f644e3c2c0d8b10a988e1600d3123ecb48702d2c0f26771f1c396"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48217be1de7d29a5600b5c513f3f7664b21d32e596d69582be0a94e36b8309cb"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e56afce6431450442f3ab5973156289bd5ec33dd618941283847c9fd5ff06bf"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:611068511b5531304137bcd7fe8117c985d1b828eb86043bd944cebb7fae3910"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b86abba762ecfeea359112b2bb4490802b340850bbee1948f785141a5e020de8"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-win32.whl", hash = "sha256:30d81cc1192dc693d49d5671cd40cdec596b885b0ce3b72f323888ab1c3863d5"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-win_amd64.whl", hash = "sha256:120af1e49d614d2525ac247f6123841589b029c318b9afbfc9e2b70e22e1827d"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d07ee7793f2aeb9b80ec8ceb96bc8cc08a2aec8a1b152da1955d64e4825fcbac"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cb0845e934647232b6ff5150df37ceffd0b67b754b9fdbb095233deebcddbd4a"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fc19ae2e07a067663dd24fca55f8ed06a288384f0e6e3910420bf4b1270cc51"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b90053be91973a6fb6020a6e44382c97739736a5a9d74e08cc29b196639eb979"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2f5c9dfb0b9ab5e3a8a00249534bdd838d943ec4cfb9abe176a6c33408430230"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:33e8bde8fff203de50399b9039c4e14e42d4d227759155c21f8da4a47fc8053c"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-win32.whl", hash = "sha256:d873c21b356bfaf1589b89090a4011e6532582b3a8ea568a00e0c3aab09399dd"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-win_amd64.whl", hash = "sha256:ff2f1b7c963961d41403b650842dc2039175b906ab2093635d8319bef0b7d620"}, - {file = "SQLAlchemy-2.0.27-py3-none-any.whl", hash = "sha256:1ab4e0448018d01b142c916cc7119ca573803a4745cfe341b8f95657812700ac"}, - {file = "SQLAlchemy-2.0.27.tar.gz", hash = "sha256:86a6ed69a71fe6b88bf9331594fa390a2adda4a49b5c06f98e47bf0d392534f8"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0b148ab0438f72ad21cb004ce3bdaafd28465c4276af66df3b9ecd2037bf252"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bbda76961eb8f27e6ad3c84d1dc56d5bc61ba8f02bd20fcf3450bd421c2fcc9c"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feea693c452d85ea0015ebe3bb9cd15b6f49acc1a31c28b3c50f4db0f8fb1e71"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5da98815f82dce0cb31fd1e873a0cb30934971d15b74e0d78cf21f9e1b05953f"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5adf383c73f2d49ad15ff363a8748319ff84c371eed59ffd0127355d6ea1da"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56856b871146bfead25fbcaed098269d90b744eea5cb32a952df00d542cdd368"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win32.whl", hash = "sha256:943aa74a11f5806ab68278284a4ddd282d3fb348a0e96db9b42cb81bf731acdc"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win_amd64.whl", hash = "sha256:c6c4da4843e0dabde41b8f2e8147438330924114f541949e6318358a56d1875a"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46a3d4e7a472bfff2d28db838669fc437964e8af8df8ee1e4548e92710929adc"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3dd67b5d69794cfe82862c002512683b3db038b99002171f624712fa71aeaa"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61e2e41656a673b777e2f0cbbe545323dbe0d32312f590b1bc09da1de6c2a02"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0315d9125a38026227f559488fe7f7cee1bd2fbc19f9fd637739dc50bb6380b2"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af8ce2d31679006e7b747d30a89cd3ac1ec304c3d4c20973f0f4ad58e2d1c4c9"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:81ba314a08c7ab701e621b7ad079c0c933c58cdef88593c59b90b996e8b58fa5"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win32.whl", hash = "sha256:1ee8bd6d68578e517943f5ebff3afbd93fc65f7ef8f23becab9fa8fb315afb1d"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win_amd64.whl", hash = "sha256:ad7acbe95bac70e4e687a4dc9ae3f7a2f467aa6597049eeb6d4a662ecd990bb6"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d3499008ddec83127ab286c6f6ec82a34f39c9817f020f75eca96155f9765097"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9b66fcd38659cab5d29e8de5409cdf91e9986817703e1078b2fdaad731ea66f5"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea30da1e76cb1acc5b72e204a920a3a7678d9d52f688f087dc08e54e2754c67"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:124202b4e0edea7f08a4db8c81cc7859012f90a0d14ba2bf07c099aff6e96462"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e23b88c69497a6322b5796c0781400692eca1ae5532821b39ce81a48c395aae9"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b6303bfd78fb3221847723104d152e5972c22367ff66edf09120fcde5ddc2e2"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win32.whl", hash = "sha256:a921002be69ac3ab2cf0c3017c4e6a3377f800f1fca7f254c13b5f1a2f10022c"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win_amd64.whl", hash = "sha256:b4a2cf92995635b64876dc141af0ef089c6eea7e05898d8d8865e71a326c0385"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e91b5e341f8c7f1e5020db8e5602f3ed045a29f8e27f7f565e0bdee3338f2c7"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c7b78dfc7278329f27be02c44abc0d69fe235495bb8e16ec7ef1b1a17952db"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3eba73ef2c30695cb7eabcdb33bb3d0b878595737479e152468f3ba97a9c22a4"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5df5d1dafb8eee89384fb7a1f79128118bc0ba50ce0db27a40750f6f91aa99d5"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2858bbab1681ee5406650202950dc8f00e83b06a198741b7c656e63818633526"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win32.whl", hash = "sha256:9461802f2e965de5cff80c5a13bc945abea7edaa1d29360b485c3d2b56cdb075"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win_amd64.whl", hash = "sha256:a6bec1c010a6d65b3ed88c863d56b9ea5eeefdf62b5e39cafd08c65f5ce5198b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:843a882cadebecc655a68bd9a5b8aa39b3c52f4a9a5572a3036fb1bb2ccdc197"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dbb990612c36163c6072723523d2be7c3eb1517bbdd63fe50449f56afafd1133"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7e4baf9161d076b9a7e432fce06217b9bd90cfb8f1d543d6e8c4595627edb9"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0a5354cb4de9b64bccb6ea33162cb83e03dbefa0d892db88a672f5aad638a75"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fffcc8edc508801ed2e6a4e7b0d150a62196fd28b4e16ab9f65192e8186102b6"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aca7b6d99a4541b2ebab4494f6c8c2f947e0df4ac859ced575238e1d6ca5716b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win32.whl", hash = "sha256:8c7f10720fc34d14abad5b647bc8202202f4948498927d9f1b4df0fb1cf391b7"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win_amd64.whl", hash = "sha256:243feb6882b06a2af68ecf4bec8813d99452a1b62ba2be917ce6283852cf701b"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fc4974d3684f28b61b9a90fcb4c41fb340fd4b6a50c04365704a4da5a9603b05"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87724e7ed2a936fdda2c05dbd99d395c91ea3c96f029a033a4a20e008dd876bf"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68722e6a550f5de2e3cfe9da6afb9a7dd15ef7032afa5651b0f0c6b3adb8815d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:328529f7c7f90adcd65aed06a161851f83f475c2f664a898af574893f55d9e53"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:df40c16a7e8be7413b885c9bf900d402918cc848be08a59b022478804ea076b8"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:426f2fa71331a64f5132369ede5171c52fd1df1bd9727ce621f38b5b24f48750"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win32.whl", hash = "sha256:33157920b233bc542ce497a81a2e1452e685a11834c5763933b440fedd1d8e2d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win_amd64.whl", hash = "sha256:2f60843068e432311c886c5f03c4664acaef507cf716f6c60d5fde7265be9d7b"}, + {file = "SQLAlchemy-2.0.28-py3-none-any.whl", hash = "sha256:78bb7e8da0183a8301352d569900d9d3594c48ac21dc1c2ec6b3121ed8b6c986"}, + {file = "SQLAlchemy-2.0.28.tar.gz", hash = "sha256:dd53b6c4e6d960600fd6532b79ee28e2da489322fcf6648738134587faf767b6"}, ] [package.dependencies] @@ -5855,13 +5824,13 @@ full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7 [[package]] name = "streamlit" -version = "1.31.1" +version = "1.32.2" description = "A faster way to build and share data apps" optional = false python-versions = ">=3.8, !=3.9.7" files = [ - {file = "streamlit-1.31.1-py2.py3-none-any.whl", hash = "sha256:a1a84249f7a9b854fe356db06c85dc03c3f9da4df06a33aa5a922647b955e8c8"}, - {file = "streamlit-1.31.1.tar.gz", hash = "sha256:dfc43ca85b4b4c31d097c27b983b8ccc960222ad907862b2b2fb4ddf04c50fdc"}, + {file = "streamlit-1.32.2-py2.py3-none-any.whl", hash = "sha256:a0b8044e76fec364b07be145f8b40dbd8d083e20ebbb189ceb1fa9423f3dedea"}, + {file = "streamlit-1.32.2.tar.gz", hash = "sha256:1258b9cbc3ff957bf7d09b1bfc85cedc308f1065b30748545295a9af8d5577ab"}, ] [package.dependencies] @@ -5870,7 +5839,6 @@ blinker = ">=1.0.0,<2" cachetools = ">=4.0,<6" click = ">=7.0,<9" gitpython = ">=3.0.7,<3.1.19 || >3.1.19,<4" -importlib-metadata = ">=1.4,<8" numpy = ">=1.19.3,<2" packaging = ">=16.8,<24" pandas = ">=1.3.0,<3" @@ -5878,15 +5846,12 @@ pillow = ">=7.1.0,<11" protobuf = ">=3.20,<5" pyarrow = ">=7.0" pydeck = ">=0.8.0b4,<1" -python-dateutil = ">=2.7.3,<3" requests = ">=2.27,<3" rich = ">=10.14.0,<14" tenacity = ">=8.1.0,<9" toml = ">=0.10.1,<2" tornado = ">=6.0.3,<7" typing-extensions = ">=4.3.0,<5" -tzlocal = ">=1.1,<6" -validators = ">=0.2,<1" watchdog = {version = ">=2.1.5", markers = "platform_system != \"Darwin\""} [package.extras] @@ -5953,13 +5918,13 @@ tests = ["pytest", "pytest-cov"] [[package]] name = "terminado" -version = "0.18.0" +version = "0.18.1" description = "Tornado websocket backend for the Xterm.js Javascript terminal emulator library." optional = false python-versions = ">=3.8" files = [ - {file = "terminado-0.18.0-py3-none-any.whl", hash = "sha256:87b0d96642d0fe5f5abd7783857b9cab167f221a39ff98e3b9619a788a3c0f2e"}, - {file = "terminado-0.18.0.tar.gz", hash = "sha256:1ea08a89b835dd1b8c0c900d92848147cef2537243361b2e3f4dc15df9b6fded"}, + {file = "terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0"}, + {file = "terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e"}, ] [package.dependencies] @@ -6076,28 +6041,28 @@ telegram = ["requests"] [[package]] name = "traitlets" -version = "5.14.1" +version = "5.14.2" description = "Traitlets Python configuration system" optional = false python-versions = ">=3.8" files = [ - {file = "traitlets-5.14.1-py3-none-any.whl", hash = "sha256:2e5a030e6eff91737c643231bfcf04a65b0132078dad75e4936700b213652e74"}, - {file = "traitlets-5.14.1.tar.gz", hash = "sha256:8585105b371a04b8316a43d5ce29c098575c2e477850b62b848b964f1444527e"}, + {file = "traitlets-5.14.2-py3-none-any.whl", hash = "sha256:fcdf85684a772ddeba87db2f398ce00b40ff550d1528c03c14dbf6a02003cd80"}, + {file = "traitlets-5.14.2.tar.gz", hash = "sha256:8cdd83c040dab7d1dee822678e5f5d100b514f7b72b01615b26fc5718916fdf9"}, ] [package.extras] docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] -test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<7.5)", "pytest-mock", "pytest-mypy-testing"] +test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.1)", "pytest-mock", "pytest-mypy-testing"] [[package]] name = "trove-classifiers" -version = "2024.2.23" +version = "2024.3.3" description = "Canonical source for classifiers on PyPI (pypi.org)." optional = false python-versions = "*" files = [ - {file = "trove-classifiers-2024.2.23.tar.gz", hash = "sha256:8385160a12aac69c93fff058fb613472ed773a24a27eb3cd4b144cfbdd79f38c"}, - {file = "trove_classifiers-2024.2.23-py3-none-any.whl", hash = "sha256:3094534b8021dc1822aadb1d11d4c7b62a854d464d19458fd0a49d6fe2b68b77"}, + {file = "trove-classifiers-2024.3.3.tar.gz", hash = "sha256:df7edff9c67ff86b733628998330b180e81d125b1e096536d83ac0fd79673fdc"}, + {file = "trove_classifiers-2024.3.3-py3-none-any.whl", hash = "sha256:3a84096861b385ec422c79995d1f6435dde47a9b63adaa3c886e53232ba7e6e0"}, ] [[package]] @@ -6123,13 +6088,13 @@ test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6. [[package]] name = "types-python-dateutil" -version = "2.8.19.20240106" +version = "2.9.0.20240316" description = "Typing stubs for python-dateutil" optional = false python-versions = ">=3.8" files = [ - {file = "types-python-dateutil-2.8.19.20240106.tar.gz", hash = "sha256:1f8db221c3b98e6ca02ea83a58371b22c374f42ae5bbdf186db9c9a76581459f"}, - {file = "types_python_dateutil-2.8.19.20240106-py3-none-any.whl", hash = "sha256:efbbdc54590d0f16152fa103c9879c7d4a00e82078f6e2cf01769042165acaa2"}, + {file = "types-python-dateutil-2.9.0.20240316.tar.gz", hash = "sha256:5d2f2e240b86905e40944dd787db6da9263f0deabef1076ddaed797351ec0202"}, + {file = "types_python_dateutil-2.9.0.20240316-py3-none-any.whl", hash = "sha256:6b8cb66d960771ce5ff974e9dd45e38facb81718cc1e208b10b1baccbfdbee3b"}, ] [[package]] @@ -6154,24 +6119,6 @@ files = [ {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, ] -[[package]] -name = "tzlocal" -version = "5.2" -description = "tzinfo object for the local timezone" -optional = false -python-versions = ">=3.8" -files = [ - {file = "tzlocal-5.2-py3-none-any.whl", hash = "sha256:49816ef2fe65ea8ac19d19aa7a1ae0551c834303d5014c6d5a62e4cbda8047b8"}, - {file = "tzlocal-5.2.tar.gz", hash = "sha256:8d399205578f1a9342816409cc1e46a93ebd5755e39ea2d85334bea911bf0e6e"}, -] - -[package.dependencies] -"backports.zoneinfo" = {version = "*", markers = "python_version < \"3.9\""} -tzdata = {version = "*", markers = "platform_system == \"Windows\""} - -[package.extras] -devenv = ["check-manifest", "pytest (>=4.3)", "pytest-cov", "pytest-mock (>=3.3)", "zest.releaser"] - [[package]] name = "uri-template" version = "1.3.0" @@ -6204,13 +6151,13 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "uvicorn" -version = "0.27.1" +version = "0.28.0" description = "The lightning-fast ASGI server." optional = false python-versions = ">=3.8" files = [ - {file = "uvicorn-0.27.1-py3-none-any.whl", hash = "sha256:5c89da2f3895767472a35556e539fd59f7edbe9b1e9c0e1c99eebeadc61838e4"}, - {file = "uvicorn-0.27.1.tar.gz", hash = "sha256:3d9a267296243532db80c83a959a3400502165ade2c1338dea4e67915fd4745a"}, + {file = "uvicorn-0.28.0-py3-none-any.whl", hash = "sha256:6623abbbe6176204a4226e67607b4d52cc60ff62cda0ff177613645cefa2ece1"}, + {file = "uvicorn-0.28.0.tar.gz", hash = "sha256:cab4473b5d1eaeb5a0f6375ac4bc85007ffc75c3cc1768816d9e5d589857b067"}, ] [package.dependencies] @@ -6221,28 +6168,6 @@ typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} [package.extras] standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] -[[package]] -name = "validators" -version = "0.22.0" -description = "Python Data Validation for Humans™" -optional = false -python-versions = ">=3.8" -files = [ - {file = "validators-0.22.0-py3-none-any.whl", hash = "sha256:61cf7d4a62bbae559f2e54aed3b000cea9ff3e2fdbe463f51179b92c58c9585a"}, - {file = "validators-0.22.0.tar.gz", hash = "sha256:77b2689b172eeeb600d9605ab86194641670cdb73b60afd577142a9397873370"}, -] - -[package.extras] -docs-offline = ["myst-parser (>=2.0.0)", "pypandoc-binary (>=1.11)", "sphinx (>=7.1.1)"] -docs-online = ["mkdocs (>=1.5.2)", "mkdocs-git-revision-date-localized-plugin (>=1.2.0)", "mkdocs-material (>=9.2.6)", "mkdocstrings[python] (>=0.22.0)", "pyaml (>=23.7.0)"] -hooks = ["pre-commit (>=3.3.3)"] -package = ["build (>=1.0.0)", "twine (>=4.0.2)"] -runner = ["tox (>=4.11.1)"] -sast = ["bandit[toml] (>=1.7.5)"] -testing = ["pytest (>=7.4.0)"] -tooling = ["black (>=23.7.0)", "pyright (>=1.1.325)", "ruff (>=0.0.287)"] -tooling-extras = ["pyaml (>=23.7.0)", "pypandoc-binary (>=1.11)", "pytest (>=7.4.0)"] - [[package]] name = "virtualenv" version = "20.25.1" @@ -6723,13 +6648,13 @@ multidict = ">=4.0" [[package]] name = "ydb" -version = "3.8.0" +version = "3.8.1" description = "YDB Python SDK" optional = true python-versions = "*" files = [ - {file = "ydb-3.8.0-py2.py3-none-any.whl", hash = "sha256:29d94f4c6047c24440e11a326604f913f1cf3d909f175329e081ac37f90a9152"}, - {file = "ydb-3.8.0.tar.gz", hash = "sha256:d5bdbc7d069977de86d6ceac62602803696d6c911d685d99edc5a9988710aaf5"}, + {file = "ydb-3.8.1-py2.py3-none-any.whl", hash = "sha256:20546e8b3062043cc1ae46e746b6ea339043fcf511890c4fff6610a1219ab998"}, + {file = "ydb-3.8.1.tar.gz", hash = "sha256:524ef8265fa1bcb532da1bda2958437a71a7fec755dc25dd9892b84de2a8ec65"}, ] [package.dependencies] @@ -6743,18 +6668,18 @@ yc = ["yandexcloud"] [[package]] name = "zipp" -version = "3.17.0" +version = "3.18.1" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ - {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, - {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, + {file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"}, + {file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] [[package]] name = "zope-event" @@ -6837,10 +6762,10 @@ postgresql = ["asyncpg", "sqlalchemy"] redis = ["redis"] sqlite = ["aiosqlite", "sqlalchemy"] stats = ["omegaconf", "opentelemetry-exporter-otlp", "opentelemetry-instrumentation", "requests", "tqdm"] -telegram = ["pytelegrambotapi"] +telegram = ["python-telegram-bot"] ydb = ["six", "ydb"] [metadata] lock-version = "2.0" python-versions = "^3.8.1,!=3.9.7" -content-hash = "bfc9b006110db970767817b6eec3f44194dc36e29748cb319d75876610a2e1a8" +content-hash = "2bab0798ec48c472886dc47a49d9bab5a0af3dfac810cea1eacf322b0a322eb2" From 771370de5e508f14d3fb95b3523277d63e5d4e72 Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 19 Mar 2024 00:34:24 +0100 Subject: [PATCH 015/140] typing fixed --- dff/script/core/message.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dff/script/core/message.py b/dff/script/core/message.py index 1ab3a6382..3878e7c11 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -5,14 +5,15 @@ DFF. It only contains types and properties that are compatible with most messaging services. """ -from typing import Any, Optional, List, Union +from typing import TYPE_CHECKING, Any, Optional, List, Union from enum import Enum, auto from pathlib import Path from urllib.request import urlopen from pydantic import field_validator, Field, FilePath, HttpUrl, BaseModel, model_validator -from dff.messengers.common.interface import MessengerInterface +if TYPE_CHECKING: + from dff.messengers.common.interface import MessengerInterface class Session(Enum): From 1c850528ee8090d329e6b14cade28b9caedf2e51 Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 19 Mar 2024 00:42:47 +0100 Subject: [PATCH 016/140] circular import resolved --- dff/messengers/common/interface.py | 3 ++- dff/script/core/message.py | 5 ++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dff/messengers/common/interface.py b/dff/messengers/common/interface.py index cc1bba876..dca964b9e 100644 --- a/dff/messengers/common/interface.py +++ b/dff/messengers/common/interface.py @@ -12,11 +12,12 @@ import uuid from typing import Optional, Any, List, Tuple, TextIO, Hashable, TYPE_CHECKING -from dff.script import Context, Message +from dff.script import Message from dff.messengers.common.types import PollingInterfaceLoopFunction from dff.script.core.message import DataAttachment if TYPE_CHECKING: + from dff.script import Context from dff.pipeline.types import PipelineRunnerFunction logger = logging.getLogger(__name__) diff --git a/dff/script/core/message.py b/dff/script/core/message.py index 3878e7c11..1ab3a6382 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -5,15 +5,14 @@ DFF. It only contains types and properties that are compatible with most messaging services. """ -from typing import TYPE_CHECKING, Any, Optional, List, Union +from typing import Any, Optional, List, Union from enum import Enum, auto from pathlib import Path from urllib.request import urlopen from pydantic import field_validator, Field, FilePath, HttpUrl, BaseModel, model_validator -if TYPE_CHECKING: - from dff.messengers.common.interface import MessengerInterface +from dff.messengers.common.interface import MessengerInterface class Session(Enum): From 0cebb97def18331fc5e4bb670e6213b580436568 Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 19 Mar 2024 00:58:44 +0100 Subject: [PATCH 017/140] one more attempt to fix circular import --- dff/messengers/common/interface.py | 12 +++++++----- dff/script/core/message.py | 9 ++++++--- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/dff/messengers/common/interface.py b/dff/messengers/common/interface.py index dca964b9e..ae46052b7 100644 --- a/dff/messengers/common/interface.py +++ b/dff/messengers/common/interface.py @@ -12,13 +12,11 @@ import uuid from typing import Optional, Any, List, Tuple, TextIO, Hashable, TYPE_CHECKING -from dff.script import Message -from dff.messengers.common.types import PollingInterfaceLoopFunction -from dff.script.core.message import DataAttachment - if TYPE_CHECKING: - from dff.script import Context + from dff.script import Context, Message from dff.pipeline.types import PipelineRunnerFunction + from dff.messengers.common.types import PollingInterfaceLoopFunction + from dff.script.core.message import DataAttachment logger = logging.getLogger(__name__) @@ -174,11 +172,15 @@ def __init__( self._descriptor: Optional[TextIO] = out_descriptor def _request(self) -> List[Tuple[Message, Any]]: + from dff.script import Message return [(Message(input(self._prompt_request)), self._ctx_id)] def _respond(self, responses: List[Context]): print(f"{self._prompt_response}{responses[0].last_response.text}", file=self._descriptor) + async def populate_attachment(self, attachment: DataAttachment) -> None: # pragma: no cover + pass + async def connect(self, pipeline_runner: PipelineRunnerFunction, **kwargs): """ The CLIProvider generates new dialog id used to user identification on each `connect` call. diff --git a/dff/script/core/message.py b/dff/script/core/message.py index 1ab3a6382..d81b83c11 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -105,11 +105,14 @@ class DataAttachment(Attachment): source: Optional[Union[HttpUrl, FilePath]] = None id: Optional[str] = None # id field is made separate to simplify type validation title: Optional[str] = None - from_messenger_interface: MessengerInterface = Field(exclude=True) + from_messenger_interface: Optional[MessengerInterface] = Field(exclude=True, default=None) async def get_bytes(self) -> Optional[bytes]: if self.source is None: - await self.from_messenger_interface.populate_attachment(self) + if self.from_messenger_interface is None: + return None + else: + await self.from_messenger_interface.populate_attachment(self) if isinstance(self.source, Path): with open(self.source, "rb") as file: return file.read() @@ -227,7 +230,7 @@ def __init__( self, text: Optional[str] = None, commands: Optional[List[Command]] = None, - attachments: Optional[Attachments] = None, + attachments: Optional[Attachment] = None, annotations: Optional[dict] = None, misc: Optional[dict] = None, **kwargs, From a5f0e02c0be9fc30e9c3a8071083f769de982df5 Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 19 Mar 2024 01:09:38 +0100 Subject: [PATCH 018/140] populate attachment method made implemented --- dff/messengers/common/interface.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/dff/messengers/common/interface.py b/dff/messengers/common/interface.py index ae46052b7..faa6324fa 100644 --- a/dff/messengers/common/interface.py +++ b/dff/messengers/common/interface.py @@ -41,9 +41,9 @@ async def connect(self, pipeline_runner: PipelineRunnerFunction): """ raise NotImplementedError - @abc.abstractmethod async def populate_attachment(self, attachment: DataAttachment) -> None: - raise NotImplementedError + if attachment.source is None: + raise NotImplementedError class PollingMessengerInterface(MessengerInterface): @@ -178,9 +178,6 @@ def _request(self) -> List[Tuple[Message, Any]]: def _respond(self, responses: List[Context]): print(f"{self._prompt_response}{responses[0].last_response.text}", file=self._descriptor) - async def populate_attachment(self, attachment: DataAttachment) -> None: # pragma: no cover - pass - async def connect(self, pipeline_runner: PipelineRunnerFunction, **kwargs): """ The CLIProvider generates new dialog id used to user identification on each `connect` call. From 01947ddcc4e75f45e5329db2183804f1d1aa6ae7 Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 19 Mar 2024 20:05:19 +0100 Subject: [PATCH 019/140] CLI interface separated --- dff/messengers/common/__init__.py | 2 +- dff/messengers/common/interface.py | 44 +---------------- dff/messengers/console.py | 47 +++++++++++++++++++ dff/pipeline/pipeline/pipeline.py | 3 +- tests/pipeline/test_messenger_interface.py | 3 +- .../pipeline/2_pre_and_post_processors.py | 2 +- .../3_pipeline_dict_with_services_full.py | 2 +- 7 files changed, 55 insertions(+), 48 deletions(-) create mode 100644 dff/messengers/console.py diff --git a/dff/messengers/common/__init__.py b/dff/messengers/common/__init__.py index d9c66d921..8bcf7eaa7 100644 --- a/dff/messengers/common/__init__.py +++ b/dff/messengers/common/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- -from .interface import MessengerInterface, PollingMessengerInterface, CallbackMessengerInterface, CLIMessengerInterface +from .interface import MessengerInterface, PollingMessengerInterface, CallbackMessengerInterface from .types import PollingInterfaceLoopFunction diff --git a/dff/messengers/common/interface.py b/dff/messengers/common/interface.py index faa6324fa..e7cd902a6 100644 --- a/dff/messengers/common/interface.py +++ b/dff/messengers/common/interface.py @@ -9,8 +9,7 @@ import abc import asyncio import logging -import uuid -from typing import Optional, Any, List, Tuple, TextIO, Hashable, TYPE_CHECKING +from typing import Optional, Any, List, Tuple, Hashable, TYPE_CHECKING if TYPE_CHECKING: from dff.script import Context, Message @@ -149,44 +148,3 @@ def on_request( This method has the same signature as :py:class:`~dff.pipeline.types.PipelineRunnerFunction`. """ return asyncio.run(self.on_request_async(request, ctx_id, update_ctx_misc)) - - -class CLIMessengerInterface(PollingMessengerInterface): - """ - Command line message interface is the default message interface, communicating with user via `STDIN/STDOUT`. - This message interface can maintain dialog with one user at a time only. - """ - - def __init__( - self, - intro: Optional[str] = None, - prompt_request: str = "request: ", - prompt_response: str = "response: ", - out_descriptor: Optional[TextIO] = None, - ): - super().__init__() - self._ctx_id: Optional[Hashable] = None - self._intro: Optional[str] = intro - self._prompt_request: str = prompt_request - self._prompt_response: str = prompt_response - self._descriptor: Optional[TextIO] = out_descriptor - - def _request(self) -> List[Tuple[Message, Any]]: - from dff.script import Message - return [(Message(input(self._prompt_request)), self._ctx_id)] - - def _respond(self, responses: List[Context]): - print(f"{self._prompt_response}{responses[0].last_response.text}", file=self._descriptor) - - async def connect(self, pipeline_runner: PipelineRunnerFunction, **kwargs): - """ - The CLIProvider generates new dialog id used to user identification on each `connect` call. - - :param pipeline_runner: A function that should process user request and return context; - usually it's a :py:meth:`~dff.pipeline.pipeline.pipeline.Pipeline._run_pipeline` function. - :param \\**kwargs: argument, added for compatibility with super class, it shouldn't be used normally. - """ - self._ctx_id = uuid.uuid4() - if self._intro is not None: - print(self._intro) - await super().connect(pipeline_runner, **kwargs) diff --git a/dff/messengers/console.py b/dff/messengers/console.py new file mode 100644 index 000000000..ab30669ad --- /dev/null +++ b/dff/messengers/console.py @@ -0,0 +1,47 @@ +from typing import Any, Hashable, List, Optional, TextIO, Tuple +from uuid import uuid4 +from dff.messengers.common.interface import PollingMessengerInterface +from dff.pipeline.types import PipelineRunnerFunction +from dff.script.core.context import Context +from dff.script.core.message import Message + + +class CLIMessengerInterface(PollingMessengerInterface): + """ + Command line message interface is the default message interface, communicating with user via `STDIN/STDOUT`. + This message interface can maintain dialog with one user at a time only. + """ + + def __init__( + self, + intro: Optional[str] = None, + prompt_request: str = "request: ", + prompt_response: str = "response: ", + out_descriptor: Optional[TextIO] = None, + ): + super().__init__() + self._ctx_id: Optional[Hashable] = None + self._intro: Optional[str] = intro + self._prompt_request: str = prompt_request + self._prompt_response: str = prompt_response + self._descriptor: Optional[TextIO] = out_descriptor + + def _request(self) -> List[Tuple[Message, Any]]: + from dff.script import Message + return [(Message(input(self._prompt_request)), self._ctx_id)] + + def _respond(self, responses: List[Context]): + print(f"{self._prompt_response}{responses[0].last_response.text}", file=self._descriptor) + + async def connect(self, pipeline_runner: PipelineRunnerFunction, **kwargs): + """ + The CLIProvider generates new dialog id used to user identification on each `connect` call. + + :param pipeline_runner: A function that should process user request and return context; + usually it's a :py:meth:`~dff.pipeline.pipeline.pipeline.Pipeline._run_pipeline` function. + :param \\**kwargs: argument, added for compatibility with super class, it shouldn't be used normally. + """ + self._ctx_id = uuid4() + if self._intro is not None: + print(self._intro) + await super().connect(pipeline_runner, **kwargs) diff --git a/dff/pipeline/pipeline/pipeline.py b/dff/pipeline/pipeline/pipeline.py index 94eb52a57..84c22b8a7 100644 --- a/dff/pipeline/pipeline/pipeline.py +++ b/dff/pipeline/pipeline/pipeline.py @@ -23,7 +23,8 @@ from dff.script import NodeLabel2Type, Message from dff.utils.turn_caching import cache_clear -from dff.messengers.common import MessengerInterface, CLIMessengerInterface +from dff.messengers.console import CLIMessengerInterface +from dff.messengers.common import MessengerInterface from ..service.group import ServiceGroup from ..types import ( ServiceBuilder, diff --git a/tests/pipeline/test_messenger_interface.py b/tests/pipeline/test_messenger_interface.py index 855efeb96..2c6459386 100644 --- a/tests/pipeline/test_messenger_interface.py +++ b/tests/pipeline/test_messenger_interface.py @@ -3,7 +3,8 @@ import pathlib from dff.script import RESPONSE, TRANSITIONS, Message -from dff.messengers.common import CLIMessengerInterface, CallbackMessengerInterface +from dff.messengers.console import CLIMessengerInterface +from dff.messengers.common import CallbackMessengerInterface from dff.pipeline import Pipeline import dff.script.conditions as cnd diff --git a/tutorials/pipeline/2_pre_and_post_processors.py b/tutorials/pipeline/2_pre_and_post_processors.py index bc8fe5625..7037d63da 100644 --- a/tutorials/pipeline/2_pre_and_post_processors.py +++ b/tutorials/pipeline/2_pre_and_post_processors.py @@ -14,7 +14,7 @@ # %% import logging -from dff.messengers.common import CLIMessengerInterface +from dff.messengers.console import CLIMessengerInterface from dff.script import Context, Message from dff.pipeline import Pipeline diff --git a/tutorials/pipeline/3_pipeline_dict_with_services_full.py b/tutorials/pipeline/3_pipeline_dict_with_services_full.py index e24999ab9..34070c34f 100644 --- a/tutorials/pipeline/3_pipeline_dict_with_services_full.py +++ b/tutorials/pipeline/3_pipeline_dict_with_services_full.py @@ -19,7 +19,7 @@ import urllib.request from dff.script import Context -from dff.messengers.common import CLIMessengerInterface +from dff.messengers.console import CLIMessengerInterface from dff.pipeline import Service, Pipeline, ServiceRuntimeInfo, ACTOR from dff.utils.testing.common import ( check_happy_path, From 5e35935c5bb848a5b82205ce755c4ff4f9774290 Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 19 Mar 2024 20:15:40 +0100 Subject: [PATCH 020/140] data message equation fixed --- dff/script/core/message.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dff/script/core/message.py b/dff/script/core/message.py index d81b83c11..f61268b44 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -128,6 +128,9 @@ def __eq__(self, other): return False if self.title != other.title: return False + if self.from_messenger_interface is not None and other.from_messenger_interface is not None: + return self.get_bytes() == other.get_bytes() + return True return NotImplemented @model_validator(mode="before") From 513f77ad2c400be9021695c9cb92701c710564b3 Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 19 Mar 2024 20:40:22 +0100 Subject: [PATCH 021/140] lint applied --- dff/messengers/common/conditions.py | 6 +- dff/messengers/console.py | 1 - dff/messengers/telegram.py | 138 +++++++++++++++--- dff/script/core/message.py | 6 - tutorials/messengers/telegram/2_buttons.py | 16 +- .../telegram/3_buttons_with_callback.py | 4 +- tutorials/messengers/telegram/4_conditions.py | 39 ++--- .../telegram/5_conditions_with_media.py | 79 +++++++--- .../telegram/6_conditions_extras.py | 39 ++++- 9 files changed, 240 insertions(+), 88 deletions(-) diff --git a/dff/messengers/common/conditions.py b/dff/messengers/common/conditions.py index f5534f993..dd978ae42 100644 --- a/dff/messengers/common/conditions.py +++ b/dff/messengers/common/conditions.py @@ -1,14 +1,16 @@ from dff.pipeline import Pipeline from dff.script import Context +from dff.script.core.message import CallbackQuery def has_callback_query(expected: str): - def condition(ctx: Context, _: Pipeline, *__, **___) -> bool: # pragma: no cover last_request = ctx.last_request if last_request is None or last_request.attachments is None or len(last_request.attachments) == 0: return False - callback_query = next((attachment for attachment in last_request.attachments if isinstance(attachment, CallbackQuery)), None) + callback_query = next( + (attachment for attachment in last_request.attachments if isinstance(attachment, CallbackQuery)), None + ) if callback_query is None: return False return callback_query.query_string == expected diff --git a/dff/messengers/console.py b/dff/messengers/console.py index ab30669ad..3c6ae2ede 100644 --- a/dff/messengers/console.py +++ b/dff/messengers/console.py @@ -27,7 +27,6 @@ def __init__( self._descriptor: Optional[TextIO] = out_descriptor def _request(self) -> List[Tuple[Message, Any]]: - from dff.script import Message return [(Message(input(self._prompt_request)), self._ctx_id)] def _respond(self, responses: List[Context]): diff --git a/dff/messengers/telegram.py b/dff/messengers/telegram.py index edc783666..c1031b144 100644 --- a/dff/messengers/telegram.py +++ b/dff/messengers/telegram.py @@ -6,18 +6,42 @@ """ from pathlib import Path from tempfile import gettempdir -from typing import Callable, Optional, Sequence, cast +from typing import Callable, Optional, Sequence from pydantic import FilePath -from telegram import InlineKeyboardButton, InlineKeyboardMarkup, InputMediaAnimation, InputMediaAudio, InputMediaDocument, InputMediaPhoto, InputMediaVideo, Update, Message as TelegramMessage +from telegram import ( + InlineKeyboardButton, + InlineKeyboardMarkup, + InputMediaAnimation, + InputMediaAudio, + InputMediaDocument, + InputMediaPhoto, + InputMediaVideo, + Update, + Message as TelegramMessage, +) from telegram.ext import Application, ExtBot, MessageHandler, CallbackQueryHandler, ContextTypes from telegram.ext.filters import ALL from dff.messengers.common import MessengerInterface -from dff.pipeline import Pipeline from dff.pipeline.types import PipelineRunnerFunction -from dff.script.core.context import Context -from dff.script.core.message import Animation, Audio, Button, CallbackQuery, Contact, DataAttachment, Document, Image, Invoice, Keyboard, Location, Message, Poll, PollOption, Video +from dff.script.core.message import ( + Animation, + Audio, + Button, + CallbackQuery, + Contact, + DataAttachment, + Document, + Image, + Invoice, + Keyboard, + Location, + Message, + Poll, + PollOption, + Video, +) class _AbstractTelegramInterface(MessengerInterface): # pragma: no cover @@ -47,44 +71,102 @@ async def extract_message_from_telegram(self, update: TelegramMessage) -> Messag if update.location is not None: message.attachments += [Location(latitude=update.location.latitude, longitude=update.location.longitude)] if update.contact is not None: - message.attachments += [Contact(phone_number=update.contact.phone_number, first_name=update.contact.first_name, last_name=update.contact.last_name)] + message.attachments += [ + Contact( + phone_number=update.contact.phone_number, + first_name=update.contact.first_name, + last_name=update.contact.last_name, + ) + ] if update.invoice is not None: - message.attachments += [Invoice(title=update.invoice.title, description=update.invoice.description, currency=update.invoice.currency, amount=update.invoice.total_amount)] + message.attachments += [ + Invoice( + title=update.invoice.title, + description=update.invoice.description, + currency=update.invoice.currency, + amount=update.invoice.total_amount, + ) + ] if update.poll is not None: - message.attachments += [Poll(question=update.poll.question, options=[PollOption(text=option.text, votes=option.voter_count) for option in update.poll.options])] + message.attachments += [ + Poll( + question=update.poll.question, + options=[PollOption(text=option.text, votes=option.voter_count) for option in update.poll.options], + ) + ] if update.audio is not None: - message.attachments += [Audio(id=update.audio.file_id, title=update.audio.file_unique_id, from_messenger_interface=self)] + message.attachments += [ + Audio(id=update.audio.file_id, title=update.audio.file_unique_id, from_messenger_interface=self) + ] if update.video is not None: - message.attachments += [Video(id=update.video.file_id, title=update.video.file_unique_id, from_messenger_interface=self)] + message.attachments += [ + Video(id=update.video.file_id, title=update.video.file_unique_id, from_messenger_interface=self) + ] if update.animation is not None: - message.attachments += [Animation(id=update.animation.file_id, title=update.animation.file_unique_id, from_messenger_interface=self)] + message.attachments += [ + Animation( + id=update.animation.file_id, title=update.animation.file_unique_id, from_messenger_interface=self + ) + ] if len(update.photo) > 0: - message.attachments += [Image(id=picture.file_id, title=picture.file_unique_id, from_messenger_interface=self) for picture in update.photo] + message.attachments += [ + Image(id=picture.file_id, title=picture.file_unique_id, from_messenger_interface=self) + for picture in update.photo + ] if update.document is not None: - message.attachments += [Document(id=update.document.file_id, title=update.document.file_unique_id, from_messenger_interface=self)] + message.attachments += [ + Document( + id=update.document.file_id, title=update.document.file_unique_id, from_messenger_interface=self + ) + ] return message - def _create_keyboard(self, buttons: Sequence[Sequence[Button]]) -> Optional[InlineKeyboardMarkup]: # pragma: no cover + def _create_keyboard( + self, buttons: Sequence[Sequence[Button]] + ) -> Optional[InlineKeyboardMarkup]: # pragma: no cover button_list = None if len(buttons) > 0: - button_list = [[InlineKeyboardButton(text=button.text, callback_data=button.data if button.data is not None else button.text) for button in row] for row in buttons] + button_list = [ + [ + InlineKeyboardButton( + text=button.text, callback_data=button.data if button.data is not None else button.text + ) + for button in row + ] + for row in buttons + ] if button_list is None: return None else: return InlineKeyboardMarkup(button_list) - async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, message: Message) -> None: # pragma: no cover + async def cast_message_to_telegram_and_send( + self, bot: ExtBot, chat_id: int, message: Message + ) -> None: # pragma: no cover buttons = list() if message.attachments is not None: files = list() for attachment in message.attachments: if isinstance(attachment, Location): - await bot.send_location(chat_id, attachment.latitude, attachment.longitude, reply_markup=self._create_keyboard(buttons)) + await bot.send_location( + chat_id, attachment.latitude, attachment.longitude, reply_markup=self._create_keyboard(buttons) + ) if isinstance(attachment, Contact): - await bot.send_contact(chat_id, attachment.phone_number, attachment.first_name, attachment.last_name, reply_markup=self._create_keyboard(buttons)) + await bot.send_contact( + chat_id, + attachment.phone_number, + attachment.first_name, + attachment.last_name, + reply_markup=self._create_keyboard(buttons), + ) if isinstance(attachment, Poll): - await bot.send_poll(chat_id, attachment.question, [option.text for option in attachment.options], reply_markup=self._create_keyboard(buttons)) + await bot.send_poll( + chat_id, + attachment.question, + [option.text for option in attachment.options], + reply_markup=self._create_keyboard(buttons), + ) if isinstance(attachment, Audio): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: @@ -113,19 +195,25 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes if message.text is not None: await bot.send_message(chat_id, message.text, reply_markup=self._create_keyboard(buttons)) - async def _on_event(self, update: Update, _: ContextTypes.DEFAULT_TYPE, create_message: Callable[[Update], Message]) -> None: + async def _on_event( + self, update: Update, _: ContextTypes.DEFAULT_TYPE, create_message: Callable[[Update], Message] + ) -> None: if update.effective_chat is not None and update.message is not None: message = create_message(update) message.original_message = update resp = await self.callback(message, update.effective_chat.id) if resp.last_response is not None: - await self.cast_message_to_telegram_and_send(self.application.bot, update.effective_chat.id, resp.last_response) + await self.cast_message_to_telegram_and_send( + self.application.bot, update.effective_chat.id, resp.last_response + ) async def on_message(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> None: await self._on_event(update, _, lambda u: await self.extract_message_from_telegram(u.message)) async def on_callback(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> None: - await self._on_event(update, _, lambda u: Message(attachments=[CallbackQuery(query_string=u.callback_query.data)])) + await self._on_event( + update, _, lambda u: Message(attachments=[CallbackQuery(query_string=u.callback_query.data)]) + ) async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs): self.callback = pipeline_runner @@ -139,7 +227,9 @@ def __init__(self, token: str, interval: int = 2, timeout: int = 20) -> None: async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs): await super().connect(pipeline_runner, *args, **kwargs) - self.application.run_polling(poll_interval=self.interval, timeout=self.timeout, allowed_updates=Update.ALL_TYPES) + self.application.run_polling( + poll_interval=self.interval, timeout=self.timeout, allowed_updates=Update.ALL_TYPES + ) class CallbackTelegramInterface(_AbstractTelegramInterface): # pragma: no cover @@ -147,7 +237,7 @@ def __init__(self, token: str, host: str = "localhost", port: int = 844): super().__init__(token) self.listen = host self.port = port - + async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs): await super().connect(pipeline_runner, *args, **kwargs) self.application.run_webhook(listen=self.listen, port=self.port, allowed_updates=Update.ALL_TYPES) diff --git a/dff/script/core/message.py b/dff/script/core/message.py index f61268b44..842ea11b7 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -42,12 +42,10 @@ class Command(DataModel): class Attachment(DataModel): - pass class CallbackQuery(Attachment): - query_string: Optional[str] @@ -70,14 +68,12 @@ def __eq__(self, other): class Contact(Attachment): - phone_number: str first_name: str last_name: Optional[str] class Invoice(Attachment): - title: str description: str currency: str @@ -85,13 +81,11 @@ class Invoice(Attachment): class PollOption(DataModel): - text: str votes: int class Poll(Attachment): - question: str options: List[PollOption] diff --git a/tutorials/messengers/telegram/2_buttons.py b/tutorials/messengers/telegram/2_buttons.py index 2c1ae78ed..6ce6c768c 100644 --- a/tutorials/messengers/telegram/2_buttons.py +++ b/tutorials/messengers/telegram/2_buttons.py @@ -77,7 +77,7 @@ class is used to represent telegram message, ], ], ), - ] + ], ), TRANSITIONS: { ("general", "success"): has_callback_query("4"), @@ -89,7 +89,9 @@ class is used to represent telegram message, TRANSITIONS: {("root", "fallback"): cnd.true()}, }, "fail": { - RESPONSE: Message(text="Incorrect answer, type anything to try again"), + RESPONSE: Message( + text="Incorrect answer, type anything to try again" + ), TRANSITIONS: {("general", "native_keyboard"): cnd.true()}, }, }, @@ -103,7 +105,7 @@ class is used to represent telegram message, Message(text="/start"), Message( text="Question: What's 2 + 2?", - attachments= [ + attachments=[ Keyboard( buttons=[ [ @@ -123,7 +125,7 @@ class is used to represent telegram message, Message(text="ok"), Message( text="Question: What's 2 + 2?", - attachments= [ + attachments=[ Keyboard( buttons=[ [ @@ -141,15 +143,13 @@ class is used to represent telegram message, ), ( Message(text="Yay!"), - Message( - text="Finishing test, send /restart command to restart" - ), + Message(text="Finishing test, send /restart command to restart"), ), ( Message(text="/start"), Message( text="Question: What's 2 + 2?", - attachments= [ + attachments=[ Keyboard( buttons=[ [ diff --git a/tutorials/messengers/telegram/3_buttons_with_callback.py b/tutorials/messengers/telegram/3_buttons_with_callback.py index cd84c016f..acab18508 100644 --- a/tutorials/messengers/telegram/3_buttons_with_callback.py +++ b/tutorials/messengers/telegram/3_buttons_with_callback.py @@ -143,9 +143,7 @@ class is used to represent telegram message, ), ( Message(text="Yay!"), - Message( - text="Finishing test, send /restart command to restart" - ), + Message(text="Finishing test, send /restart command to restart"), ), ( Message(text="/restart"), diff --git a/tutorials/messengers/telegram/4_conditions.py b/tutorials/messengers/telegram/4_conditions.py index 69641a27c..40a245b4f 100644 --- a/tutorials/messengers/telegram/4_conditions.py +++ b/tutorials/messengers/telegram/4_conditions.py @@ -20,6 +20,7 @@ import dff.script.conditions as cnd from dff.messengers.telegram import PollingTelegramInterface from dff.pipeline import Pipeline +from dff.script.core.context import Context from dff.script.core.message import Message from dff.utils.testing.common import is_interactive_mode @@ -50,6 +51,21 @@ """ +# %% +def check_if_latest_message_test_has_music( + ctx: Context, _: Pipeline, __, ___ +) -> bool: + if ctx.last_request is None: + return False + if ctx.last_request.original_message is None: + return False + if ctx.last_request.original_message.message is None: + return False + if ctx.last_request.original_message.message.text is None: + return False + return "music" in ctx.last_request.original_message.message.text + + # %% script = { "greeting_flow": { @@ -65,28 +81,15 @@ }, "node1": { RESPONSE: Message(text="Hi, how are you?"), - TRANSITIONS: { - "node2": cnd.regexp("fine") - }, + TRANSITIONS: {"node2": cnd.regexp("fine")}, }, "node2": { - RESPONSE: Message( - text="Good. What do you want to talk about?" - ), - TRANSITIONS: { - "node3": lambda ctx, _, __, ___: - ctx.last_request.original_message.message is not None - and ctx.last_request.original_message.message.text is not None - and "music" in ctx.last_request.original_message.message.text - }, + RESPONSE: Message(text="Good. What do you want to talk about?"), + TRANSITIONS: {"node3": check_if_latest_message_test_has_music}, }, "node3": { - RESPONSE: Message( - text="Sorry, I can not talk about music now." - ), - TRANSITIONS: { - "node4": lambda _, __, ___, ____: True - }, + RESPONSE: Message(text="Sorry, I can not talk about music now."), + TRANSITIONS: {"node4": lambda _, __, ___, ____: True}, # This condition is true for any type of update }, "node4": { diff --git a/tutorials/messengers/telegram/5_conditions_with_media.py b/tutorials/messengers/telegram/5_conditions_with_media.py index 67ecaaebb..a20d19715 100644 --- a/tutorials/messengers/telegram/5_conditions_with_media.py +++ b/tutorials/messengers/telegram/5_conditions_with_media.py @@ -22,6 +22,7 @@ import dff.script.conditions as cnd from dff.script import TRANSITIONS, RESPONSE +from dff.script.core.context import Context from dff.script.core.message import Message, Image from dff.messengers.telegram import PollingTelegramInterface from dff.pipeline import Pipeline @@ -30,13 +31,16 @@ # %% -picture_url = HttpUrl("https://avatars.githubusercontent.com/u/29918795?s=200&v=4") +picture_url = HttpUrl( + "https://avatars.githubusercontent.com/u/29918795?s=200&v=4" +) # %% [markdown] """ To filter user messages depending on whether or not media files were sent, -you can use the `content_types` parameter of the `ctx.last_request.original_message.message.document`. +you can use the `content_types` parameter of the +`Context.last_request.original_message.message.document`. """ @@ -44,6 +48,49 @@ interface = PollingTelegramInterface(token=os.environ["TG_BOT_TOKEN"]) +def check_if_latest_message_has_photos( + ctx: Context, _: Pipeline, __, ___ +) -> bool: + if ctx.last_request is None: + return False + if ctx.last_request.original_message is None: + return False + if ctx.last_request.original_message.message is None: + return False + if ctx.last_request.original_message.message.photo is None: + return False + return len(ctx.last_request.original_message.message.photo) > 0 + + +def check_if_latest_message_has_image_documents( + ctx: Context, _: Pipeline, __, ___ +) -> bool: + if ctx.last_request is None: + return False + if ctx.last_request.original_message is None: + return False + if ctx.last_request.original_message.message is None: + return False + if ctx.last_request.original_message.message.document is None: + return False + return ( + ctx.last_request.original_message.message.document.mime_type + == "image/jpeg" + ) + + +def check_if_latest_message_has_text( + ctx: Context, _: Pipeline, __, ___ +) -> bool: + if ctx.last_request is None: + return False + if ctx.last_request.original_message is None: + return False + if ctx.last_request.original_message.message is None: + return False + return ctx.last_request.original_message.message.text is None + + # %% script = { "root": { @@ -81,18 +128,14 @@ # both in 'photo' and 'document' fields. # We should consider both cases # when we check the message for media. - lambda ctx, _, __, ___: - ctx.last_request.original_message.message is not None - and len(ctx.last_request.original_message.message.photo) > 0, - lambda ctx, _, __, ___: - ctx.last_request.original_message.message is not None - and ctx.last_request.original_message.message.document is not None - and ctx.last_request.original_message.message.document.mime_type == "image/jpeg", + check_if_latest_message_has_photos, + check_if_latest_message_has_image_documents, ] ), - ("pics", "send_many"): lambda ctx, _, __, ___: - ctx.last_request.original_message.message is not None - and ctx.last_request.original_message.message.text is not None, + ( + "pics", + "send_many", + ): check_if_latest_message_has_text, ("pics", "ask_picture"): cnd.true(), }, }, @@ -123,9 +166,7 @@ Message(text="Send me a picture"), ), ( - Message( - attachments=[Image(source=picture_url)] - ), + Message(attachments=[Image(source=picture_url)]), Message( text="Here's my picture!", attachments=[Image(source=picture_url)], @@ -133,9 +174,7 @@ ), ( Message(text="ok"), - Message( - text="Finishing test, send /restart command to restart" - ), + Message(text="Finishing test, send /restart command to restart"), ), ( Message(text="/restart"), @@ -150,9 +189,7 @@ ), ( Message(text="ok"), - Message( - text="Finishing test, send /restart command to restart" - ), + Message(text="Finishing test, send /restart command to restart"), ), ( Message(text="/restart"), diff --git a/tutorials/messengers/telegram/6_conditions_extras.py b/tutorials/messengers/telegram/6_conditions_extras.py index 7ed535e0a..09a9ba3ed 100644 --- a/tutorials/messengers/telegram/6_conditions_extras.py +++ b/tutorials/messengers/telegram/6_conditions_extras.py @@ -21,6 +21,7 @@ import dff.script.conditions as cnd from dff.messengers.telegram import PollingTelegramInterface from dff.pipeline import Pipeline +from dff.script.core.context import Context from dff.script.core.message import Message from dff.utils.testing.common import is_interactive_mode @@ -55,17 +56,45 @@ """ +# %% +def check_if_latest_message_is_new_chat_member( + ctx: Context, _: Pipeline, __, ___ +) -> bool: + if ctx.last_request is None: + return False + if ctx.last_request.original_message is None: + return False + if ctx.last_request.original_message.message is None: + return False + return ( + ctx.last_request.original_message.message.new_chat_members is not None + ) + + +def check_if_latest_message_is_callback_query( + ctx: Context, _: Pipeline, __, ___ +) -> bool: + if ctx.last_request is None: + return False + if ctx.last_request.original_message is None: + return False + return ctx.last_request.original_message.inline_query is not None + + # %% script = { GLOBAL: { TRANSITIONS: { # say hi when someone enters the chat - ("greeting_flow", "node1"): lambda ctx, _, __, ___: - ctx.last_request.original_message.message is not None - and ctx.last_request.original_message.message.new_chat_members is not None, + ( + "greeting_flow", + "node1", + ): check_if_latest_message_is_new_chat_member, # send a message when inline query is received - ("greeting_flow", "node2"): lambda ctx, _, __, ___: - ctx.last_request.original_message.inline_query is not None, + ( + "greeting_flow", + "node2", + ): check_if_latest_message_is_callback_query, }, }, "greeting_flow": { From 48bb29f0fd93b9f0cc0a60a936237f1904101662 Mon Sep 17 00:00:00 2001 From: Roman Zlobin Date: Wed, 20 Mar 2024 13:10:18 +0300 Subject: [PATCH 022/140] move callback query to script.conditions --- dff/messengers/common/conditions.py | 18 ------------------ dff/script/conditions/std_conditions.py | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 18 deletions(-) delete mode 100644 dff/messengers/common/conditions.py diff --git a/dff/messengers/common/conditions.py b/dff/messengers/common/conditions.py deleted file mode 100644 index dd978ae42..000000000 --- a/dff/messengers/common/conditions.py +++ /dev/null @@ -1,18 +0,0 @@ -from dff.pipeline import Pipeline -from dff.script import Context -from dff.script.core.message import CallbackQuery - - -def has_callback_query(expected: str): - def condition(ctx: Context, _: Pipeline, *__, **___) -> bool: # pragma: no cover - last_request = ctx.last_request - if last_request is None or last_request.attachments is None or len(last_request.attachments) == 0: - return False - callback_query = next( - (attachment for attachment in last_request.attachments if isinstance(attachment, CallbackQuery)), None - ) - if callback_query is None: - return False - return callback_query.query_string == expected - - return condition diff --git a/dff/script/conditions/std_conditions.py b/dff/script/conditions/std_conditions.py index d2d23f4e8..c71c600d4 100644 --- a/dff/script/conditions/std_conditions.py +++ b/dff/script/conditions/std_conditions.py @@ -17,6 +17,7 @@ from dff.pipeline import Pipeline from dff.script import NodeLabel2Type, Context, Message +from dff.script.core.message import CallbackQuery logger = logging.getLogger(__name__) @@ -242,3 +243,16 @@ def false_handler(ctx: Context, pipeline: Pipeline) -> bool: """ :py:func:`~neg` is an alias for :py:func:`~negation`. """ + + +def has_callback_query(expected_query_string: str): + """ + Condition that checks if :py:attr:`~.CallbackQuery.query_string` of the last message matches `expected`. + """ + def has_callback_query_handler(ctx: Context, _: Pipeline) -> bool: + last_request = ctx.last_request + if last_request is None or last_request.attachments is None: + return False + return CallbackQuery(query_string=expected_query_string) in last_request.attachments + + return has_callback_query_handler From e2ea3715f0d2c3ec2c21b9e16ac34a7b6f354121 Mon Sep 17 00:00:00 2001 From: pseusys Date: Thu, 21 Mar 2024 10:14:01 +0100 Subject: [PATCH 023/140] condition signature fix --- tutorials/messengers/telegram/4_conditions.py | 4 +--- .../messengers/telegram/5_conditions_with_media.py | 12 +++--------- tutorials/messengers/telegram/6_conditions_extras.py | 8 ++------ 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/tutorials/messengers/telegram/4_conditions.py b/tutorials/messengers/telegram/4_conditions.py index 40a245b4f..ae2bb285d 100644 --- a/tutorials/messengers/telegram/4_conditions.py +++ b/tutorials/messengers/telegram/4_conditions.py @@ -52,9 +52,7 @@ # %% -def check_if_latest_message_test_has_music( - ctx: Context, _: Pipeline, __, ___ -) -> bool: +def check_if_latest_message_test_has_music(ctx: Context, _: Pipeline) -> bool: if ctx.last_request is None: return False if ctx.last_request.original_message is None: diff --git a/tutorials/messengers/telegram/5_conditions_with_media.py b/tutorials/messengers/telegram/5_conditions_with_media.py index a20d19715..51f80b907 100644 --- a/tutorials/messengers/telegram/5_conditions_with_media.py +++ b/tutorials/messengers/telegram/5_conditions_with_media.py @@ -48,9 +48,7 @@ interface = PollingTelegramInterface(token=os.environ["TG_BOT_TOKEN"]) -def check_if_latest_message_has_photos( - ctx: Context, _: Pipeline, __, ___ -) -> bool: +def check_if_latest_message_has_photos(ctx: Context, _: Pipeline) -> bool: if ctx.last_request is None: return False if ctx.last_request.original_message is None: @@ -62,9 +60,7 @@ def check_if_latest_message_has_photos( return len(ctx.last_request.original_message.message.photo) > 0 -def check_if_latest_message_has_image_documents( - ctx: Context, _: Pipeline, __, ___ -) -> bool: +def check_if_latest_message_has_image_documents(ctx: Context, _: Pipeline) -> bool: if ctx.last_request is None: return False if ctx.last_request.original_message is None: @@ -79,9 +75,7 @@ def check_if_latest_message_has_image_documents( ) -def check_if_latest_message_has_text( - ctx: Context, _: Pipeline, __, ___ -) -> bool: +def check_if_latest_message_has_text(ctx: Context, _: Pipeline) -> bool: if ctx.last_request is None: return False if ctx.last_request.original_message is None: diff --git a/tutorials/messengers/telegram/6_conditions_extras.py b/tutorials/messengers/telegram/6_conditions_extras.py index 09a9ba3ed..a84121090 100644 --- a/tutorials/messengers/telegram/6_conditions_extras.py +++ b/tutorials/messengers/telegram/6_conditions_extras.py @@ -57,9 +57,7 @@ # %% -def check_if_latest_message_is_new_chat_member( - ctx: Context, _: Pipeline, __, ___ -) -> bool: +def check_if_latest_message_is_new_chat_member(ctx: Context, _: Pipeline) -> bool: if ctx.last_request is None: return False if ctx.last_request.original_message is None: @@ -71,9 +69,7 @@ def check_if_latest_message_is_new_chat_member( ) -def check_if_latest_message_is_callback_query( - ctx: Context, _: Pipeline, __, ___ -) -> bool: +def check_if_latest_message_is_callback_query(ctx: Context, _: Pipeline) -> bool: if ctx.last_request is None: return False if ctx.last_request.original_message is None: From ea6653750c2de73b4829b23ae61b425eda6bc5bf Mon Sep 17 00:00:00 2001 From: pseusys Date: Thu, 21 Mar 2024 10:18:50 +0100 Subject: [PATCH 024/140] messenger interface parameter added to `get_bytes` method --- dff/script/core/message.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/dff/script/core/message.py b/dff/script/core/message.py index 842ea11b7..abc4f52a8 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -99,14 +99,10 @@ class DataAttachment(Attachment): source: Optional[Union[HttpUrl, FilePath]] = None id: Optional[str] = None # id field is made separate to simplify type validation title: Optional[str] = None - from_messenger_interface: Optional[MessengerInterface] = Field(exclude=True, default=None) - async def get_bytes(self) -> Optional[bytes]: + async def get_bytes(self, from_messenger_interface: MessengerInterface) -> Optional[bytes]: if self.source is None: - if self.from_messenger_interface is None: - return None - else: - await self.from_messenger_interface.populate_attachment(self) + await from_messenger_interface.populate_attachment(self) if isinstance(self.source, Path): with open(self.source, "rb") as file: return file.read() @@ -122,8 +118,6 @@ def __eq__(self, other): return False if self.title != other.title: return False - if self.from_messenger_interface is not None and other.from_messenger_interface is not None: - return self.get_bytes() == other.get_bytes() return True return NotImplemented From db1de9ad3f05b9fcaf922d20fdf62d5378c02165 Mon Sep 17 00:00:00 2001 From: pseusys Date: Thu, 21 Mar 2024 16:45:48 +0100 Subject: [PATCH 025/140] async def removed from `extract_message_from_telegram` function --- dff/messengers/telegram.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dff/messengers/telegram.py b/dff/messengers/telegram.py index c1031b144..1b942ebdb 100644 --- a/dff/messengers/telegram.py +++ b/dff/messengers/telegram.py @@ -62,7 +62,7 @@ async def populate_attachment(self, attachment: DataAttachment) -> None: # prag else: raise ValueError(f"For attachment {attachment} title or id is not defined!") - async def extract_message_from_telegram(self, update: TelegramMessage) -> Message: # pragma: no cover + def extract_message_from_telegram(self, update: TelegramMessage) -> Message: # pragma: no cover message = Message() message.attachments = list() @@ -208,7 +208,7 @@ async def _on_event( ) async def on_message(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> None: - await self._on_event(update, _, lambda u: await self.extract_message_from_telegram(u.message)) + await self._on_event(update, _, lambda u: self.extract_message_from_telegram(u.message)) async def on_callback(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> None: await self._on_event( From 19d00f3a9fe265668e115cdaf9a18ba60eaaa9d2 Mon Sep 17 00:00:00 2001 From: pseusys Date: Thu, 21 Mar 2024 16:53:50 +0100 Subject: [PATCH 026/140] docs fixed --- tutorials/messengers/telegram/1_basic.py | 2 +- tutorials/messengers/telegram/2_buttons.py | 3 +-- tutorials/messengers/telegram/3_buttons_with_callback.py | 5 ++--- tutorials/messengers/telegram/7_polling_setup.py | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/tutorials/messengers/telegram/1_basic.py b/tutorials/messengers/telegram/1_basic.py index 5f633c500..ce750afea 100644 --- a/tutorials/messengers/telegram/1_basic.py +++ b/tutorials/messengers/telegram/1_basic.py @@ -5,7 +5,7 @@ The following tutorial shows how to run a regular DFF script in Telegram. It asks users for the '/start' command and then loops in one place. -Here, %mddoclink(api,messengers.telegram.interface,PollingTelegramInterface) +Here, %mddoclink(api,messengers.telegram,PollingTelegramInterface) class and [telebot](https://pytba.readthedocs.io/en/latest/index.html) library are used for accessing telegram API in polling mode. diff --git a/tutorials/messengers/telegram/2_buttons.py b/tutorials/messengers/telegram/2_buttons.py index 6ce6c768c..b8db30676 100644 --- a/tutorials/messengers/telegram/2_buttons.py +++ b/tutorials/messengers/telegram/2_buttons.py @@ -25,7 +25,6 @@ class is used to represent telegram message, from dff.script import TRANSITIONS, RESPONSE from dff.script.core.message import Button, Keyboard, Message from dff.pipeline import Pipeline -from dff.messengers.common.conditions import has_callback_query from dff.messengers.telegram import PollingTelegramInterface from dff.utils.testing.common import is_interactive_mode @@ -80,7 +79,7 @@ class is used to represent telegram message, ], ), TRANSITIONS: { - ("general", "success"): has_callback_query("4"), + ("general", "success"): cnd.has_callback_query("4"), ("general", "fail"): cnd.true(), }, }, diff --git a/tutorials/messengers/telegram/3_buttons_with_callback.py b/tutorials/messengers/telegram/3_buttons_with_callback.py index acab18508..03ba0b29b 100644 --- a/tutorials/messengers/telegram/3_buttons_with_callback.py +++ b/tutorials/messengers/telegram/3_buttons_with_callback.py @@ -26,7 +26,6 @@ class is used to represent telegram message, from dff.script import TRANSITIONS, RESPONSE from dff.pipeline import Pipeline from dff.script.core.message import Button, Keyboard, Message -from dff.messengers.common.conditions import has_callback_query from dff.messengers.telegram import PollingTelegramInterface from dff.utils.testing.common import is_interactive_mode @@ -82,8 +81,8 @@ class is used to represent telegram message, ], ), TRANSITIONS: { - ("general", "success"): has_callback_query("correct"), - ("general", "fail"): has_callback_query("wrong"), + ("general", "success"): cnd.has_callback_query("correct"), + ("general", "fail"): cnd.has_callback_query("wrong"), }, }, "success": { diff --git a/tutorials/messengers/telegram/7_polling_setup.py b/tutorials/messengers/telegram/7_polling_setup.py index 48ceb5ede..81ded86ac 100644 --- a/tutorials/messengers/telegram/7_polling_setup.py +++ b/tutorials/messengers/telegram/7_polling_setup.py @@ -4,7 +4,7 @@ The following tutorial shows how to configure `PollingTelegramInterface`. -See %mddoclink(api,messengers.telegram.interface,PollingTelegramInterface) +See %mddoclink(api,messengers.telegram,PollingTelegramInterface) for more information. """ From 11cc1a38bb5567b0e6263fd0e7f1ce56e0701fa2 Mon Sep 17 00:00:00 2001 From: pseusys Date: Thu, 21 Mar 2024 17:00:55 +0100 Subject: [PATCH 027/140] condition import added --- dff/script/conditions/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dff/script/conditions/__init__.py b/dff/script/conditions/__init__.py index 06bebb91a..9b5fe812f 100644 --- a/dff/script/conditions/__init__.py +++ b/dff/script/conditions/__init__.py @@ -14,4 +14,5 @@ false, agg, neg, + has_callback_query, ) From 66f82b1eb989c2f0c635c76923b4cc73c95eaede Mon Sep 17 00:00:00 2001 From: pseusys Date: Fri, 22 Mar 2024 01:45:20 +0100 Subject: [PATCH 028/140] two attachment tutorials fixed --- tutorials/script/responses/2_buttons.py | 60 ++++++++++++++++--------- tutorials/script/responses/3_media.py | 10 ++--- 2 files changed, 44 insertions(+), 26 deletions(-) diff --git a/tutorials/script/responses/2_buttons.py b/tutorials/script/responses/2_buttons.py index faa9f60bc..8d8635eb3 100644 --- a/tutorials/script/responses/2_buttons.py +++ b/tutorials/script/responses/2_buttons.py @@ -57,8 +57,10 @@ def payload_check_inner(ctx: Context, _: Pipeline): "misc": { "ui": Keyboard( buttons=[ - Button(text="5", payload="5"), - Button(text="4", payload="4"), + [ + Button(text="5", data="5"), + Button(text="4", data="4"), + ] ] ), }, @@ -77,8 +79,10 @@ def payload_check_inner(ctx: Context, _: Pipeline): "misc": { "ui": Keyboard( buttons=[ - Button(text="38", payload="38"), - Button(text="48", payload="48"), + [ + Button(text="38", data="38"), + Button(text="48", data="48"), + ] ] ), }, @@ -97,8 +101,10 @@ def payload_check_inner(ctx: Context, _: Pipeline): "misc": { "ui": Keyboard( buttons=[ - Button(text="229", payload="229"), - Button(text="283", payload="283"), + [ + Button(text="229", data="229"), + Button(text="283", data="283"), + ] ] ), }, @@ -126,8 +132,10 @@ def payload_check_inner(ctx: Context, _: Pipeline): "misc": { "ui": Keyboard( buttons=[ - Button(text="5", payload="5"), - Button(text="4", payload="4"), + [ + Button(text="5", data="5"), + Button(text="4", data="4"), + ] ] ) }, @@ -143,8 +151,10 @@ def payload_check_inner(ctx: Context, _: Pipeline): "misc": { "ui": Keyboard( buttons=[ - Button(text="5", payload="5"), - Button(text="4", payload="4"), + [ + Button(text="5", data="5"), + Button(text="4", data="4"), + ] ] ), }, @@ -160,8 +170,10 @@ def payload_check_inner(ctx: Context, _: Pipeline): "misc": { "ui": Keyboard( buttons=[ - Button(text="38", payload="38"), - Button(text="48", payload="48"), + [ + Button(text="38", data="38"), + Button(text="48", data="48"), + ] ] ), }, @@ -177,8 +189,10 @@ def payload_check_inner(ctx: Context, _: Pipeline): "misc": { "ui": Keyboard( buttons=[ - Button(text="38", payload="38"), - Button(text="48", payload="48"), + [ + Button(text="38", data="38"), + Button(text="48", data="48"), + ] ] ), }, @@ -194,8 +208,10 @@ def payload_check_inner(ctx: Context, _: Pipeline): "misc": { "ui": Keyboard( buttons=[ - Button(text="229", payload="229"), - Button(text="283", payload="283"), + [ + Button(text="229", data="229"), + Button(text="283", data="283"), + ] ] ), }, @@ -211,8 +227,10 @@ def payload_check_inner(ctx: Context, _: Pipeline): "misc": { "ui": Keyboard( buttons=[ - Button(text="229", payload="229"), - Button(text="283", payload="283"), + [ + Button(text="229", data="229"), + Button(text="283", data="283"), + ] ] ), }, @@ -230,15 +248,15 @@ def process_request(ctx: Context): and ctx.last_response.misc and ctx.last_response.misc.get("ui") ) - if ui and ui.buttons: + if ui and ui.buttons[0]: try: - chosen_button = ui.buttons[int(ctx.last_request.text)] + chosen_button = ui.buttons[0][int(ctx.last_request.text)] except (IndexError, ValueError): raise ValueError( "Type in the index of the correct option " "to choose from the buttons." ) - ctx.last_request = Message(misc={"payload": chosen_button.payload}) + ctx.last_request = Message(misc={"payload": chosen_button.data}) # %% diff --git a/tutorials/script/responses/3_media.py b/tutorials/script/responses/3_media.py index 412363c14..4f46bb130 100644 --- a/tutorials/script/responses/3_media.py +++ b/tutorials/script/responses/3_media.py @@ -18,7 +18,7 @@ from dff.script import RESPONSE, TRANSITIONS from dff.script.conditions import std_conditions as cnd -from dff.script.core.message import Attachments, Image, Message +from dff.script.core.message import Image, Message from dff.pipeline import Pipeline from dff.utils.testing import ( @@ -57,14 +57,14 @@ "send_one": { RESPONSE: Message( text="here's my picture!", - attachments=Attachments(files=[Image(source=img_url)]), + attachments=[Image(source=img_url)], ), TRANSITIONS: {("root", "fallback"): cnd.true()}, }, "send_many": { RESPONSE: Message( text="Look at my pictures", - attachments=Attachments(files=[Image(source=img_url)] * 10), + attachments=[Image(source=img_url)], ), TRANSITIONS: {("root", "fallback"): cnd.true()}, }, @@ -93,7 +93,7 @@ Message(img_url), Message( text="here's my picture!", - attachments=Attachments(files=[Image(source=img_url)]), + attachments=[Image(source=img_url)], ), ), ( @@ -105,7 +105,7 @@ Message(f"{img_url} repeat 10 times"), Message( text="Look at my pictures", - attachments=Attachments(files=[Image(source=img_url)] * 10), + attachments=[Image(source=img_url)], ), ), ( From 2773c9d4c7e8250c8e83fc71fc2f1aede754e987 Mon Sep 17 00:00:00 2001 From: pseusys Date: Fri, 22 Mar 2024 01:48:02 +0100 Subject: [PATCH 029/140] attachment tuples changed to sets --- dff/messengers/console.py | 3 +++ dff/messengers/telegram.py | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/dff/messengers/console.py b/dff/messengers/console.py index 3c6ae2ede..6a9dfcb64 100644 --- a/dff/messengers/console.py +++ b/dff/messengers/console.py @@ -12,6 +12,9 @@ class CLIMessengerInterface(PollingMessengerInterface): This message interface can maintain dialog with one user at a time only. """ + request_attachments = set() + response_attachments = set() + def __init__( self, intro: Optional[str] = None, diff --git a/dff/messengers/telegram.py b/dff/messengers/telegram.py index 1b942ebdb..96625e6a7 100644 --- a/dff/messengers/telegram.py +++ b/dff/messengers/telegram.py @@ -45,8 +45,8 @@ class _AbstractTelegramInterface(MessengerInterface): # pragma: no cover - accepts_attachments = (Location, Contact, Invoice, Poll, Audio, Video, Animation, Image, Document) - produces_attachments = (Location, Contact, Poll, Audio, Video, Animation, Image, Document, Keyboard) + request_attachments = {Location, Contact, Invoice, Poll, Audio, Video, Animation, Image, Document} + response_attachments = {Location, Contact, Poll, Audio, Video, Animation, Image, Document, Keyboard} def __init__(self, token: str) -> None: self.application = Application.builder().token(token).build() From 80fceb573c5cf2839a7b80f91b43e6995bc8408a Mon Sep 17 00:00:00 2001 From: pseusys Date: Fri, 22 Mar 2024 01:50:16 +0100 Subject: [PATCH 030/140] lint applied --- dff/script/conditions/std_conditions.py | 1 + tutorials/messengers/telegram/5_conditions_with_media.py | 4 ++-- tutorials/messengers/telegram/6_conditions_extras.py | 8 ++++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/dff/script/conditions/std_conditions.py b/dff/script/conditions/std_conditions.py index c71c600d4..aefae6c9f 100644 --- a/dff/script/conditions/std_conditions.py +++ b/dff/script/conditions/std_conditions.py @@ -249,6 +249,7 @@ def has_callback_query(expected_query_string: str): """ Condition that checks if :py:attr:`~.CallbackQuery.query_string` of the last message matches `expected`. """ + def has_callback_query_handler(ctx: Context, _: Pipeline) -> bool: last_request = ctx.last_request if last_request is None or last_request.attachments is None: diff --git a/tutorials/messengers/telegram/5_conditions_with_media.py b/tutorials/messengers/telegram/5_conditions_with_media.py index 51f80b907..2828d67d3 100644 --- a/tutorials/messengers/telegram/5_conditions_with_media.py +++ b/tutorials/messengers/telegram/5_conditions_with_media.py @@ -60,7 +60,7 @@ def check_if_latest_message_has_photos(ctx: Context, _: Pipeline) -> bool: return len(ctx.last_request.original_message.message.photo) > 0 -def check_if_latest_message_has_image_documents(ctx: Context, _: Pipeline) -> bool: +def check_if_latest_message_has_images(ctx: Context, _: Pipeline) -> bool: if ctx.last_request is None: return False if ctx.last_request.original_message is None: @@ -123,7 +123,7 @@ def check_if_latest_message_has_text(ctx: Context, _: Pipeline) -> bool: # We should consider both cases # when we check the message for media. check_if_latest_message_has_photos, - check_if_latest_message_has_image_documents, + check_if_latest_message_has_images, ] ), ( diff --git a/tutorials/messengers/telegram/6_conditions_extras.py b/tutorials/messengers/telegram/6_conditions_extras.py index a84121090..01c5b8811 100644 --- a/tutorials/messengers/telegram/6_conditions_extras.py +++ b/tutorials/messengers/telegram/6_conditions_extras.py @@ -57,7 +57,9 @@ # %% -def check_if_latest_message_is_new_chat_member(ctx: Context, _: Pipeline) -> bool: +def check_if_latest_message_is_new_chat_member( + ctx: Context, _: Pipeline +) -> bool: if ctx.last_request is None: return False if ctx.last_request.original_message is None: @@ -69,7 +71,9 @@ def check_if_latest_message_is_new_chat_member(ctx: Context, _: Pipeline) -> boo ) -def check_if_latest_message_is_callback_query(ctx: Context, _: Pipeline) -> bool: +def check_if_latest_message_is_callback_query( + ctx: Context, _: Pipeline +) -> bool: if ctx.last_request is None: return False if ctx.last_request.original_message is None: From a5fbe60681e3f1c24d1700d0487cdfc9f241a650 Mon Sep 17 00:00:00 2001 From: pseusys Date: Fri, 22 Mar 2024 02:05:39 +0100 Subject: [PATCH 031/140] docs links fixed --- tutorials/messengers/telegram/2_buttons.py | 6 ------ tutorials/messengers/telegram/3_buttons_with_callback.py | 6 ------ tutorials/messengers/telegram/4_conditions.py | 5 +++-- tutorials/messengers/telegram/5_conditions_with_media.py | 5 +++-- tutorials/messengers/telegram/6_conditions_extras.py | 7 +++---- tutorials/script/responses/3_media.py | 2 +- 6 files changed, 10 insertions(+), 21 deletions(-) diff --git a/tutorials/messengers/telegram/2_buttons.py b/tutorials/messengers/telegram/2_buttons.py index b8db30676..e3926df12 100644 --- a/tutorials/messengers/telegram/2_buttons.py +++ b/tutorials/messengers/telegram/2_buttons.py @@ -4,12 +4,6 @@ This tutorial shows how to display and hide a basic keyboard in Telegram. -Here, %mddoclink(api,messengers.telegram.message,TelegramMessage) -class is used to represent telegram message, -%mddoclink(api,messengers.telegram.message,TelegramUI) and -%mddoclink(api,messengers.telegram.message,RemoveKeyboard) -classes are used for configuring additional telegram message features. - Different %mddoclink(api,script.core.message,message) classes are used for representing different common message features, like Attachment, Audio, Button, Image, etc. diff --git a/tutorials/messengers/telegram/3_buttons_with_callback.py b/tutorials/messengers/telegram/3_buttons_with_callback.py index 03ba0b29b..d993672a8 100644 --- a/tutorials/messengers/telegram/3_buttons_with_callback.py +++ b/tutorials/messengers/telegram/3_buttons_with_callback.py @@ -5,12 +5,6 @@ This tutorial demonstrates, how to add an inline keyboard and utilize inline queries. -Here, %mddoclink(api,messengers.telegram.message,TelegramMessage) -class is used to represent telegram message, -%mddoclink(api,messengers.telegram.message,TelegramUI) and -%mddoclink(api,messengers.telegram.message,RemoveKeyboard) -classes are used for configuring additional telegram message features. - Different %mddoclink(api,script.core.message,message) classes are used for representing different common message features, like Attachment, Audio, Button, Image, etc. diff --git a/tutorials/messengers/telegram/4_conditions.py b/tutorials/messengers/telegram/4_conditions.py index ae2bb285d..4b2937e94 100644 --- a/tutorials/messengers/telegram/4_conditions.py +++ b/tutorials/messengers/telegram/4_conditions.py @@ -5,8 +5,9 @@ This tutorial shows how to process Telegram updates in your script and reuse handler triggers from the `pytelegrambotapi` library. -Here, %mddoclink(api,messengers.telegram.messenger) -function is used for graph navigation according to Telegram events. +Here, %mddoclink(api,messengers.telegram) +message `original_message` component used +for graph navigation according to Telegram events. """ diff --git a/tutorials/messengers/telegram/5_conditions_with_media.py b/tutorials/messengers/telegram/5_conditions_with_media.py index 2828d67d3..fb6602f37 100644 --- a/tutorials/messengers/telegram/5_conditions_with_media.py +++ b/tutorials/messengers/telegram/5_conditions_with_media.py @@ -4,8 +4,9 @@ This tutorial shows how to use media-related logic in your script. -Here, %mddoclink(api,messengers.telegram.messenger) -function is used for graph navigation according to Telegram events. +Here, %mddoclink(api,messengers.telegram) +message `original_message` component used +for graph navigation according to Telegram events. Different %mddoclink(api,script.core.message,message) classes are used for representing different common message features, diff --git a/tutorials/messengers/telegram/6_conditions_extras.py b/tutorials/messengers/telegram/6_conditions_extras.py index 01c5b8811..ed9d5b2ff 100644 --- a/tutorials/messengers/telegram/6_conditions_extras.py +++ b/tutorials/messengers/telegram/6_conditions_extras.py @@ -5,10 +5,9 @@ This tutorial shows how to use additional update filters inherited from the `pytelegrambotapi` library. -%mddoclink(api,messengers.telegram.messenger) -function and different types of -%mddoclink(api,messengers.telegram.messenger,UpdateType) -are used for telegram message type checking. +Here, %mddoclink(api,messengers.telegram) +message `original_message` component +is used for telegram message type checking. """ diff --git a/tutorials/script/responses/3_media.py b/tutorials/script/responses/3_media.py index 4f46bb130..3072c7587 100644 --- a/tutorials/script/responses/3_media.py +++ b/tutorials/script/responses/3_media.py @@ -2,7 +2,7 @@ """ # Responses: 3. Media -Here, %mddoclink(api,script.core.message,Attachments) class is shown. +Here, %mddoclink(api,script.core.message,Attachment) class is shown. Attachments can be used for attaching different media elements (such as %mddoclink(api,script.core.message,Image), %mddoclink(api,script.core.message,Document) From f8cecdfc3e2f7c5d86eb66799cd13d79337937c6 Mon Sep 17 00:00:00 2001 From: pseusys Date: Wed, 27 Mar 2024 11:34:17 +0100 Subject: [PATCH 032/140] telegram formatted --- dff/messengers/telegram.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dff/messengers/telegram.py b/dff/messengers/telegram.py index 96625e6a7..cdb9faeff 100644 --- a/dff/messengers/telegram.py +++ b/dff/messengers/telegram.py @@ -4,6 +4,7 @@ This module implements various interfaces for :py:class:`~dff.messengers.telegram.messenger.TelegramMessenger` that can be used to interact with the Telegram API. """ + from pathlib import Path from tempfile import gettempdir from typing import Callable, Optional, Sequence From 95019ad83cbedadbbfa7f3a7ce5f3ac732827482 Mon Sep 17 00:00:00 2001 From: pseusys Date: Wed, 27 Mar 2024 11:40:13 +0100 Subject: [PATCH 033/140] lock update --- poetry.lock | 452 ++++++++++++++++++++++------------------------------ 1 file changed, 191 insertions(+), 261 deletions(-) diff --git a/poetry.lock b/poetry.lock index c4e208e2b..03ab5d54c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "accessible-pygments" @@ -342,13 +342,13 @@ test = ["dateparser (==1.*)", "pre-commit", "pytest", "pytest-cov", "pytest-mock [[package]] name = "asgiref" -version = "3.7.2" +version = "3.8.1" description = "ASGI specs, helper code, and adapters" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "asgiref-3.7.2-py3-none-any.whl", hash = "sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e"}, - {file = "asgiref-3.7.2.tar.gz", hash = "sha256:9e0ce3aa93a819ba5b45120216b23878cf6e8525eb3848653452b4192b92afed"}, + {file = "asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47"}, + {file = "asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590"}, ] [package.dependencies] @@ -594,7 +594,6 @@ lxml = ["lxml"] [[package]] name = "black" version = "24.3.0" -version = "24.3.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.8" @@ -621,28 +620,6 @@ files = [ {file = "black-24.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:7bb041dca0d784697af4646d3b62ba4a6b028276ae878e53f6b4f74ddd6db99f"}, {file = "black-24.3.0-py3-none-any.whl", hash = "sha256:41622020d7120e01d377f74249e677039d20e6344ff5851de8a10f11f513bf93"}, {file = "black-24.3.0.tar.gz", hash = "sha256:a0c9c4a0771afc6919578cec71ce82a3e31e054904e7197deacbc9382671c41f"}, - {file = "black-24.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395"}, - {file = "black-24.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995"}, - {file = "black-24.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2af80566f43c85f5797365077fb64a393861a3730bd110971ab7a0c94e873e7"}, - {file = "black-24.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:4be5bb28e090456adfc1255e03967fb67ca846a03be7aadf6249096100ee32d0"}, - {file = "black-24.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4f1373a7808a8f135b774039f61d59e4be7eb56b2513d3d2f02a8b9365b8a8a9"}, - {file = "black-24.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aadf7a02d947936ee418777e0247ea114f78aff0d0959461057cae8a04f20597"}, - {file = "black-24.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c02e4ea2ae09d16314d30912a58ada9a5c4fdfedf9512d23326128ac08ac3d"}, - {file = "black-24.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf21b7b230718a5f08bd32d5e4f1db7fc8788345c8aea1d155fc17852b3410f5"}, - {file = "black-24.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:2818cf72dfd5d289e48f37ccfa08b460bf469e67fb7c4abb07edc2e9f16fb63f"}, - {file = "black-24.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4acf672def7eb1725f41f38bf6bf425c8237248bb0804faa3965c036f7672d11"}, - {file = "black-24.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7ed6668cbbfcd231fa0dc1b137d3e40c04c7f786e626b405c62bcd5db5857e4"}, - {file = "black-24.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:56f52cfbd3dabe2798d76dbdd299faa046a901041faf2cf33288bc4e6dae57b5"}, - {file = "black-24.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:79dcf34b33e38ed1b17434693763301d7ccbd1c5860674a8f871bd15139e7837"}, - {file = "black-24.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e19cb1c6365fd6dc38a6eae2dcb691d7d83935c10215aef8e6c38edee3f77abd"}, - {file = "black-24.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b76c275e4c1c5ce6e9870911384bff5ca31ab63d19c76811cb1fb162678213"}, - {file = "black-24.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:b5991d523eee14756f3c8d5df5231550ae8993e2286b8014e2fdea7156ed0959"}, - {file = "black-24.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c45f8dff244b3c431b36e3224b6be4a127c6aca780853574c00faf99258041eb"}, - {file = "black-24.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6905238a754ceb7788a73f02b45637d820b2f5478b20fec82ea865e4f5d4d9f7"}, - {file = "black-24.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7de8d330763c66663661a1ffd432274a2f92f07feeddd89ffd085b5744f85e7"}, - {file = "black-24.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:7bb041dca0d784697af4646d3b62ba4a6b028276ae878e53f6b4f74ddd6db99f"}, - {file = "black-24.3.0-py3-none-any.whl", hash = "sha256:41622020d7120e01d377f74249e677039d20e6344ff5851de8a10f11f513bf93"}, - {file = "black-24.3.0.tar.gz", hash = "sha256:a0c9c4a0771afc6919578cec71ce82a3e31e054904e7197deacbc9382671c41f"}, ] [package.dependencies] @@ -1502,18 +1479,18 @@ devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benc [[package]] name = "filelock" -version = "3.13.1" +version = "3.13.3" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"}, - {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"}, + {file = "filelock-3.13.3-py3-none-any.whl", hash = "sha256:5ffa845303983e7a0b7ae17636509bc97997d58afeafa72fb141a17b152284cb"}, + {file = "filelock-3.13.3.tar.gz", hash = "sha256:a79895a25bbefdf55d1a2a0a80968f7dbb28edcd6d4234a0afb3f37ecde4b546"}, ] [package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] typing = ["typing-extensions (>=4.8)"] [[package]] @@ -2169,13 +2146,13 @@ testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs [[package]] name = "importlib-resources" -version = "6.3.1" +version = "6.4.0" description = "Read resources from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_resources-6.3.1-py3-none-any.whl", hash = "sha256:4811639ca7fa830abdb8e9ca0a104dc6ad13de691d9fe0d3173a71304f068159"}, - {file = "importlib_resources-6.3.1.tar.gz", hash = "sha256:29a3d16556e330c3c8fb8202118c5ff41241cc34cbfb25989bbad226d99b7995"}, + {file = "importlib_resources-6.4.0-py3-none-any.whl", hash = "sha256:50d10f043df931902d4194ea07ec57960f66a80449ff867bfe782b4c486ba78c"}, + {file = "importlib_resources-6.4.0.tar.gz", hash = "sha256:cdb2b453b8046ca4e3798eb1d84f3cce1446a0e8e7b5ef4efb600f19fc398145"}, ] [package.dependencies] @@ -2183,7 +2160,7 @@ zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["jaraco.collections", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] +testing = ["jaraco.test (>=5.4)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] [[package]] name = "iniconfig" @@ -2795,16 +2772,13 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-ena [[package]] name = "locust" -version = "2.24.0" -version = "2.24.0" +version = "2.24.1" description = "Developer friendly load testing framework" optional = false python-versions = ">=3.8" files = [ - {file = "locust-2.24.0-py3-none-any.whl", hash = "sha256:1b6b878b4fd0108fec956120815e69775d2616c8f4d1e9f365c222a7a5c17d9a"}, - {file = "locust-2.24.0.tar.gz", hash = "sha256:6cffa378d995244a7472af6be1d6139331f19aee44e907deee73e0281252804d"}, - {file = "locust-2.24.0-py3-none-any.whl", hash = "sha256:1b6b878b4fd0108fec956120815e69775d2616c8f4d1e9f365c222a7a5c17d9a"}, - {file = "locust-2.24.0.tar.gz", hash = "sha256:6cffa378d995244a7472af6be1d6139331f19aee44e907deee73e0281252804d"}, + {file = "locust-2.24.1-py3-none-any.whl", hash = "sha256:7f6ed4dc289aad66c304582e6d25e4de5d7c3b175b580332442ab2be35b9d916"}, + {file = "locust-2.24.1.tar.gz", hash = "sha256:094161d44d94839bf1120fd7898b7abb9c143833743ba7c096beb470a236b9a7"}, ] [package.dependencies] @@ -2821,7 +2795,6 @@ pyzmq = ">=25.0.0" requests = ">=2.26.0" roundrobin = ">=0.0.2" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} Werkzeug = ">=2.0.0" [[package]] @@ -3012,13 +2985,13 @@ files = [ [[package]] name = "motor" -version = "3.3.2" +version = "3.4.0" description = "Non-blocking MongoDB driver for Tornado or asyncio" optional = true python-versions = ">=3.7" files = [ - {file = "motor-3.3.2-py3-none-any.whl", hash = "sha256:6fe7e6f0c4f430b9e030b9d22549b732f7c2226af3ab71ecc309e4a1b7d19953"}, - {file = "motor-3.3.2.tar.gz", hash = "sha256:d2fc38de15f1c8058f389c1a44a4d4105c0405c48c061cd492a654496f7bc26a"}, + {file = "motor-3.4.0-py3-none-any.whl", hash = "sha256:4b1e1a0cc5116ff73be2c080a72da078f2bb719b53bc7a6bb9e9a2f7dcd421ed"}, + {file = "motor-3.4.0.tar.gz", hash = "sha256:c89b4e4eb2e711345e91c7c9b122cb68cce0e5e869ed0387dd0acb10775e3131"}, ] [package.dependencies] @@ -3031,7 +3004,7 @@ gssapi = ["pymongo[gssapi] (>=4.5,<5)"] ocsp = ["pymongo[ocsp] (>=4.5,<5)"] snappy = ["pymongo[snappy] (>=4.5,<5)"] srv = ["pymongo[srv] (>=4.5,<5)"] -test = ["aiohttp (<3.8.6)", "mockupdb", "motor[encryption]", "pytest (>=7)", "tornado (>=5)"] +test = ["aiohttp (!=3.8.6)", "mockupdb", "motor[encryption]", "pytest (>=7)", "tornado (>=5)"] zstd = ["pymongo[zstd] (>=4.5,<5)"] [[package]] @@ -3201,7 +3174,6 @@ files = [ [[package]] name = "mypy" version = "1.9.0" -version = "1.9.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" @@ -3233,33 +3205,6 @@ files = [ {file = "mypy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9"}, {file = "mypy-1.9.0-py3-none-any.whl", hash = "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e"}, {file = "mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974"}, - {file = "mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f"}, - {file = "mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed"}, - {file = "mypy-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150"}, - {file = "mypy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374"}, - {file = "mypy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03"}, - {file = "mypy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3"}, - {file = "mypy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc"}, - {file = "mypy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129"}, - {file = "mypy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612"}, - {file = "mypy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3"}, - {file = "mypy-1.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd"}, - {file = "mypy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6"}, - {file = "mypy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185"}, - {file = "mypy-1.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913"}, - {file = "mypy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"}, - {file = "mypy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b"}, - {file = "mypy-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2"}, - {file = "mypy-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e"}, - {file = "mypy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04"}, - {file = "mypy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89"}, - {file = "mypy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02"}, - {file = "mypy-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4"}, - {file = "mypy-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d"}, - {file = "mypy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf"}, - {file = "mypy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9"}, - {file = "mypy-1.9.0-py3-none-any.whl", hash = "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e"}, - {file = "mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974"}, ] [package.dependencies] @@ -3308,13 +3253,13 @@ test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>= [[package]] name = "nbconvert" -version = "7.16.2" +version = "7.16.3" description = "Converting Jupyter Notebooks (.ipynb files) to other formats. Output formats include asciidoc, html, latex, markdown, pdf, py, rst, script. nbconvert can be used both as a Python library (`import nbconvert`) or as a command line tool (invoked as `jupyter nbconvert ...`)." optional = false python-versions = ">=3.8" files = [ - {file = "nbconvert-7.16.2-py3-none-any.whl", hash = "sha256:0c01c23981a8de0220255706822c40b751438e32467d6a686e26be08ba784382"}, - {file = "nbconvert-7.16.2.tar.gz", hash = "sha256:8310edd41e1c43947e4ecf16614c61469ebc024898eb808cce0999860fc9fb16"}, + {file = "nbconvert-7.16.3-py3-none-any.whl", hash = "sha256:ddeff14beeeedf3dd0bc506623e41e4507e551736de59df69a91f86700292b3b"}, + {file = "nbconvert-7.16.3.tar.gz", hash = "sha256:a6733b78ce3d47c3f85e504998495b07e6ea9cf9bf6ec1c98dda63ec6ad19142"}, ] [package.dependencies] @@ -3341,7 +3286,7 @@ docs = ["ipykernel", "ipython", "myst-parser", "nbsphinx (>=0.2.12)", "pydata-sp qtpdf = ["nbconvert[qtpng]"] qtpng = ["pyqtwebengine (>=5.15)"] serve = ["tornado (>=6.1)"] -test = ["flaky", "ipykernel", "ipywidgets (>=7.5)", "pytest"] +test = ["flaky", "ipykernel", "ipywidgets (>=7.5)", "pytest (>=7)"] webpdf = ["playwright"] [[package]] @@ -3693,8 +3638,8 @@ files = [ [package.dependencies] numpy = [ {version = ">=1.20.3", markers = "python_version < \"3.10\""}, - {version = ">=1.23.2", markers = "python_version >= \"3.11\""}, {version = ">=1.21.0", markers = "python_version >= \"3.10\" and python_version < \"3.11\""}, + {version = ">=1.23.2", markers = "python_version >= \"3.11\""}, ] python-dateutil = ">=2.8.2" pytz = ">=2020.1" @@ -4049,13 +3994,13 @@ files = [ [[package]] name = "poetry-plugin-export" -version = "1.7.0" +version = "1.7.1" description = "Poetry plugin to export the dependencies to various formats" optional = false python-versions = ">=3.8,<4.0" files = [ - {file = "poetry_plugin_export-1.7.0-py3-none-any.whl", hash = "sha256:2283b28e0209f9f9598c6fe44f30586ec91329ea1558f908708261e0516bf427"}, - {file = "poetry_plugin_export-1.7.0.tar.gz", hash = "sha256:e73f207fc0e08c2f59ead82cbe39ef259b351a318d5c0bddcec13990bcd324a7"}, + {file = "poetry_plugin_export-1.7.1-py3-none-any.whl", hash = "sha256:b2258e53ae0d369a73806f957ed0e726eb95c571a0ce8b1f273da686528cc1da"}, + {file = "poetry_plugin_export-1.7.1.tar.gz", hash = "sha256:cf62cfb6218a904290ba6db3bc1a24aa076d10f81c48c6e48b2ded430131e22e"}, ] [package.dependencies] @@ -4223,13 +4168,13 @@ numpy = ">=1.16.6,<2" [[package]] name = "pyasn1" -version = "0.5.1" +version = "0.6.0" description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +python-versions = ">=3.8" files = [ - {file = "pyasn1-0.5.1-py2.py3-none-any.whl", hash = "sha256:4439847c58d40b1d0a573d07e3856e95333f1976294494c325775aeca506eb58"}, - {file = "pyasn1-0.5.1.tar.gz", hash = "sha256:6d391a96e59b23130a5cfa74d6fd7f388dbbe26cc8f1edf39fdddf08d9d6676c"}, + {file = "pyasn1-0.6.0-py2.py3-none-any.whl", hash = "sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473"}, + {file = "pyasn1-0.6.0.tar.gz", hash = "sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c"}, ] [[package]] @@ -4567,15 +4512,12 @@ tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} [[package]] name = "pytest" version = "8.1.1" -version = "8.1.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, - {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, - {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, ] [package.dependencies] @@ -4585,22 +4527,19 @@ iniconfig = "*" packaging = "*" pluggy = ">=1.4,<2.0" tomli = {version = ">=1", markers = "python_version < \"3.11\""} -pluggy = ">=1.4,<2.0" -tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] -testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-asyncio" -version = "0.23.5.post1" +version = "0.23.6" description = "Pytest support for asyncio" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-asyncio-0.23.5.post1.tar.gz", hash = "sha256:b9a8806bea78c21276bc34321bbf234ba1b2ea5b30d9f0ce0f2dea45e4685813"}, - {file = "pytest_asyncio-0.23.5.post1-py3-none-any.whl", hash = "sha256:30f54d27774e79ac409778889880242b0403d09cabd65b727ce90fe92dd5d80e"}, + {file = "pytest-asyncio-0.23.6.tar.gz", hash = "sha256:ffe523a89c1c222598c76856e76852b787504ddb72dd5d9b6617ffa8aa2cde5f"}, + {file = "pytest_asyncio-0.23.6-py3-none-any.whl", hash = "sha256:68516fdd1018ac57b846c9846b954f0393b26f094764a28c955eabb0536a4e8a"}, ] [package.dependencies] @@ -4729,16 +4668,13 @@ files = [ [[package]] name = "python-on-whales" -version = "0.70.0" -version = "0.70.0" +version = "0.70.1" description = "A Docker client for Python, designed to be fun and intuitive!" optional = false python-versions = "<4,>=3.8" files = [ - {file = "python-on-whales-0.70.0.tar.gz", hash = "sha256:bb89e91c86e049f9c04e2636f2d40faa000ff5b17d54f71e68581201e449eda5"}, - {file = "python_on_whales-0.70.0-py3-none-any.whl", hash = "sha256:492325387d7686adc6669e911820bd4da1cd672bc5a7fb6d54c32ee849bfe7e6"}, - {file = "python-on-whales-0.70.0.tar.gz", hash = "sha256:bb89e91c86e049f9c04e2636f2d40faa000ff5b17d54f71e68581201e449eda5"}, - {file = "python_on_whales-0.70.0-py3-none-any.whl", hash = "sha256:492325387d7686adc6669e911820bd4da1cd672bc5a7fb6d54c32ee849bfe7e6"}, + {file = "python-on-whales-0.70.1.tar.gz", hash = "sha256:1e7ac35cd16afaad8d23f01be860cb3ff906ee81816d032a327d4d07da1f9341"}, + {file = "python_on_whales-0.70.1-py3-none-any.whl", hash = "sha256:3cecd833359d90fd564cadf7f5e3c88209f8baf998316e0731f3d375d17af2f2"}, ] [package.dependencies] @@ -5045,101 +4981,101 @@ test = ["pytest (>=6,!=7.0.0,!=7.0.1)", "pytest-cov (>=3.0.0)", "pytest-qt"] [[package]] name = "rapidfuzz" -version = "3.6.2" +version = "3.7.0" description = "rapid fuzzy string matching" optional = false python-versions = ">=3.8" files = [ - {file = "rapidfuzz-3.6.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a5637e6bf11b15b5aff6ee818c76bdec99ad208511b78985e6209ba648a6e3ee"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:380586664f2f63807050ddb95e7702888b4f0b425abf17655940c411f39287ad"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3168ff565d4b8c239cf11fb604dd2507d30e9bcaac76a4077c0ac23cf2c866ed"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be69f7fd46b5c6467fe5e2fd4cff3816b0c03048eed8a4becb9a73e6000960e7"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cbd5894f23fdf5697499cf759523639838ac822bd1600e343fdce7313baa02ae"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:85a5b6e026393fe39fb61146b9c17c5af66fffbe1410e992c4bb06d9ec327bd3"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab269adfc64480f209e99f253391a10735edd5c09046e04899adab5fb132f20e"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35aeac852bca06023d6bbd50c1fc504ca5a9a3613d5e75a140f0be7601fa34ef"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e706f302c6a3ae0d74edd0d6ace46aee1ae07c563b436ccf5ff04db2b3571e60"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bec353f022011e6e5cd28ccb8700fbd2a33918197af0d4e0abb3c3f4845cc864"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ef3925daaa93eed20401012e219f569ff0c039ed5bf4ce2d3737b4f75d441622"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6ee98d88ae9ccc77ff61992ed33b2496478def5dc0da55c9a9aa06fcb725a352"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:423c7c588b09d618601097b7a0017dfcb91132a2076bef29023c5f3cd2dc3de1"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-win32.whl", hash = "sha256:c17c5efee347a40a6f4c1eec59e3d7d1e22f7613a97f8b8a07733ef723483a04"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-win_amd64.whl", hash = "sha256:4209816626d8d6ff8ae7dc248061c6059e618b70c6e6f6e4d7444ae3740b2b85"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-win_arm64.whl", hash = "sha256:1c54d3c85e522d3ac9ee39415f183c8fa184c4f87e7e5a37938f15a6d50e853a"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e06f6d270112f5db001f1cba5a97e1a48aee3d3dbdcbea3ec027c230462dbf9b"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:080cb71b50cb6aff11d1c6aeb157f273e2da0b2bdb3f9d7b01257e49e69a8576"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a7895e04a22d6515bc91a850e0831f2405547605aa311d1ffec51e4818abc3c1"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd82f9838519136b7083dd1e3149ee80344521f3dc37f744f227505ff0883efb"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a945567c2b0b6e069454c9782d5234b0b6795718adf7a9f868bd3144afa6a023"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:673ba2c343644805acdae1cb949c6a4de71aa2f62a998978551ebea59603af3f"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9d457c89bac1471442002e70551e8268e639b3870b4a4521eae363c07253be87"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:495c0d8e14e6f12520eb7fc71b9ba9fcaafb47fc23a654e6e89b6c7985ec0020"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6d67b649bf3e1b1722d04eca44d37919aef88305ce7ad05564502d013cf550fd"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e48dde8ca83d11daa00900cf6a5d281a1297aef9b7bfa73801af6e8822be5019"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:824cc381cf81cbf8d158f6935664ec2a69e6ac3b1d39fa201988bf81a257f775"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:1dfe4c24957474ce0ac75d886387e30e292b4be39228a6d71f76de414dc187db"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d57b98013b802621bbc8b12a46bfc9d36ac552ab51ca207f7ce167ad46adabeb"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-win32.whl", hash = "sha256:9a07dffac439223b4f1025dbfc68f4445a3460a859309c9858c2a3fa29617cdc"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-win_amd64.whl", hash = "sha256:95a49c6b8bf1229743ae585dd5b7d57f0d15a7eb6e826866d5c9965ba958503c"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-win_arm64.whl", hash = "sha256:af7c19ec86e11488539380d3db1755be5d561a3c0e7b04ff9d07abd7f9a8e9d8"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:de8adc12161bf282c60f12dc9233bb31632f71d446a010fe7469a69b8153427f"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:337e357f693130c4c6be740652542b260e36f622c59e01fa33d58f1d2750c930"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6468f8bc8c3c50604f43631550ef9cfec873515dba5023ca34d461be94669fc8"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74c6773b11445b5e5cf93ca383171cd0ac0cdeafea11a7b2a5688f8bf8d813e6"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1507fc5769aa109dda4de3a15f822a0f6a03e18d627bd0ba3ddbb253cf70e07"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:617949a70150e6fffdaed19253dd49f7a53528411dc8bf7663d499ba21e0f61e"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f8b77779174b1b40aa70827692571ab457061897846255ad7d5d559e2edb1932"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80e51b22a7da83f9c87a97e92df07ed0612c74c35496590255f4b5d5b4212dfe"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3ae7c86914cb6673e97e187ba431b9c4cf4177d9ae77f8a1e5b2ba9a5628839e"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ddc380ffaa90f204cc9ddcb779114b9ab6f015246d549de9d47871a97ef9f18a"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:3c1dc078ef371fce09f9f3eec2ca4eaa2a8cd412ec53941015b4f39f14d34407"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:9a74102fc5a2534fe91f7507838623e1f3a149d8e05648389c42bb42e14b1c3f"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:48e1eaea8fcd522fca7f04f0480663f0f0cfb77957092cce60a93f4462864996"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-win32.whl", hash = "sha256:66b008bf2972740cd2dda5d382eb8bdb87265cd88198e71c7797bdc0d1f79d20"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-win_amd64.whl", hash = "sha256:87ac3a87f2251ae2e95fc9478ca5c759de6d141d04c84d3fec9f9cdcfc167b33"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-win_arm64.whl", hash = "sha256:b593cc51aed887e93b78c2f94dfae9008be2b23d17afd3b1f1d3eb3913b58f26"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:7d830bc7a9b586a374147ec60b08b1f9ae5996b43f75cc514f37faef3866b519"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:dbee7f5ff11872b76505cbd87c814abc823e8757f11c69062eb3b25130a283da"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:28c011fb31f2c3f82f503aedd6097d3d3854e574e327a119a3b7eb2cf90b79ca"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cda81d0e0ce0c13abfa46b24e10c1e85f9c6acb628f0a9a948f5779f9c2076a2"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c279928651ce0e9e5220dcb25a00cc53b65e592a0861336a38299bcdca3a596"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35bd4bc9c40e6994c5d6edea4b9319388b4d9711c13c66d543bb4c37624b4184"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d07899506a5a8760448d9df036d528b55a554bf571714173635c79eef4a86e58"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb2e51d01b9c6d6954a3e055c57a80d4685b4fc82719db5519fc153566bcd6bb"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:153d065e353371cc0aeff32b99999a5758266a64e958d1364189367c1c9f6814"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4edcceebb85ebfa49a3ddcde20ad891d36c08dc0fd592efdab0e7d313a4e36af"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3549123fca5bb817341025f98e8e49ca99f84596c7c4f92b658f8e5836040d4a"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:84c1032ae42628465b7a5cc35249906061e18a8193c9c27cbd2db54e9823a9a6"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9bcc91ebd8fc69a6bd3b5711c8250f5f4e70606b4da75ef415f57ad209978205"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-win32.whl", hash = "sha256:f3a70f341c4c111bad910d2df69c78577a98af140319a996af24c9385939335d"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-win_amd64.whl", hash = "sha256:354ad5fe655beb7b279390cb58334903931c5452ecbad1b1666ffb06786498e2"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1b86b93d93020c2b3edc1665d75c8855784845fc0a739b312c26c3a4bf0c80d5"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:28243086ed0e50808bb56632e5442c457241646aeafafd501ac87901f40a3237"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ed52461ae5a9ea4c400d38e2649c74a413f1a6d8fb8308b66f1fbd122514732f"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a46220f86a5f9cb016af31525e0d0865cad437d02239aa0d8aed2ab8bff1f1c"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:81a630ed2fc3ec5fc7400eb66bab1f87e282b4d47f0abe3e48c6634dfa13b5e4"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8e5a437b9089df6242a718d9c31ab1742989e9400a0977af012ef483b63b4c2"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16270b5529de83b7bae7457e952e4d9cf3fbf029a837dd32d415bb9e0eb8e599"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5378c04102c7f084cde30a100154fa6d7e2baf0d51a6bdd2f912545559c1fb35"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7f18397c8d6a65fc0b288d2fc29bc7baeea6ba91eeb95163a3cd98f23cd3bc85"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2acd2514defce81e6ff4bbff50252d5e7df8e85a731442c4b83e44c86cf1c916"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:1df2faf80201952e252413b6fac6f3e146080dcebb87bb1bb722508e67558ed8"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6440ed0b3007c1c9286b0b88fe2ab2d9e83edd60cd62293b3dfabb732b4e8a30"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4fcfa23b5553b27f4016df77c53172ea743454cf12c28cfa7c35a309a2be93b3"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-win32.whl", hash = "sha256:2d580d937146e803c8e5e1b87916cab8d6f84013b6392713e201efcda335c7d8"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-win_amd64.whl", hash = "sha256:fe2a68be734e8e88af23385c68d6467e15818b6b1df1cbfebf7bff577226c957"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-win_arm64.whl", hash = "sha256:6478f7803efebf5f644d0b758439c5b25728550fdfbb19783d150004c46a75a9"}, - {file = "rapidfuzz-3.6.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:36ce7b68a7b90b787cdd73480a68d2f1ca63c31a3a9d5a79a8736f978e1e9344"}, - {file = "rapidfuzz-3.6.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53597fd72a9340bcdd80d3620f4957c2b92f9b569313b969a3abdaffd193aae6"}, - {file = "rapidfuzz-3.6.2-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4f6de745fe6ce46a422d353ee10599013631d7d714a36d025f164b2d4e8c000"}, - {file = "rapidfuzz-3.6.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62df2136068e2515ed8beb01756381ff62c29384d785e3bf46e3111d4ea3ba1e"}, - {file = "rapidfuzz-3.6.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7382c90170f60c846c81a07ddd80bb2e8c43c8383754486fa37f67391a571897"}, - {file = "rapidfuzz-3.6.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f31314fd2e2f3dc3e519e6f93669462ce7953df2def1c344aa8f5345976d0eb2"}, - {file = "rapidfuzz-3.6.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:012221629d54d3bee954148247f711eb86d4d390b589ebfe03172ea0b37a7531"}, - {file = "rapidfuzz-3.6.2-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d41dd59a70decfce6595315367a2fea2af660d92a9d144acc6479030501014d7"}, - {file = "rapidfuzz-3.6.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f9fa14136a5b0cba1ec42531f7c3e0b0d3edb7fd6bc5e5ae7b498541f3855ab"}, - {file = "rapidfuzz-3.6.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:259364199cbfeca33b1af369fc7951f71717aa285184a3fa5a7b1772da1b89db"}, - {file = "rapidfuzz-3.6.2.tar.gz", hash = "sha256:cf911e792ab0c431694c9bf2648afabfd92099103f2e31492893e078ddca5e1a"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:860f438238f1807532aa5c5c25e74c284232ccc115fe84697b78e25d48f364f7"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4bb9285abeb0477cdb2f8ea0cf7fd4b5f72ed5a9a7d3f0c0bb4a5239db2fc1ed"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:08671280e0c04d2bb3f39511f13cae5914e6690036fd1eefc3d47a47f9fae634"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:04bae4d9c16ce1bab6447d196fb8258d98139ed8f9b288a38b84887985e4227b"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1efa2268b51b68156fb84d18ca1720311698a58051c4a19c40d670057ce60519"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:600b4d4315f33ec0356c0dab3991a5d5761102420bcff29e0773706aa48936e8"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18bc2f13c73d5d34499ff6ada55b052c445d3aa64d22c2639e5ab45472568046"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e11c5e6593be41a555475c9c20320342c1f5585d635a064924956944c465ad4"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d7878025248b99ccca3285891899373f98548f2ca13835d83619ffc42241c626"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b4a7e37fe136022d944374fcd8a2f72b8a19f7b648d2cdfb946667e9ede97f9f"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b5881856f830351aaabd869151124f64a80bf61560546d9588a630a4e933a5de"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:c788b11565cc176fab8fab6dfcd469031e906927db94bf7e422afd8ef8f88a5a"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9e17a3092e74025d896ef1d67ac236c83494da37a78ef84c712e4e2273c115f1"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-win32.whl", hash = "sha256:e499c823206c9ffd9d89aa11f813a4babdb9219417d4efe4c8a6f8272da00e98"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:91f798cc00cd94a0def43e9befc6e867c9bd8fa8f882d1eaa40042f528b7e2c7"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-win_arm64.whl", hash = "sha256:d5a3872f35bec89f07b993fa1c5401d11b9e68bcdc1b9737494e279308a38a5f"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ef6b6ab64c4c91c57a6b58e1d690b59453bfa1f1e9757a7e52e59b4079e36631"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2f9070b42c0ba030b045bba16a35bdb498a0d6acb0bdb3ff4e325960e685e290"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:63044c63565f50818d885bfcd40ac369947da4197de56b4d6c26408989d48edf"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49b0c47860c733a3d73a4b70b97b35c8cbf24ef24f8743732f0d1c412a8c85de"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1b14489b038f007f425a06fcf28ac6313c02cb603b54e3a28d9cfae82198cc0"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be08f39e397a618aab907887465d7fabc2d1a4d15d1a67cb8b526a7fb5202a3e"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16895dc62a7b92028f9c8b6d22830f1cbc77306ee794f461afc6028e1a8d7539"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:579cce49dfa57ffd8c8227b3fb53cced54b4df70cec502e63e9799b4d1f44004"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:40998c8dc35fdd221790b8b5134a8d7499adbfab9a5dd9ec626c7e92e17a43ed"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:dc3fdb4738a6b83ae27f1d8923b00d3a9c2b5c50da75b9f8b81841839c6e3e1f"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:92b8146fbfb37ac358ef7e0f6b79619e4f793fbbe894b99ea87920f9c0a9d77d"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:1dfceaa7c2914585bb8a043265c39ec09078f13fbf53b5525722fc074306b6fa"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f332d61f51b0b9c8b55a0fb052b4764b6ad599ea8ce948ac47a4388e9083c35e"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-win32.whl", hash = "sha256:dfd1e4819f1f3c47141f86159b44b7360ecb19bf675080b3b40437bf97273ab9"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:594b9c33fc1a86784962043ee3fbaaed875fbaadff72e467c2f7a83cd6c5d69d"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-win_arm64.whl", hash = "sha256:0b13a6823a1b83ae43f8bf35955df35032bee7bec0daf9b5ab836e0286067434"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:075a419a0ec29be44b3d7f4bcfa5cb7e91e419379a85fc05eb33de68315bd96f"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:51a5b96d2081c3afbef1842a61d63e55d0a5a201473e6975a80190ff2d6f22ca"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9460d8fddac7ea46dff9298eee9aa950dbfe79f2eb509a9f18fbaefcd10894c"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f39eb1513ee139ba6b5c01fe47ddf2d87e9560dd7fdee1068f7f6efbae70de34"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eace9fdde58a425d4c9a93021b24a0cac830df167a5b2fc73299e2acf9f41493"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cc77237242303733de47829028a0a8b6ab9188b23ec9d9ff0a674fdcd3c8e7f"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:74e692357dd324dff691d379ef2c094c9ec526c0ce83ed43a066e4e68fe70bf6"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f2075ac9ee5c15d33d24a1efc8368d095602b5fd9634c5b5f24d83e41903528"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5a8ba64d72329a940ff6c74b721268c2004eecc48558f648a38e96915b5d1c1b"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a1f268a2a37cd22573b4a06eccd481c04504b246d3cadc2d8e8dfa64b575636d"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:42c2e8a2341363c7caf276efdbe1a673fc5267a02568c47c8e980f12e9bc8727"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:a9acca34b34fb895ee6a84c436bb919f3b9cd8f43e7003d43e9573a1d990ff74"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9bad6a0fe3bc1753dacaa6229a8ba7d9844eb7ae24d44d17c5f4c51c91a8a95e"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-win32.whl", hash = "sha256:c86bc4b1d2380739e6485396195e30021df509b4923f3f757914e171587bce7c"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:d7361608c8e73a1dc0203a87d151cddebdade0098a047c46da43c469c07df964"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:8fdc26e7863e0f63c2185d53bb61f5173ad4451c1c8287b535b30ea25a419a5a"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9b6167468f76779a14b9af66210f68741af94d32d086f19118de4e919f00585c"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5bd394e28ff221557ea4d8152fcec3e66d9f620557feca5f2bedc4c21f8cf2f9"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8e70f876ca89a6df344f8157ac60384e8c05a0dfb442da2490c3f1c45238ccf5"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c837f89d86a5affe9ee6574dad6b195475676a6ab171a67920fc99966f2ab2c"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cda4550a98658f9a8bcdc03d0498ed1565c1563880e3564603a9eaae28d51b2a"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecd70212fd9f1f8b1d3bdd8bcb05acc143defebd41148bdab43e573b043bb241"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:187db4cc8fb54f8c49c67b7f38ef3a122ce23be273032fa2ff34112a2694c3d8"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4604dfc1098920c4eb6d0c6b5cc7bdd4bf95b48633e790c1d3f100a25870691d"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:01581b688c5f4f6665b779135e32db0edab1d78028abf914bb91469928efa383"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0828b55ec8ad084febdf4ab0c942eb1f81c97c0935f1cb0be0b4ea84ce755988"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:150c98b65faff17b917b9d36bff8a4d37b6173579c6bc2e38ff2044e209d37a4"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:7e4eea225d2bff1aff4c85fcc44716596d3699374d99eb5906b7a7560297460e"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7bc944d7e830cfce0f8b4813875f05904207017b66e25ab7ee757507001310a9"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-win32.whl", hash = "sha256:3e55f02105c451ab6ff0edaaba57cab1b6c0a0241cfb2b306d4e8e1503adba50"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:41851620d2900791d66d9b6092fc163441d7dd91a460c73b07957ff1c517bc30"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e8041c6b2d339766efe6298fa272f79d6dd799965df364ef4e50f488c101c899"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4e09d81008e212fc824ea23603ff5270d75886e72372fa6c7c41c1880bcb57ed"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:419c8961e861fb5fc5590056c66a279623d1ea27809baea17e00cdc313f1217a"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1522eaab91b9400b3ef16eebe445940a19e70035b5bc5d98aef23d66e9ac1df0"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:611278ce3136f4544d596af18ab8849827d64372e1d8888d9a8d071bf4a3f44d"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4efa9bfc5b955b6474ee077eee154e240441842fa304f280b06e6b6aa58a1d1e"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0cc9d3c8261457af3f8756b1f71a9fdc4892978a9e8b967976d2803e08bf972"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce728e2b582fd396bc2559160ee2e391e6a4b5d2e455624044699d96abe8a396"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3a6a36c9299e059e0bee3409218bc5235a46570c20fc980cdee5ed21ea6110ad"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9ea720db8def684c1eb71dadad1f61c9b52f4d979263eb5d443f2b22b0d5430a"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:358692f1df3f8aebcd48e69c77c948c9283b44c0efbaf1eeea01739efe3cd9a6"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:faded69ffe79adcefa8da08f414a0fd52375e2b47f57be79471691dad9656b5a"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7f9f3dc14fadbd553975f824ac48c381f42192cec9d7e5711b528357662a8d8e"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-win32.whl", hash = "sha256:7be5f460ff42d7d27729115bfe8a02e83fa0284536d8630ee900d17b75c29e65"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:dd5ad2c12dab2b98340c4b7b9592c8f349730bda9a2e49675ea592bbcbc1360b"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-win_arm64.whl", hash = "sha256:aa163257a0ac4e70f9009d25e5030bdd83a8541dfa3ba78dc86b35c9e16a80b4"}, + {file = "rapidfuzz-3.7.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4e50840a8a8e0229563eeaf22e21a203359859557db8829f4d0285c17126c5fb"}, + {file = "rapidfuzz-3.7.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:632f09e19365ace5ff2670008adc8bf23d03d668b03a30230e5b60ff9317ee93"}, + {file = "rapidfuzz-3.7.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:209dda6ae66b702f74a78cef555397cdc2a83d7f48771774a20d2fc30808b28c"}, + {file = "rapidfuzz-3.7.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bc0b78572626af6ab134895e4dbfe4f4d615d18dcc43b8d902d8e45471aabba"}, + {file = "rapidfuzz-3.7.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7ba14850cc8258b3764ea16b8a4409ac2ba16d229bde7a5f495dd479cd9ccd56"}, + {file = "rapidfuzz-3.7.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b917764fd2b267addc9d03a96d26f751f6117a95f617428c44a069057653b528"}, + {file = "rapidfuzz-3.7.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1252ca156e1b053e84e5ae1c8e9e062ee80468faf23aa5c543708212a42795fd"}, + {file = "rapidfuzz-3.7.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:86c7676a32d7524e40bc73546e511a408bc831ae5b163029d325ea3a2027d089"}, + {file = "rapidfuzz-3.7.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20e7d729af2e5abb29caa070ec048aba042f134091923d9ca2ac662b5604577e"}, + {file = "rapidfuzz-3.7.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:86eea3e6c314a9238de568254a9c591ec73c2985f125675ed5f171d869c47773"}, + {file = "rapidfuzz-3.7.0.tar.gz", hash = "sha256:620df112c39c6d27316dc1e22046dc0382d6d91fd60d7c51bd41ca0333d867e9"}, ] [package.extras] @@ -5749,60 +5685,60 @@ test = ["pytest"] [[package]] name = "sqlalchemy" -version = "2.0.28" +version = "2.0.29" description = "Database Abstraction Library" optional = true python-versions = ">=3.7" files = [ - {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0b148ab0438f72ad21cb004ce3bdaafd28465c4276af66df3b9ecd2037bf252"}, - {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bbda76961eb8f27e6ad3c84d1dc56d5bc61ba8f02bd20fcf3450bd421c2fcc9c"}, - {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feea693c452d85ea0015ebe3bb9cd15b6f49acc1a31c28b3c50f4db0f8fb1e71"}, - {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5da98815f82dce0cb31fd1e873a0cb30934971d15b74e0d78cf21f9e1b05953f"}, - {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5adf383c73f2d49ad15ff363a8748319ff84c371eed59ffd0127355d6ea1da"}, - {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56856b871146bfead25fbcaed098269d90b744eea5cb32a952df00d542cdd368"}, - {file = "SQLAlchemy-2.0.28-cp310-cp310-win32.whl", hash = "sha256:943aa74a11f5806ab68278284a4ddd282d3fb348a0e96db9b42cb81bf731acdc"}, - {file = "SQLAlchemy-2.0.28-cp310-cp310-win_amd64.whl", hash = "sha256:c6c4da4843e0dabde41b8f2e8147438330924114f541949e6318358a56d1875a"}, - {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46a3d4e7a472bfff2d28db838669fc437964e8af8df8ee1e4548e92710929adc"}, - {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3dd67b5d69794cfe82862c002512683b3db038b99002171f624712fa71aeaa"}, - {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61e2e41656a673b777e2f0cbbe545323dbe0d32312f590b1bc09da1de6c2a02"}, - {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0315d9125a38026227f559488fe7f7cee1bd2fbc19f9fd637739dc50bb6380b2"}, - {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af8ce2d31679006e7b747d30a89cd3ac1ec304c3d4c20973f0f4ad58e2d1c4c9"}, - {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:81ba314a08c7ab701e621b7ad079c0c933c58cdef88593c59b90b996e8b58fa5"}, - {file = "SQLAlchemy-2.0.28-cp311-cp311-win32.whl", hash = "sha256:1ee8bd6d68578e517943f5ebff3afbd93fc65f7ef8f23becab9fa8fb315afb1d"}, - {file = "SQLAlchemy-2.0.28-cp311-cp311-win_amd64.whl", hash = "sha256:ad7acbe95bac70e4e687a4dc9ae3f7a2f467aa6597049eeb6d4a662ecd990bb6"}, - {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d3499008ddec83127ab286c6f6ec82a34f39c9817f020f75eca96155f9765097"}, - {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9b66fcd38659cab5d29e8de5409cdf91e9986817703e1078b2fdaad731ea66f5"}, - {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea30da1e76cb1acc5b72e204a920a3a7678d9d52f688f087dc08e54e2754c67"}, - {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:124202b4e0edea7f08a4db8c81cc7859012f90a0d14ba2bf07c099aff6e96462"}, - {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e23b88c69497a6322b5796c0781400692eca1ae5532821b39ce81a48c395aae9"}, - {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b6303bfd78fb3221847723104d152e5972c22367ff66edf09120fcde5ddc2e2"}, - {file = "SQLAlchemy-2.0.28-cp312-cp312-win32.whl", hash = "sha256:a921002be69ac3ab2cf0c3017c4e6a3377f800f1fca7f254c13b5f1a2f10022c"}, - {file = "SQLAlchemy-2.0.28-cp312-cp312-win_amd64.whl", hash = "sha256:b4a2cf92995635b64876dc141af0ef089c6eea7e05898d8d8865e71a326c0385"}, - {file = "SQLAlchemy-2.0.28-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e91b5e341f8c7f1e5020db8e5602f3ed045a29f8e27f7f565e0bdee3338f2c7"}, - {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c7b78dfc7278329f27be02c44abc0d69fe235495bb8e16ec7ef1b1a17952db"}, - {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3eba73ef2c30695cb7eabcdb33bb3d0b878595737479e152468f3ba97a9c22a4"}, - {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5df5d1dafb8eee89384fb7a1f79128118bc0ba50ce0db27a40750f6f91aa99d5"}, - {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2858bbab1681ee5406650202950dc8f00e83b06a198741b7c656e63818633526"}, - {file = "SQLAlchemy-2.0.28-cp37-cp37m-win32.whl", hash = "sha256:9461802f2e965de5cff80c5a13bc945abea7edaa1d29360b485c3d2b56cdb075"}, - {file = "SQLAlchemy-2.0.28-cp37-cp37m-win_amd64.whl", hash = "sha256:a6bec1c010a6d65b3ed88c863d56b9ea5eeefdf62b5e39cafd08c65f5ce5198b"}, - {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:843a882cadebecc655a68bd9a5b8aa39b3c52f4a9a5572a3036fb1bb2ccdc197"}, - {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dbb990612c36163c6072723523d2be7c3eb1517bbdd63fe50449f56afafd1133"}, - {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7e4baf9161d076b9a7e432fce06217b9bd90cfb8f1d543d6e8c4595627edb9"}, - {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0a5354cb4de9b64bccb6ea33162cb83e03dbefa0d892db88a672f5aad638a75"}, - {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fffcc8edc508801ed2e6a4e7b0d150a62196fd28b4e16ab9f65192e8186102b6"}, - {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aca7b6d99a4541b2ebab4494f6c8c2f947e0df4ac859ced575238e1d6ca5716b"}, - {file = "SQLAlchemy-2.0.28-cp38-cp38-win32.whl", hash = "sha256:8c7f10720fc34d14abad5b647bc8202202f4948498927d9f1b4df0fb1cf391b7"}, - {file = "SQLAlchemy-2.0.28-cp38-cp38-win_amd64.whl", hash = "sha256:243feb6882b06a2af68ecf4bec8813d99452a1b62ba2be917ce6283852cf701b"}, - {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fc4974d3684f28b61b9a90fcb4c41fb340fd4b6a50c04365704a4da5a9603b05"}, - {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87724e7ed2a936fdda2c05dbd99d395c91ea3c96f029a033a4a20e008dd876bf"}, - {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68722e6a550f5de2e3cfe9da6afb9a7dd15ef7032afa5651b0f0c6b3adb8815d"}, - {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:328529f7c7f90adcd65aed06a161851f83f475c2f664a898af574893f55d9e53"}, - {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:df40c16a7e8be7413b885c9bf900d402918cc848be08a59b022478804ea076b8"}, - {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:426f2fa71331a64f5132369ede5171c52fd1df1bd9727ce621f38b5b24f48750"}, - {file = "SQLAlchemy-2.0.28-cp39-cp39-win32.whl", hash = "sha256:33157920b233bc542ce497a81a2e1452e685a11834c5763933b440fedd1d8e2d"}, - {file = "SQLAlchemy-2.0.28-cp39-cp39-win_amd64.whl", hash = "sha256:2f60843068e432311c886c5f03c4664acaef507cf716f6c60d5fde7265be9d7b"}, - {file = "SQLAlchemy-2.0.28-py3-none-any.whl", hash = "sha256:78bb7e8da0183a8301352d569900d9d3594c48ac21dc1c2ec6b3121ed8b6c986"}, - {file = "SQLAlchemy-2.0.28.tar.gz", hash = "sha256:dd53b6c4e6d960600fd6532b79ee28e2da489322fcf6648738134587faf767b6"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4c142852ae192e9fe5aad5c350ea6befe9db14370b34047e1f0f7cf99e63c63b"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:99a1e69d4e26f71e750e9ad6fdc8614fbddb67cfe2173a3628a2566034e223c7"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ef3fbccb4058355053c51b82fd3501a6e13dd808c8d8cd2561e610c5456013c"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d6753305936eddc8ed190e006b7bb33a8f50b9854823485eed3a886857ab8d1"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0f3ca96af060a5250a8ad5a63699180bc780c2edf8abf96c58af175921df847a"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c4520047006b1d3f0d89e0532978c0688219857eb2fee7c48052560ae76aca1e"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-win32.whl", hash = "sha256:b2a0e3cf0caac2085ff172c3faacd1e00c376e6884b5bc4dd5b6b84623e29e4f"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-win_amd64.whl", hash = "sha256:01d10638a37460616708062a40c7b55f73e4d35eaa146781c683e0fa7f6c43fb"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:308ef9cb41d099099fffc9d35781638986870b29f744382904bf9c7dadd08513"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:296195df68326a48385e7a96e877bc19aa210e485fa381c5246bc0234c36c78e"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a13b917b4ffe5a0a31b83d051d60477819ddf18276852ea68037a144a506efb9"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f6d971255d9ddbd3189e2e79d743ff4845c07f0633adfd1de3f63d930dbe673"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:61405ea2d563407d316c63a7b5271ae5d274a2a9fbcd01b0aa5503635699fa1e"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:de7202ffe4d4a8c1e3cde1c03e01c1a3772c92858837e8f3879b497158e4cb44"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-win32.whl", hash = "sha256:b5d7ed79df55a731749ce65ec20d666d82b185fa4898430b17cb90c892741520"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-win_amd64.whl", hash = "sha256:205f5a2b39d7c380cbc3b5dcc8f2762fb5bcb716838e2d26ccbc54330775b003"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d96710d834a6fb31e21381c6d7b76ec729bd08c75a25a5184b1089141356171f"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:52de4736404e53c5c6a91ef2698c01e52333988ebdc218f14c833237a0804f1b"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c7b02525ede2a164c5fa5014915ba3591730f2cc831f5be9ff3b7fd3e30958e"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dfefdb3e54cd15f5d56fd5ae32f1da2d95d78319c1f6dfb9bcd0eb15d603d5d"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a88913000da9205b13f6f195f0813b6ffd8a0c0c2bd58d499e00a30eb508870c"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fecd5089c4be1bcc37c35e9aa678938d2888845a134dd016de457b942cf5a758"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-win32.whl", hash = "sha256:8197d6f7a3d2b468861ebb4c9f998b9df9e358d6e1cf9c2a01061cb9b6cf4e41"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-win_amd64.whl", hash = "sha256:9b19836ccca0d321e237560e475fd99c3d8655d03da80c845c4da20dda31b6e1"}, + {file = "SQLAlchemy-2.0.29-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:87a1d53a5382cdbbf4b7619f107cc862c1b0a4feb29000922db72e5a66a5ffc0"}, + {file = "SQLAlchemy-2.0.29-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a0732dffe32333211801b28339d2a0babc1971bc90a983e3035e7b0d6f06b93"}, + {file = "SQLAlchemy-2.0.29-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90453597a753322d6aa770c5935887ab1fc49cc4c4fdd436901308383d698b4b"}, + {file = "SQLAlchemy-2.0.29-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ea311d4ee9a8fa67f139c088ae9f905fcf0277d6cd75c310a21a88bf85e130f5"}, + {file = "SQLAlchemy-2.0.29-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:5f20cb0a63a3e0ec4e169aa8890e32b949c8145983afa13a708bc4b0a1f30e03"}, + {file = "SQLAlchemy-2.0.29-cp37-cp37m-win32.whl", hash = "sha256:e5bbe55e8552019c6463709b39634a5fc55e080d0827e2a3a11e18eb73f5cdbd"}, + {file = "SQLAlchemy-2.0.29-cp37-cp37m-win_amd64.whl", hash = "sha256:c2f9c762a2735600654c654bf48dad388b888f8ce387b095806480e6e4ff6907"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7e614d7a25a43a9f54fcce4675c12761b248547f3d41b195e8010ca7297c369c"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:471fcb39c6adf37f820350c28aac4a7df9d3940c6548b624a642852e727ea586"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:988569c8732f54ad3234cf9c561364221a9e943b78dc7a4aaf35ccc2265f1930"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dddaae9b81c88083e6437de95c41e86823d150f4ee94bf24e158a4526cbead01"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:334184d1ab8f4c87f9652b048af3f7abea1c809dfe526fb0435348a6fef3d380"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:38b624e5cf02a69b113c8047cf7f66b5dfe4a2ca07ff8b8716da4f1b3ae81567"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-win32.whl", hash = "sha256:bab41acf151cd68bc2b466deae5deeb9e8ae9c50ad113444151ad965d5bf685b"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-win_amd64.whl", hash = "sha256:52c8011088305476691b8750c60e03b87910a123cfd9ad48576d6414b6ec2a1d"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3071ad498896907a5ef756206b9dc750f8e57352113c19272bdfdc429c7bd7de"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dba622396a3170974f81bad49aacebd243455ec3cc70615aeaef9e9613b5bca5"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b184e3de58009cc0bf32e20f137f1ec75a32470f5fede06c58f6c355ed42a72"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c37f1050feb91f3d6c32f864d8e114ff5545a4a7afe56778d76a9aec62638ba"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bda7ce59b06d0f09afe22c56714c65c957b1068dee3d5e74d743edec7daba552"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:25664e18bef6dc45015b08f99c63952a53a0a61f61f2e48a9e70cec27e55f699"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-win32.whl", hash = "sha256:77d29cb6c34b14af8a484e831ab530c0f7188f8efed1c6a833a2c674bf3c26ec"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-win_amd64.whl", hash = "sha256:04c487305ab035a9548f573763915189fc0fe0824d9ba28433196f8436f1449c"}, + {file = "SQLAlchemy-2.0.29-py3-none-any.whl", hash = "sha256:dc4ee2d4ee43251905f88637d5281a8d52e916a021384ec10758826f5cbae305"}, + {file = "SQLAlchemy-2.0.29.tar.gz", hash = "sha256:bd9566b8e58cabd700bc367b60e90d9349cd16f0984973f98a9a09f9c64e86f0"}, ] [package.dependencies] @@ -5890,15 +5826,12 @@ full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7 [[package]] name = "streamlit" version = "1.32.2" -version = "1.32.2" description = "A faster way to build and share data apps" optional = false python-versions = ">=3.8, !=3.9.7" files = [ {file = "streamlit-1.32.2-py2.py3-none-any.whl", hash = "sha256:a0b8044e76fec364b07be145f8b40dbd8d083e20ebbb189ceb1fa9423f3dedea"}, {file = "streamlit-1.32.2.tar.gz", hash = "sha256:1258b9cbc3ff957bf7d09b1bfc85cedc308f1065b30748545295a9af8d5577ab"}, - {file = "streamlit-1.32.2-py2.py3-none-any.whl", hash = "sha256:a0b8044e76fec364b07be145f8b40dbd8d083e20ebbb189ceb1fa9423f3dedea"}, - {file = "streamlit-1.32.2.tar.gz", hash = "sha256:1258b9cbc3ff957bf7d09b1bfc85cedc308f1065b30748545295a9af8d5577ab"}, ] [package.dependencies] @@ -6124,35 +6057,32 @@ test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0, [[package]] name = "trove-classifiers" -version = "2024.3.3" +version = "2024.3.25" description = "Canonical source for classifiers on PyPI (pypi.org)." optional = false python-versions = "*" files = [ - {file = "trove-classifiers-2024.3.3.tar.gz", hash = "sha256:df7edff9c67ff86b733628998330b180e81d125b1e096536d83ac0fd79673fdc"}, - {file = "trove_classifiers-2024.3.3-py3-none-any.whl", hash = "sha256:3a84096861b385ec422c79995d1f6435dde47a9b63adaa3c886e53232ba7e6e0"}, + {file = "trove-classifiers-2024.3.25.tar.gz", hash = "sha256:6de68d06edd6fec5032162b6af22e818a4bb6f4ae2258e74699f8a41064b7cad"}, + {file = "trove_classifiers-2024.3.25-py3-none-any.whl", hash = "sha256:c400e0bdceb018913339d53b07682d09a42aada687d070e90ee3c08477bec024"}, ] [[package]] name = "typer" -version = "0.9.0" +version = "0.11.0" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "typer-0.9.0-py3-none-any.whl", hash = "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee"}, - {file = "typer-0.9.0.tar.gz", hash = "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2"}, + {file = "typer-0.11.0-py3-none-any.whl", hash = "sha256:049cc47bef39f46b043eddd9165492209fdd9bc7d79afa7ba9cc5cd017caa817"}, + {file = "typer-0.11.0.tar.gz", hash = "sha256:a6ce173c0f03d3a41b49c0a945874cc489e91f88faabf76517b2b91c670fcde7"}, ] [package.dependencies] -click = ">=7.1.1,<9.0.0" +click = ">=8.0.0" typing-extensions = ">=3.7.4.3" [package.extras] all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] -dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] -doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"] -test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] [[package]] name = "types-python-dateutil" @@ -6219,13 +6149,13 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "uvicorn" -version = "0.28.0" +version = "0.29.0" description = "The lightning-fast ASGI server." optional = false python-versions = ">=3.8" files = [ - {file = "uvicorn-0.28.0-py3-none-any.whl", hash = "sha256:6623abbbe6176204a4226e67607b4d52cc60ff62cda0ff177613645cefa2ece1"}, - {file = "uvicorn-0.28.0.tar.gz", hash = "sha256:cab4473b5d1eaeb5a0f6375ac4bc85007ffc75c3cc1768816d9e5d589857b067"}, + {file = "uvicorn-0.29.0-py3-none-any.whl", hash = "sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de"}, + {file = "uvicorn-0.29.0.tar.gz", hash = "sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0"}, ] [package.dependencies] From 8c90763ed1aadc50af2b5a8a69d40780d11492c4 Mon Sep 17 00:00:00 2001 From: pseusys Date: Wed, 27 Mar 2024 14:01:37 +0100 Subject: [PATCH 034/140] tuples replaced with sets everywhere --- dff/messengers/common/interface.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dff/messengers/common/interface.py b/dff/messengers/common/interface.py index e7cd902a6..1530d630e 100644 --- a/dff/messengers/common/interface.py +++ b/dff/messengers/common/interface.py @@ -26,8 +26,8 @@ class MessengerInterface(abc.ABC): It is responsible for connection between user and pipeline, as well as for request-response transactions. """ - accepts_attachments = tuple() - produces_attachments = tuple() + request_attachments = set() + response_attachments = set() @abc.abstractmethod async def connect(self, pipeline_runner: PipelineRunnerFunction): From 30db35a6cd7d1f65edb5be200571ded1603b030a Mon Sep 17 00:00:00 2001 From: pseusys Date: Thu, 4 Apr 2024 21:57:25 +0200 Subject: [PATCH 035/140] telegram-specific request attachments added --- dff/messengers/telegram/__init__.py | 12 ++ dff/messengers/telegram/abstract.py | 273 +++++++++++++++++++++++++ dff/messengers/telegram/attachments.py | 54 +++++ dff/messengers/telegram/interface.py | 29 +++ 4 files changed, 368 insertions(+) create mode 100644 dff/messengers/telegram/__init__.py create mode 100644 dff/messengers/telegram/abstract.py create mode 100644 dff/messengers/telegram/attachments.py create mode 100644 dff/messengers/telegram/interface.py diff --git a/dff/messengers/telegram/__init__.py b/dff/messengers/telegram/__init__.py new file mode 100644 index 000000000..dd8a7cdfd --- /dev/null +++ b/dff/messengers/telegram/__init__.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- + +from .attachments import ( + TelegramContact, + TelegramPoll, + TelegramAudio, + TelegramVideo, + TelegramAnimation, + TelegramImage, + TelegramDocument, +) +from .interface import PollingTelegramInterface, CallbackTelegramInterface diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py new file mode 100644 index 000000000..b23d06681 --- /dev/null +++ b/dff/messengers/telegram/abstract.py @@ -0,0 +1,273 @@ +""" +Interface +------------ +This module implements various interfaces for :py:class:`~dff.messengers.telegram.messenger.TelegramMessenger` +that can be used to interact with the Telegram API. +""" + +from pathlib import Path +from tempfile import gettempdir +from typing import Callable, Optional, Sequence +from pydantic import FilePath + +from telegram import ( + InlineKeyboardButton, + InlineKeyboardMarkup, + InputMediaAnimation, + InputMediaAudio, + InputMediaDocument, + InputMediaPhoto, + InputMediaVideo, + Update, + Message as TelegramMessage, +) +from telegram.ext import Application, ExtBot, MessageHandler, CallbackQueryHandler, ContextTypes +from telegram.ext.filters import ALL + +from dff.messengers.common import MessengerInterface +from dff.pipeline.types import PipelineRunnerFunction +from dff.script.core.message import ( + Animation, + Audio, + Button, + CallbackQuery, + Contact, + DataAttachment, + Document, + Image, + Invoice, + Keyboard, + Location, + Message, + Poll, + PollOption, + Video, +) +from .attachments import ( + TelegramContact, + TelegramPoll, + TelegramAudio, + TelegramVideo, + TelegramAnimation, + TelegramImage, + TelegramDocument, +) + + +class _AbstractTelegramInterface(MessengerInterface): # pragma: no cover + request_attachments = {Location, Contact, Invoice, Poll, Audio, Video, Animation, Image, Document} + response_attachments = {Location, Contact, Poll, Audio, Video, Animation, Image, Document, Keyboard} + + def __init__(self, token: str) -> None: + self.application = Application.builder().token(token).build() + self.application.add_handler(MessageHandler(ALL, self.on_message)) + self.application.add_handler(CallbackQueryHandler(self.on_callback)) + + async def populate_attachment(self, attachment: DataAttachment) -> None: # pragma: no cover + if attachment.title is not None and attachment.id is not None: + file_name = Path(gettempdir()) / str(attachment.title) + if not file_name.exists(): + await (await self.application.bot.get_file(attachment.id)).download_to_drive(file_name) + attachment.source = FilePath(file_name) + else: + raise ValueError(f"For attachment {attachment} title or id is not defined!") + + def extract_message_from_telegram(self, update: TelegramMessage) -> Message: # pragma: no cover + message = Message() + message.attachments = list() + + if update.text is not None: + message.text = update.text + if update.location is not None: + message.attachments += [Location(latitude=update.location.latitude, longitude=update.location.longitude)] + if update.contact is not None: + message.attachments += [ + TelegramContact( + phone_number=update.contact.phone_number, + first_name=update.contact.first_name, + last_name=update.contact.last_name, + user_id=update.contact.user_id, + ) + ] + if update.invoice is not None: + message.attachments += [ + Invoice( + title=update.invoice.title, + description=update.invoice.description, + currency=update.invoice.currency, + amount=update.invoice.total_amount, + ) + ] + if update.poll is not None: + message.attachments += [ + TelegramPoll( + question=update.poll.question, + options=[PollOption(text=option.text, votes=option.voter_count) for option in update.poll.options], + is_closed=update.poll.is_closed, + is_anonymous=update.poll.is_anonymous, + type=update.poll.type, + multiple_answers=update.poll.allows_multiple_answers, + correct_option_id=update.poll.correct_option_id, + explanation=update.poll.explanation, + open_period=update.poll.open_period, + ) + ] + if update.audio is not None: + thumbnail = Image(id=update.audio.thumbnail.file_id, title=update.audio.thumbnail.file_unique_id) if update.audio.thumbnail is not None else None + message.attachments += [ + TelegramAudio( + id=update.audio.file_id, + title=update.audio.file_unique_id, + duration=update.audio.duration, + performer=update.audio.performer, + file_name=update.audio.file_name, + mime_type=update.audio.mime_type, + thumbnail=thumbnail, + ) + ] + if update.video is not None: + thumbnail = Image(id=update.video.thumbnail.file_id, title=update.video.thumbnail.file_unique_id) if update.video.thumbnail is not None else None + message.attachments += [ + TelegramVideo( + id=update.video.file_id, + title=update.video.file_unique_id, + width=update.video.width, + height=update.video.height, + duration=update.video.duration, + file_name=update.video.file_name, + mime_type=update.video.mime_type, + thumbnail=thumbnail, + ) + ] + if update.animation is not None: + thumbnail = Image(id=update.animation.thumbnail.file_id, title=update.animation.thumbnail.file_unique_id) if update.animation.thumbnail is not None else None + message.attachments += [ + TelegramAnimation( + id=update.animation.file_id, + title=update.animation.file_unique_id, + width=update.animation.width, + height=update.animation.height, + duration=update.animation.duration, + file_name=update.animation.file_name, + mime_type=update.animation.mime_type, + thumbnail=thumbnail, + ) + ] + if len(update.photo) > 0: + message.attachments += [ + TelegramImage( + id=picture.file_id, + title=picture.file_unique_id, + width=picture.width, + height=picture.height, + ) for picture in update.photo + ] + if update.document is not None: + thumbnail = Image(id=update.document.thumbnail.file_id, title=update.document.thumbnail.file_unique_id) if update.document.thumbnail is not None else None + message.attachments += [ + TelegramDocument( + id=update.document.file_id, + title=update.document.file_unique_id, + file_name=update.document.file_name, + mime_type=update.document.mime_type, + thumbnail=thumbnail, + ) + ] + + return message + + def _create_keyboard( + self, buttons: Sequence[Sequence[Button]] + ) -> Optional[InlineKeyboardMarkup]: # pragma: no cover + button_list = None + if len(buttons) > 0: + button_list = [ + [ + InlineKeyboardButton( + text=button.text, callback_data=button.data if button.data is not None else button.text + ) + for button in row + ] + for row in buttons + ] + if button_list is None: + return None + else: + return InlineKeyboardMarkup(button_list) + + async def cast_message_to_telegram_and_send( + self, bot: ExtBot, chat_id: int, message: Message + ) -> None: # pragma: no cover + buttons = list() + if message.attachments is not None: + files = list() + for attachment in message.attachments: + if isinstance(attachment, Location): + await bot.send_location( + chat_id, attachment.latitude, attachment.longitude, reply_markup=self._create_keyboard(buttons) + ) + if isinstance(attachment, Contact): + await bot.send_contact( + chat_id, + attachment.phone_number, + attachment.first_name, + attachment.last_name, + reply_markup=self._create_keyboard(buttons), + ) + if isinstance(attachment, Poll): + await bot.send_poll( + chat_id, + attachment.question, + [option.text for option in attachment.options], + reply_markup=self._create_keyboard(buttons), + ) + if isinstance(attachment, Audio): + attachment_bytes = await attachment.get_bytes(self) + if attachment_bytes is not None: + files += [InputMediaAudio(attachment_bytes)] + if isinstance(attachment, Video): + attachment_bytes = await attachment.get_bytes(self) + if attachment_bytes is not None: + files += [InputMediaVideo(attachment_bytes)] + if isinstance(attachment, Animation): + attachment_bytes = await attachment.get_bytes(self) + if attachment_bytes is not None: + files += [InputMediaAnimation(attachment_bytes)] + if isinstance(attachment, Image): + attachment_bytes = await attachment.get_bytes(self) + if attachment_bytes is not None: + files += [InputMediaPhoto(attachment_bytes)] + if isinstance(attachment, Document): + attachment_bytes = await attachment.get_bytes(self) + if attachment_bytes is not None: + files += [InputMediaDocument(attachment_bytes)] + if isinstance(attachment, Keyboard): + buttons = attachment.buttons + if len(files) > 0: + await bot.send_media_group(chat_id, files, caption=message.text) + return + if message.text is not None: + await bot.send_message(chat_id, message.text, reply_markup=self._create_keyboard(buttons)) + + async def _on_event( + self, update: Update, _: ContextTypes.DEFAULT_TYPE, create_message: Callable[[Update], Message] + ) -> None: + if update.effective_chat is not None and update.message is not None: + message = create_message(update) + message.original_message = update + resp = await self.callback(message, update.effective_chat.id) + if resp.last_response is not None: + await self.cast_message_to_telegram_and_send( + self.application.bot, update.effective_chat.id, resp.last_response + ) + + async def on_message(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> None: + await self._on_event(update, _, lambda u: self.extract_message_from_telegram(u.message)) + + async def on_callback(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> None: + await self._on_event( + update, _, lambda u: Message(attachments=[CallbackQuery(query_string=u.callback_query.data)]) + ) + + async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs): + self.callback = pipeline_runner diff --git a/dff/messengers/telegram/attachments.py b/dff/messengers/telegram/attachments.py new file mode 100644 index 000000000..4340ca996 --- /dev/null +++ b/dff/messengers/telegram/attachments.py @@ -0,0 +1,54 @@ +from typing import Optional + +from dff.script.core.message import Animation, Audio, Contact, Document, Image, Poll, Video + + +class TelegramContact(Contact): + user_id: Optional[int] + + +class TelegramPoll(Poll): + is_closed: bool + is_anonymous: bool + type: str + multiple_answers: bool + correct_option_id: Optional[int] + explanation: Optional[str] + open_period: Optional[int] + + +class TelegramAudio(Audio): + duration: int + performer: Optional[str] + file_name: Optional[str] + mime_type: Optional[str] + thumbnail: Optional[Image] + + +class TelegramVideo(Video): + width: int + height: int + duration: int + file_name: Optional[str] + mime_type: Optional[str] + thumbnail: Optional[Image] + + +class TelegramAnimation(Animation): + width: int + height: int + duration: int + file_name: Optional[str] + mime_type: Optional[str] + thumbnail: Optional[Image] + + +class TelegramImage(Image): + width: int + height: int + + +class TelegramDocument(Document): + file_name: Optional[str] + mime_type: Optional[str] + thumbnail: Optional[Image] diff --git a/dff/messengers/telegram/interface.py b/dff/messengers/telegram/interface.py new file mode 100644 index 000000000..76597a65e --- /dev/null +++ b/dff/messengers/telegram/interface.py @@ -0,0 +1,29 @@ +from telegram import Update + +from dff.pipeline.types import PipelineRunnerFunction + +from .abstract import _AbstractTelegramInterface + + +class PollingTelegramInterface(_AbstractTelegramInterface): # pragma: no cover + def __init__(self, token: str, interval: int = 2, timeout: int = 20) -> None: + super().__init__(token) + self.interval = interval + self.timeout = timeout + + async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs): + await super().connect(pipeline_runner, *args, **kwargs) + self.application.run_polling( + poll_interval=self.interval, timeout=self.timeout, allowed_updates=Update.ALL_TYPES + ) + + +class CallbackTelegramInterface(_AbstractTelegramInterface): # pragma: no cover + def __init__(self, token: str, host: str = "localhost", port: int = 844): + super().__init__(token) + self.listen = host + self.port = port + + async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs): + await super().connect(pipeline_runner, *args, **kwargs) + self.application.run_webhook(listen=self.listen, port=self.port, allowed_updates=Update.ALL_TYPES) From 1432893e768ae9fccdca18cb16e6df4c1e48ccc0 Mon Sep 17 00:00:00 2001 From: pseusys Date: Wed, 10 Apr 2024 00:13:49 +0200 Subject: [PATCH 036/140] attachment subclasses moved to extras --- dff/messengers/telegram.py | 244 ------------------------- dff/messengers/telegram/abstract.py | 23 +-- dff/messengers/telegram/attachments.py | 54 ------ 3 files changed, 7 insertions(+), 314 deletions(-) delete mode 100644 dff/messengers/telegram.py delete mode 100644 dff/messengers/telegram/attachments.py diff --git a/dff/messengers/telegram.py b/dff/messengers/telegram.py deleted file mode 100644 index cdb9faeff..000000000 --- a/dff/messengers/telegram.py +++ /dev/null @@ -1,244 +0,0 @@ -""" -Interface ------------- -This module implements various interfaces for :py:class:`~dff.messengers.telegram.messenger.TelegramMessenger` -that can be used to interact with the Telegram API. -""" - -from pathlib import Path -from tempfile import gettempdir -from typing import Callable, Optional, Sequence -from pydantic import FilePath - -from telegram import ( - InlineKeyboardButton, - InlineKeyboardMarkup, - InputMediaAnimation, - InputMediaAudio, - InputMediaDocument, - InputMediaPhoto, - InputMediaVideo, - Update, - Message as TelegramMessage, -) -from telegram.ext import Application, ExtBot, MessageHandler, CallbackQueryHandler, ContextTypes -from telegram.ext.filters import ALL - -from dff.messengers.common import MessengerInterface -from dff.pipeline.types import PipelineRunnerFunction -from dff.script.core.message import ( - Animation, - Audio, - Button, - CallbackQuery, - Contact, - DataAttachment, - Document, - Image, - Invoice, - Keyboard, - Location, - Message, - Poll, - PollOption, - Video, -) - - -class _AbstractTelegramInterface(MessengerInterface): # pragma: no cover - request_attachments = {Location, Contact, Invoice, Poll, Audio, Video, Animation, Image, Document} - response_attachments = {Location, Contact, Poll, Audio, Video, Animation, Image, Document, Keyboard} - - def __init__(self, token: str) -> None: - self.application = Application.builder().token(token).build() - self.application.add_handler(MessageHandler(ALL, self.on_message)) - self.application.add_handler(CallbackQueryHandler(self.on_callback)) - - async def populate_attachment(self, attachment: DataAttachment) -> None: # pragma: no cover - if attachment.title is not None and attachment.id is not None: - file_name = Path(gettempdir()) / str(attachment.title) - if not file_name.exists(): - await (await self.application.bot.get_file(attachment.id)).download_to_drive(file_name) - attachment.source = FilePath(file_name) - else: - raise ValueError(f"For attachment {attachment} title or id is not defined!") - - def extract_message_from_telegram(self, update: TelegramMessage) -> Message: # pragma: no cover - message = Message() - message.attachments = list() - - if update.text is not None: - message.text = update.text - if update.location is not None: - message.attachments += [Location(latitude=update.location.latitude, longitude=update.location.longitude)] - if update.contact is not None: - message.attachments += [ - Contact( - phone_number=update.contact.phone_number, - first_name=update.contact.first_name, - last_name=update.contact.last_name, - ) - ] - if update.invoice is not None: - message.attachments += [ - Invoice( - title=update.invoice.title, - description=update.invoice.description, - currency=update.invoice.currency, - amount=update.invoice.total_amount, - ) - ] - if update.poll is not None: - message.attachments += [ - Poll( - question=update.poll.question, - options=[PollOption(text=option.text, votes=option.voter_count) for option in update.poll.options], - ) - ] - if update.audio is not None: - message.attachments += [ - Audio(id=update.audio.file_id, title=update.audio.file_unique_id, from_messenger_interface=self) - ] - if update.video is not None: - message.attachments += [ - Video(id=update.video.file_id, title=update.video.file_unique_id, from_messenger_interface=self) - ] - if update.animation is not None: - message.attachments += [ - Animation( - id=update.animation.file_id, title=update.animation.file_unique_id, from_messenger_interface=self - ) - ] - if len(update.photo) > 0: - message.attachments += [ - Image(id=picture.file_id, title=picture.file_unique_id, from_messenger_interface=self) - for picture in update.photo - ] - if update.document is not None: - message.attachments += [ - Document( - id=update.document.file_id, title=update.document.file_unique_id, from_messenger_interface=self - ) - ] - - return message - - def _create_keyboard( - self, buttons: Sequence[Sequence[Button]] - ) -> Optional[InlineKeyboardMarkup]: # pragma: no cover - button_list = None - if len(buttons) > 0: - button_list = [ - [ - InlineKeyboardButton( - text=button.text, callback_data=button.data if button.data is not None else button.text - ) - for button in row - ] - for row in buttons - ] - if button_list is None: - return None - else: - return InlineKeyboardMarkup(button_list) - - async def cast_message_to_telegram_and_send( - self, bot: ExtBot, chat_id: int, message: Message - ) -> None: # pragma: no cover - buttons = list() - if message.attachments is not None: - files = list() - for attachment in message.attachments: - if isinstance(attachment, Location): - await bot.send_location( - chat_id, attachment.latitude, attachment.longitude, reply_markup=self._create_keyboard(buttons) - ) - if isinstance(attachment, Contact): - await bot.send_contact( - chat_id, - attachment.phone_number, - attachment.first_name, - attachment.last_name, - reply_markup=self._create_keyboard(buttons), - ) - if isinstance(attachment, Poll): - await bot.send_poll( - chat_id, - attachment.question, - [option.text for option in attachment.options], - reply_markup=self._create_keyboard(buttons), - ) - if isinstance(attachment, Audio): - attachment_bytes = await attachment.get_bytes(self) - if attachment_bytes is not None: - files += [InputMediaAudio(attachment_bytes)] - if isinstance(attachment, Video): - attachment_bytes = await attachment.get_bytes(self) - if attachment_bytes is not None: - files += [InputMediaVideo(attachment_bytes)] - if isinstance(attachment, Animation): - attachment_bytes = await attachment.get_bytes(self) - if attachment_bytes is not None: - files += [InputMediaAnimation(attachment_bytes)] - if isinstance(attachment, Image): - attachment_bytes = await attachment.get_bytes(self) - if attachment_bytes is not None: - files += [InputMediaPhoto(attachment_bytes)] - if isinstance(attachment, Document): - attachment_bytes = await attachment.get_bytes(self) - if attachment_bytes is not None: - files += [InputMediaDocument(attachment_bytes)] - if isinstance(attachment, Keyboard): - buttons = attachment.buttons - if len(files) > 0: - await bot.send_media_group(chat_id, files, caption=message.text) - return - if message.text is not None: - await bot.send_message(chat_id, message.text, reply_markup=self._create_keyboard(buttons)) - - async def _on_event( - self, update: Update, _: ContextTypes.DEFAULT_TYPE, create_message: Callable[[Update], Message] - ) -> None: - if update.effective_chat is not None and update.message is not None: - message = create_message(update) - message.original_message = update - resp = await self.callback(message, update.effective_chat.id) - if resp.last_response is not None: - await self.cast_message_to_telegram_and_send( - self.application.bot, update.effective_chat.id, resp.last_response - ) - - async def on_message(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> None: - await self._on_event(update, _, lambda u: self.extract_message_from_telegram(u.message)) - - async def on_callback(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> None: - await self._on_event( - update, _, lambda u: Message(attachments=[CallbackQuery(query_string=u.callback_query.data)]) - ) - - async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs): - self.callback = pipeline_runner - - -class PollingTelegramInterface(_AbstractTelegramInterface): # pragma: no cover - def __init__(self, token: str, interval: int = 2, timeout: int = 20) -> None: - super().__init__(token) - self.interval = interval - self.timeout = timeout - - async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs): - await super().connect(pipeline_runner, *args, **kwargs) - self.application.run_polling( - poll_interval=self.interval, timeout=self.timeout, allowed_updates=Update.ALL_TYPES - ) - - -class CallbackTelegramInterface(_AbstractTelegramInterface): # pragma: no cover - def __init__(self, token: str, host: str = "localhost", port: int = 844): - super().__init__(token) - self.listen = host - self.port = port - - async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs): - await super().connect(pipeline_runner, *args, **kwargs) - self.application.run_webhook(listen=self.listen, port=self.port, allowed_updates=Update.ALL_TYPES) diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index b23d06681..da0d2ab44 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -43,15 +43,6 @@ PollOption, Video, ) -from .attachments import ( - TelegramContact, - TelegramPoll, - TelegramAudio, - TelegramVideo, - TelegramAnimation, - TelegramImage, - TelegramDocument, -) class _AbstractTelegramInterface(MessengerInterface): # pragma: no cover @@ -82,7 +73,7 @@ def extract_message_from_telegram(self, update: TelegramMessage) -> Message: # message.attachments += [Location(latitude=update.location.latitude, longitude=update.location.longitude)] if update.contact is not None: message.attachments += [ - TelegramContact( + Contact( phone_number=update.contact.phone_number, first_name=update.contact.first_name, last_name=update.contact.last_name, @@ -100,7 +91,7 @@ def extract_message_from_telegram(self, update: TelegramMessage) -> Message: # ] if update.poll is not None: message.attachments += [ - TelegramPoll( + Poll( question=update.poll.question, options=[PollOption(text=option.text, votes=option.voter_count) for option in update.poll.options], is_closed=update.poll.is_closed, @@ -115,7 +106,7 @@ def extract_message_from_telegram(self, update: TelegramMessage) -> Message: # if update.audio is not None: thumbnail = Image(id=update.audio.thumbnail.file_id, title=update.audio.thumbnail.file_unique_id) if update.audio.thumbnail is not None else None message.attachments += [ - TelegramAudio( + Audio( id=update.audio.file_id, title=update.audio.file_unique_id, duration=update.audio.duration, @@ -128,7 +119,7 @@ def extract_message_from_telegram(self, update: TelegramMessage) -> Message: # if update.video is not None: thumbnail = Image(id=update.video.thumbnail.file_id, title=update.video.thumbnail.file_unique_id) if update.video.thumbnail is not None else None message.attachments += [ - TelegramVideo( + Video( id=update.video.file_id, title=update.video.file_unique_id, width=update.video.width, @@ -142,7 +133,7 @@ def extract_message_from_telegram(self, update: TelegramMessage) -> Message: # if update.animation is not None: thumbnail = Image(id=update.animation.thumbnail.file_id, title=update.animation.thumbnail.file_unique_id) if update.animation.thumbnail is not None else None message.attachments += [ - TelegramAnimation( + Animation( id=update.animation.file_id, title=update.animation.file_unique_id, width=update.animation.width, @@ -155,7 +146,7 @@ def extract_message_from_telegram(self, update: TelegramMessage) -> Message: # ] if len(update.photo) > 0: message.attachments += [ - TelegramImage( + Image( id=picture.file_id, title=picture.file_unique_id, width=picture.width, @@ -165,7 +156,7 @@ def extract_message_from_telegram(self, update: TelegramMessage) -> Message: # if update.document is not None: thumbnail = Image(id=update.document.thumbnail.file_id, title=update.document.thumbnail.file_unique_id) if update.document.thumbnail is not None else None message.attachments += [ - TelegramDocument( + Document( id=update.document.file_id, title=update.document.file_unique_id, file_name=update.document.file_name, diff --git a/dff/messengers/telegram/attachments.py b/dff/messengers/telegram/attachments.py deleted file mode 100644 index 4340ca996..000000000 --- a/dff/messengers/telegram/attachments.py +++ /dev/null @@ -1,54 +0,0 @@ -from typing import Optional - -from dff.script.core.message import Animation, Audio, Contact, Document, Image, Poll, Video - - -class TelegramContact(Contact): - user_id: Optional[int] - - -class TelegramPoll(Poll): - is_closed: bool - is_anonymous: bool - type: str - multiple_answers: bool - correct_option_id: Optional[int] - explanation: Optional[str] - open_period: Optional[int] - - -class TelegramAudio(Audio): - duration: int - performer: Optional[str] - file_name: Optional[str] - mime_type: Optional[str] - thumbnail: Optional[Image] - - -class TelegramVideo(Video): - width: int - height: int - duration: int - file_name: Optional[str] - mime_type: Optional[str] - thumbnail: Optional[Image] - - -class TelegramAnimation(Animation): - width: int - height: int - duration: int - file_name: Optional[str] - mime_type: Optional[str] - thumbnail: Optional[Image] - - -class TelegramImage(Image): - width: int - height: int - - -class TelegramDocument(Document): - file_name: Optional[str] - mime_type: Optional[str] - thumbnail: Optional[Image] From 1bfe52493224645d09195b6df9d20f85d94a7eac Mon Sep 17 00:00:00 2001 From: pseusys Date: Thu, 11 Apr 2024 01:05:38 +0200 Subject: [PATCH 037/140] extras added --- dff/messengers/telegram/__init__.py | 9 --- dff/messengers/telegram/abstract.py | 99 ++++++++++++++++++++++++++--- 2 files changed, 90 insertions(+), 18 deletions(-) diff --git a/dff/messengers/telegram/__init__.py b/dff/messengers/telegram/__init__.py index dd8a7cdfd..68ea21beb 100644 --- a/dff/messengers/telegram/__init__.py +++ b/dff/messengers/telegram/__init__.py @@ -1,12 +1,3 @@ # -*- coding: utf-8 -*- -from .attachments import ( - TelegramContact, - TelegramPoll, - TelegramAudio, - TelegramVideo, - TelegramAnimation, - TelegramImage, - TelegramDocument, -) from .interface import PollingTelegramInterface, CallbackTelegramInterface diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index da0d2ab44..5b8aa467f 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -175,7 +175,9 @@ def _create_keyboard( button_list = [ [ InlineKeyboardButton( - text=button.text, callback_data=button.data if button.data is not None else button.text + text=button.text, + url=button.__pydantic_extra__.get("url", None), + callback_data=button.data if button.data is not None else button.text, ) for button in row ] @@ -195,7 +197,13 @@ async def cast_message_to_telegram_and_send( for attachment in message.attachments: if isinstance(attachment, Location): await bot.send_location( - chat_id, attachment.latitude, attachment.longitude, reply_markup=self._create_keyboard(buttons) + chat_id, + attachment.latitude, + attachment.longitude, + horizontal_accuracy=attachment.__pydantic_extra__.get("horizontal_accuracy", None), + disable_notification=attachment.__pydantic_extra__.get("disable_notification", None), + protect_content=attachment.__pydantic_extra__.get("protect_content", None), + reply_markup=self._create_keyboard(buttons) ) if isinstance(attachment, Contact): await bot.send_contact( @@ -203,6 +211,9 @@ async def cast_message_to_telegram_and_send( attachment.phone_number, attachment.first_name, attachment.last_name, + vcard=attachment.__pydantic_extra__.get("vcard", None), + disable_notification=attachment.__pydantic_extra__.get("disable_notification", None), + protect_content=attachment.__pydantic_extra__.get("protect_content", None), reply_markup=self._create_keyboard(buttons), ) if isinstance(attachment, Poll): @@ -210,35 +221,105 @@ async def cast_message_to_telegram_and_send( chat_id, attachment.question, [option.text for option in attachment.options], + is_anonymous=attachment.__pydantic_extra__.get("is_anonymous", None), + type=attachment.__pydantic_extra__.get("type", None), + allows_multiple_answers=attachment.__pydantic_extra__.get("allows_multiple_answers", None), + correct_option_id=attachment.__pydantic_extra__.get("correct_option_id", None), + explanation=attachment.__pydantic_extra__.get("explanation", None), + explanation_parse_mode=attachment.__pydantic_extra__.get("explanation_parse_mode", None), + open_period=attachment.__pydantic_extra__.get("open_period", None), + is_close=attachment.__pydantic_extra__.get("is_close", None), + disable_notification=attachment.__pydantic_extra__.get("disable_notification", None), + protect_content=attachment.__pydantic_extra__.get("protect_content", None), reply_markup=self._create_keyboard(buttons), ) if isinstance(attachment, Audio): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: - files += [InputMediaAudio(attachment_bytes)] + files += [ + InputMediaAudio( + attachment_bytes, + filename=attachment.__pydantic_extra__.get("filename", None), + caption=attachment.__pydantic_extra__.get("caption", None), + parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), + has_spoiler=attachment.__pydantic_extra__.get("has_spoiler", None), + performer=attachment.__pydantic_extra__.get("performer", None), + title=attachment.__pydantic_extra__.get("title", None), + thumbnail=attachment.__pydantic_extra__.get("thumbnail", None), + ), + ] if isinstance(attachment, Video): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: - files += [InputMediaVideo(attachment_bytes)] + files += [ + InputMediaVideo( + attachment_bytes, + filename=attachment.__pydantic_extra__.get("filename", None), + caption=attachment.__pydantic_extra__.get("caption", None), + parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), + supports_streaming=attachment.__pydantic_extra__.get("supports_streaming", None), + has_spoiler=attachment.__pydantic_extra__.get("has_spoiler", None), + thumbnail=attachment.__pydantic_extra__.get("thumbnail", None), + ), + ] if isinstance(attachment, Animation): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: - files += [InputMediaAnimation(attachment_bytes)] + files += [ + InputMediaAnimation( + attachment_bytes, + filename=attachment.__pydantic_extra__.get("filename", None), + caption=attachment.__pydantic_extra__.get("caption", None), + parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), + has_spoiler=attachment.__pydantic_extra__.get("has_spoiler", None), + thumbnail=attachment.__pydantic_extra__.get("thumbnail", None), + ), + ] if isinstance(attachment, Image): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: - files += [InputMediaPhoto(attachment_bytes)] + files += [ + InputMediaPhoto( + attachment_bytes, + filename=attachment.__pydantic_extra__.get("filename", None), + caption=attachment.__pydantic_extra__.get("caption", None), + parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), + has_spoiler=attachment.__pydantic_extra__.get("has_spoiler", None), + ), + ] if isinstance(attachment, Document): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: - files += [InputMediaDocument(attachment_bytes)] + files += [ + InputMediaDocument( + attachment_bytes, + filename=attachment.__pydantic_extra__.get("filename", None), + caption=attachment.__pydantic_extra__.get("caption", None), + parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), + disable_content_type_detection=attachment.__pydantic_extra__.get("disable_content_type_detection", None), + thumbnail=attachment.__pydantic_extra__.get("thumbnail", None), + ), + ] if isinstance(attachment, Keyboard): buttons = attachment.buttons if len(files) > 0: - await bot.send_media_group(chat_id, files, caption=message.text) + await bot.send_media_group( + chat_id, + files, + disable_notification=message.__pydantic_extra__.get("disable_notification", None), + protect_content=message.__pydantic_extra__.get("protect_content", None), + caption=message.text, + ) return if message.text is not None: - await bot.send_message(chat_id, message.text, reply_markup=self._create_keyboard(buttons)) + await bot.send_message( + chat_id, + message.text, + parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), + disable_notification=message.__pydantic_extra__.get("disable_notification", None), + protect_content=message.__pydantic_extra__.get("protect_content", None), + reply_markup=self._create_keyboard(buttons), + ) async def _on_event( self, update: Update, _: ContextTypes.DEFAULT_TYPE, create_message: Callable[[Update], Message] From 82e7a3efe2288a79295973ffc53ca1c324d289a3 Mon Sep 17 00:00:00 2001 From: pseusys Date: Thu, 11 Apr 2024 10:15:18 +0200 Subject: [PATCH 038/140] lint fixed --- dff/messengers/telegram/abstract.py | 33 +++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index 5b8aa467f..bda827a89 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -104,7 +104,11 @@ def extract_message_from_telegram(self, update: TelegramMessage) -> Message: # ) ] if update.audio is not None: - thumbnail = Image(id=update.audio.thumbnail.file_id, title=update.audio.thumbnail.file_unique_id) if update.audio.thumbnail is not None else None + thumbnail = ( + Image(id=update.audio.thumbnail.file_id, title=update.audio.thumbnail.file_unique_id) + if update.audio.thumbnail is not None + else None + ) message.attachments += [ Audio( id=update.audio.file_id, @@ -117,7 +121,11 @@ def extract_message_from_telegram(self, update: TelegramMessage) -> Message: # ) ] if update.video is not None: - thumbnail = Image(id=update.video.thumbnail.file_id, title=update.video.thumbnail.file_unique_id) if update.video.thumbnail is not None else None + thumbnail = ( + Image(id=update.video.thumbnail.file_id, title=update.video.thumbnail.file_unique_id) + if update.video.thumbnail is not None + else None + ) message.attachments += [ Video( id=update.video.file_id, @@ -131,7 +139,11 @@ def extract_message_from_telegram(self, update: TelegramMessage) -> Message: # ) ] if update.animation is not None: - thumbnail = Image(id=update.animation.thumbnail.file_id, title=update.animation.thumbnail.file_unique_id) if update.animation.thumbnail is not None else None + thumbnail = ( + Image(id=update.animation.thumbnail.file_id, title=update.animation.thumbnail.file_unique_id) + if update.animation.thumbnail is not None + else None + ) message.attachments += [ Animation( id=update.animation.file_id, @@ -151,10 +163,15 @@ def extract_message_from_telegram(self, update: TelegramMessage) -> Message: # title=picture.file_unique_id, width=picture.width, height=picture.height, - ) for picture in update.photo + ) + for picture in update.photo ] if update.document is not None: - thumbnail = Image(id=update.document.thumbnail.file_id, title=update.document.thumbnail.file_unique_id) if update.document.thumbnail is not None else None + thumbnail = ( + Image(id=update.document.thumbnail.file_id, title=update.document.thumbnail.file_unique_id) + if update.document.thumbnail is not None + else None + ) message.attachments += [ Document( id=update.document.file_id, @@ -203,7 +220,7 @@ async def cast_message_to_telegram_and_send( horizontal_accuracy=attachment.__pydantic_extra__.get("horizontal_accuracy", None), disable_notification=attachment.__pydantic_extra__.get("disable_notification", None), protect_content=attachment.__pydantic_extra__.get("protect_content", None), - reply_markup=self._create_keyboard(buttons) + reply_markup=self._create_keyboard(buttons), ) if isinstance(attachment, Contact): await bot.send_contact( @@ -296,7 +313,9 @@ async def cast_message_to_telegram_and_send( filename=attachment.__pydantic_extra__.get("filename", None), caption=attachment.__pydantic_extra__.get("caption", None), parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), - disable_content_type_detection=attachment.__pydantic_extra__.get("disable_content_type_detection", None), + disable_content_type_detection=attachment.__pydantic_extra__.get( + "disable_content_type_detection", None + ), thumbnail=attachment.__pydantic_extra__.get("thumbnail", None), ), ] From 334cd745f80c3e3be43d52f39e2224d88c94f7fe Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 16 Apr 2024 23:29:36 +0200 Subject: [PATCH 039/140] ui replaced with keyboard --- tutorials/script/responses/2_buttons.py | 88 ++++++++++--------------- 1 file changed, 35 insertions(+), 53 deletions(-) diff --git a/tutorials/script/responses/2_buttons.py b/tutorials/script/responses/2_buttons.py index 8d8635eb3..f12a73675 100644 --- a/tutorials/script/responses/2_buttons.py +++ b/tutorials/script/responses/2_buttons.py @@ -29,8 +29,8 @@ class is demonstrated. # %% def check_button_payload(value: str): def payload_check_inner(ctx: Context, _: Pipeline): - if ctx.last_request.misc is not None: - return ctx.last_request.misc.get("payload") == value + if ctx.last_request.text is not None: + return ctx.last_request.text == value else: return False @@ -54,8 +54,8 @@ def payload_check_inner(ctx: Context, _: Pipeline): **{ "text": "Starting test! What's 2 + 2?" " (type in the index of the correct option)", - "misc": { - "ui": Keyboard( + "attachments": [ + Keyboard( buttons=[ [ Button(text="5", data="5"), @@ -63,7 +63,7 @@ def payload_check_inner(ctx: Context, _: Pipeline): ] ] ), - }, + ], } ), TRANSITIONS: { @@ -76,8 +76,8 @@ def payload_check_inner(ctx: Context, _: Pipeline): **{ "text": "Next question: what's 6 * 8?" " (type in the index of the correct option)", - "misc": { - "ui": Keyboard( + "attachments": [ + Keyboard( buttons=[ [ Button(text="38", data="38"), @@ -85,7 +85,7 @@ def payload_check_inner(ctx: Context, _: Pipeline): ] ] ), - }, + ], } ), TRANSITIONS: { @@ -98,8 +98,8 @@ def payload_check_inner(ctx: Context, _: Pipeline): **{ "text": "What's 114 + 115? " "(type in the index of the correct option)", - "misc": { - "ui": Keyboard( + "attachments": [ + Keyboard( buttons=[ [ Button(text="229", data="229"), @@ -107,7 +107,7 @@ def payload_check_inner(ctx: Context, _: Pipeline): ] ] ), - }, + ], } ), TRANSITIONS: { @@ -129,8 +129,8 @@ def payload_check_inner(ctx: Context, _: Pipeline): **{ "text": "Starting test! What's 2 + 2? " "(type in the index of the correct option)", - "misc": { - "ui": Keyboard( + "attachments": [ + Keyboard( buttons=[ [ Button(text="5", data="5"), @@ -138,18 +138,18 @@ def payload_check_inner(ctx: Context, _: Pipeline): ] ] ) - }, + ], } ), ), ( - Message("0"), + Message("5"), Message( **{ "text": "Starting test! What's 2 + 2? " "(type in the index of the correct option)", - "misc": { - "ui": Keyboard( + "attachments": [ + Keyboard( buttons=[ [ Button(text="5", data="5"), @@ -157,18 +157,18 @@ def payload_check_inner(ctx: Context, _: Pipeline): ] ] ), - }, + ], } ), ), ( - Message("1"), + Message("4"), Message( **{ "text": "Next question: what's 6 * 8? " "(type in the index of the correct option)", - "misc": { - "ui": Keyboard( + "attachments": [ + Keyboard( buttons=[ [ Button(text="38", data="38"), @@ -176,18 +176,18 @@ def payload_check_inner(ctx: Context, _: Pipeline): ] ] ), - }, + ], } ), ), ( - Message("0"), + Message("38"), Message( **{ "text": "Next question: what's 6 * 8? " "(type in the index of the correct option)", - "misc": { - "ui": Keyboard( + "attachments": [ + Keyboard( buttons=[ [ Button(text="38", data="38"), @@ -195,18 +195,18 @@ def payload_check_inner(ctx: Context, _: Pipeline): ] ] ), - }, + ], } ), ), ( - Message("1"), + Message("48"), Message( **{ "text": "What's 114 + 115? " "(type in the index of the correct option)", - "misc": { - "ui": Keyboard( + "attachments": [ + Keyboard( buttons=[ [ Button(text="229", data="229"), @@ -214,18 +214,18 @@ def payload_check_inner(ctx: Context, _: Pipeline): ] ] ), - }, + ], } ), ), ( - Message("1"), + Message("283"), Message( **{ "text": "What's 114 + 115? " "(type in the index of the correct option)", - "misc": { - "ui": Keyboard( + "attachments": [ + Keyboard( buttons=[ [ Button(text="229", data="229"), @@ -233,38 +233,20 @@ def payload_check_inner(ctx: Context, _: Pipeline): ] ] ), - }, + ], } ), ), - (Message("0"), Message("Success!")), + (Message("229"), Message("Success!")), (Message("ok"), Message("Finishing test")), ) -def process_request(ctx: Context): - ui = ( - ctx.last_response - and ctx.last_response.misc - and ctx.last_response.misc.get("ui") - ) - if ui and ui.buttons[0]: - try: - chosen_button = ui.buttons[0][int(ctx.last_request.text)] - except (IndexError, ValueError): - raise ValueError( - "Type in the index of the correct option " - "to choose from the buttons." - ) - ctx.last_request = Message(misc={"payload": chosen_button.data}) - - # %% pipeline = Pipeline.from_script( toy_script, start_label=("root", "start"), fallback_label=("root", "fallback"), - pre_services=[process_request], ) if __name__ == "__main__": From bc406f6c4920ca6ea64fcadc2acf8b601c54be53 Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 16 Apr 2024 23:32:04 +0200 Subject: [PATCH 040/140] "check_if_" removed --- tutorials/messengers/telegram/4_conditions.py | 4 ++-- .../messengers/telegram/5_conditions_with_media.py | 12 ++++++------ tutorials/messengers/telegram/6_conditions_extras.py | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tutorials/messengers/telegram/4_conditions.py b/tutorials/messengers/telegram/4_conditions.py index 4b2937e94..5e03f52a0 100644 --- a/tutorials/messengers/telegram/4_conditions.py +++ b/tutorials/messengers/telegram/4_conditions.py @@ -53,7 +53,7 @@ # %% -def check_if_latest_message_test_has_music(ctx: Context, _: Pipeline) -> bool: +def latest_message_test_has_music(ctx: Context, _: Pipeline) -> bool: if ctx.last_request is None: return False if ctx.last_request.original_message is None: @@ -84,7 +84,7 @@ def check_if_latest_message_test_has_music(ctx: Context, _: Pipeline) -> bool: }, "node2": { RESPONSE: Message(text="Good. What do you want to talk about?"), - TRANSITIONS: {"node3": check_if_latest_message_test_has_music}, + TRANSITIONS: {"node3": latest_message_test_has_music}, }, "node3": { RESPONSE: Message(text="Sorry, I can not talk about music now."), diff --git a/tutorials/messengers/telegram/5_conditions_with_media.py b/tutorials/messengers/telegram/5_conditions_with_media.py index fb6602f37..5c26deb2c 100644 --- a/tutorials/messengers/telegram/5_conditions_with_media.py +++ b/tutorials/messengers/telegram/5_conditions_with_media.py @@ -49,7 +49,7 @@ interface = PollingTelegramInterface(token=os.environ["TG_BOT_TOKEN"]) -def check_if_latest_message_has_photos(ctx: Context, _: Pipeline) -> bool: +def latest_message_has_photos(ctx: Context, _: Pipeline) -> bool: if ctx.last_request is None: return False if ctx.last_request.original_message is None: @@ -61,7 +61,7 @@ def check_if_latest_message_has_photos(ctx: Context, _: Pipeline) -> bool: return len(ctx.last_request.original_message.message.photo) > 0 -def check_if_latest_message_has_images(ctx: Context, _: Pipeline) -> bool: +def latest_message_has_images(ctx: Context, _: Pipeline) -> bool: if ctx.last_request is None: return False if ctx.last_request.original_message is None: @@ -76,7 +76,7 @@ def check_if_latest_message_has_images(ctx: Context, _: Pipeline) -> bool: ) -def check_if_latest_message_has_text(ctx: Context, _: Pipeline) -> bool: +def latest_message_has_text(ctx: Context, _: Pipeline) -> bool: if ctx.last_request is None: return False if ctx.last_request.original_message is None: @@ -123,14 +123,14 @@ def check_if_latest_message_has_text(ctx: Context, _: Pipeline) -> bool: # both in 'photo' and 'document' fields. # We should consider both cases # when we check the message for media. - check_if_latest_message_has_photos, - check_if_latest_message_has_images, + latest_message_has_photos, + latest_message_has_images, ] ), ( "pics", "send_many", - ): check_if_latest_message_has_text, + ): latest_message_has_text, ("pics", "ask_picture"): cnd.true(), }, }, diff --git a/tutorials/messengers/telegram/6_conditions_extras.py b/tutorials/messengers/telegram/6_conditions_extras.py index ed9d5b2ff..f74faa993 100644 --- a/tutorials/messengers/telegram/6_conditions_extras.py +++ b/tutorials/messengers/telegram/6_conditions_extras.py @@ -56,7 +56,7 @@ # %% -def check_if_latest_message_is_new_chat_member( +def latest_message_is_new_chat_member( ctx: Context, _: Pipeline ) -> bool: if ctx.last_request is None: @@ -70,7 +70,7 @@ def check_if_latest_message_is_new_chat_member( ) -def check_if_latest_message_is_callback_query( +def latest_message_is_callback_query( ctx: Context, _: Pipeline ) -> bool: if ctx.last_request is None: @@ -88,12 +88,12 @@ def check_if_latest_message_is_callback_query( ( "greeting_flow", "node1", - ): check_if_latest_message_is_new_chat_member, + ): latest_message_is_new_chat_member, # send a message when inline query is received ( "greeting_flow", "node2", - ): check_if_latest_message_is_callback_query, + ): latest_message_is_callback_query, }, }, "greeting_flow": { From b08e8f370f8d50e26dc619a8681402b8c91987f6 Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 16 Apr 2024 23:34:27 +0200 Subject: [PATCH 041/140] undefined message removed --- dff/messengers/telegram/abstract.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index bda827a89..847341ba4 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -334,7 +334,7 @@ async def cast_message_to_telegram_and_send( await bot.send_message( chat_id, message.text, - parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), + parse_mode=message.__pydantic_extra__.get("parse_mode", None), disable_notification=message.__pydantic_extra__.get("disable_notification", None), protect_content=message.__pydantic_extra__.get("protect_content", None), reply_markup=self._create_keyboard(buttons), From c79a732ded736bc360628cc2d5a348b4df307841 Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 16 Apr 2024 23:37:26 +0200 Subject: [PATCH 042/140] unnecessary type casting removed --- tutorials/messengers/telegram/5_conditions_with_media.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorials/messengers/telegram/5_conditions_with_media.py b/tutorials/messengers/telegram/5_conditions_with_media.py index 5c26deb2c..646df8f39 100644 --- a/tutorials/messengers/telegram/5_conditions_with_media.py +++ b/tutorials/messengers/telegram/5_conditions_with_media.py @@ -146,7 +146,7 @@ def latest_message_has_text(ctx: Context, _: Pipeline) -> bool: RESPONSE: Message( text="Look at my pictures!", # An HTTP path or a path to a local file can be used here. - attachments=list(tuple([Image(source=picture_url)] * 2)), + attachments=[Image(source=picture_url)] * 2, ), TRANSITIONS: {("root", "fallback"): cnd.true()}, }, From 7853dbc19c17856237d446f4d012d700e609b224 Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 16 Apr 2024 23:39:06 +0200 Subject: [PATCH 043/140] args and kwargs removed --- tutorials/messengers/telegram/4_conditions.py | 4 ++-- tutorials/utils/1_cache.py | 2 +- tutorials/utils/2_lru_cache.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tutorials/messengers/telegram/4_conditions.py b/tutorials/messengers/telegram/4_conditions.py index 5e03f52a0..38d65d894 100644 --- a/tutorials/messengers/telegram/4_conditions.py +++ b/tutorials/messengers/telegram/4_conditions.py @@ -88,12 +88,12 @@ def latest_message_test_has_music(ctx: Context, _: Pipeline) -> bool: }, "node3": { RESPONSE: Message(text="Sorry, I can not talk about music now."), - TRANSITIONS: {"node4": lambda _, __, ___, ____: True}, + TRANSITIONS: {"node4": lambda _, __: True}, # This condition is true for any type of update }, "node4": { RESPONSE: Message(text="bye"), - TRANSITIONS: {"node1": lambda _, __, ___, ____: True}, + TRANSITIONS: {"node1": lambda _, __: True}, # This condition is true if the last update is of type `message` }, "fallback_node": { diff --git a/tutorials/utils/1_cache.py b/tutorials/utils/1_cache.py index 871c9a939..c6d02bc0a 100644 --- a/tutorials/utils/1_cache.py +++ b/tutorials/utils/1_cache.py @@ -47,7 +47,7 @@ def cached_response(_): return external_data["counter"] -def response(ctx: Context, _, *__, **___) -> Message: +def response(ctx: Context, _) -> Message: if ctx.validation: return Message() return Message( diff --git a/tutorials/utils/2_lru_cache.py b/tutorials/utils/2_lru_cache.py index 40e59715f..f6d735dc8 100644 --- a/tutorials/utils/2_lru_cache.py +++ b/tutorials/utils/2_lru_cache.py @@ -46,7 +46,7 @@ def cached_response(_): return external_data["counter"] -def response(ctx: Context, _, *__, **___) -> Message: +def response(ctx: Context, _) -> Message: if ctx.validation: return Message() return Message( From 5b9992f3eb7fb0654d100ac1f95f06ffd8344659 Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 16 Apr 2024 23:42:05 +0200 Subject: [PATCH 044/140] default poll option voter count added --- dff/script/core/message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dff/script/core/message.py b/dff/script/core/message.py index abc4f52a8..91e8e60d4 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -82,7 +82,7 @@ class Invoice(Attachment): class PollOption(DataModel): text: str - votes: int + votes: int = Field(default=0) class Poll(Attachment): From a3beac94c16a7db5b022a39a06ba4e755e0ed145 Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 16 Apr 2024 23:47:58 +0200 Subject: [PATCH 045/140] telegram import check added --- dff/messengers/telegram/abstract.py | 72 ++++++++++++++++------------- 1 file changed, 40 insertions(+), 32 deletions(-) diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index 847341ba4..ecdb7c758 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -10,39 +10,44 @@ from typing import Callable, Optional, Sequence from pydantic import FilePath -from telegram import ( - InlineKeyboardButton, - InlineKeyboardMarkup, - InputMediaAnimation, - InputMediaAudio, - InputMediaDocument, - InputMediaPhoto, - InputMediaVideo, - Update, - Message as TelegramMessage, -) -from telegram.ext import Application, ExtBot, MessageHandler, CallbackQueryHandler, ContextTypes -from telegram.ext.filters import ALL +try: + from telegram import ( + InlineKeyboardButton, + InlineKeyboardMarkup, + InputMediaAnimation, + InputMediaAudio, + InputMediaDocument, + InputMediaPhoto, + InputMediaVideo, + Update, + Message as TelegramMessage, + ) + from telegram.ext import Application, ExtBot, MessageHandler, CallbackQueryHandler, ContextTypes + from telegram.ext.filters import ALL -from dff.messengers.common import MessengerInterface -from dff.pipeline.types import PipelineRunnerFunction -from dff.script.core.message import ( - Animation, - Audio, - Button, - CallbackQuery, - Contact, - DataAttachment, - Document, - Image, - Invoice, - Keyboard, - Location, - Message, - Poll, - PollOption, - Video, -) + from dff.messengers.common import MessengerInterface + from dff.pipeline.types import PipelineRunnerFunction + from dff.script.core.message import ( + Animation, + Audio, + Button, + CallbackQuery, + Contact, + DataAttachment, + Document, + Image, + Invoice, + Keyboard, + Location, + Message, + Poll, + PollOption, + Video, + ) + + telegram_available = True +except ImportError: + telegram_available = False class _AbstractTelegramInterface(MessengerInterface): # pragma: no cover @@ -50,6 +55,9 @@ class _AbstractTelegramInterface(MessengerInterface): # pragma: no cover response_attachments = {Location, Contact, Poll, Audio, Video, Animation, Image, Document, Keyboard} def __init__(self, token: str) -> None: + if not telegram_available: + raise ImportError("`python-telegram-bot` package is missing.\nTry to run `pip install dff[telegram]`.") + self.application = Application.builder().token(token).build() self.application.add_handler(MessageHandler(ALL, self.on_message)) self.application.add_handler(CallbackQueryHandler(self.on_callback)) From 5dd075152e78982d8ae6600d3b9e123e832447c1 Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 16 Apr 2024 23:49:18 +0200 Subject: [PATCH 046/140] interface attachments sorted --- dff/messengers/telegram/abstract.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index ecdb7c758..564c62e99 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -51,7 +51,7 @@ class _AbstractTelegramInterface(MessengerInterface): # pragma: no cover - request_attachments = {Location, Contact, Invoice, Poll, Audio, Video, Animation, Image, Document} + request_attachments = {Location, Contact, Poll, Audio, Video, Animation, Image, Document, Invoice} response_attachments = {Location, Contact, Poll, Audio, Video, Animation, Image, Document, Keyboard} def __init__(self, token: str) -> None: From b9517724df677d15a7cf850ce64cb9aad64250db Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 16 Apr 2024 23:58:13 +0200 Subject: [PATCH 047/140] temp attachment dir warning added --- dff/messengers/common/interface.py | 9 +++++++++ dff/messengers/telegram/abstract.py | 6 +++--- dff/messengers/telegram/interface.py | 10 ++++++---- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/dff/messengers/common/interface.py b/dff/messengers/common/interface.py index 1530d630e..2c80e968a 100644 --- a/dff/messengers/common/interface.py +++ b/dff/messengers/common/interface.py @@ -9,6 +9,8 @@ import abc import asyncio import logging +from pathlib import Path +from tempfile import gettempdir from typing import Optional, Any, List, Tuple, Hashable, TYPE_CHECKING if TYPE_CHECKING: @@ -29,6 +31,13 @@ class MessengerInterface(abc.ABC): request_attachments = set() response_attachments = set() + def __init__(self, attachments_directory: Optional[Path] = None) -> None: + if attachments_directory is not None: + self.attachments_directory = attachments_directory + else: + self.attachments_directory = Path(gettempdir()) + logger.warning(f"Attachments directory for {type(self)} messenger interface is set to tempdir!") + @abc.abstractmethod async def connect(self, pipeline_runner: PipelineRunnerFunction): """ diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index 564c62e99..81de01615 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -6,7 +6,6 @@ """ from pathlib import Path -from tempfile import gettempdir from typing import Callable, Optional, Sequence from pydantic import FilePath @@ -54,7 +53,8 @@ class _AbstractTelegramInterface(MessengerInterface): # pragma: no cover request_attachments = {Location, Contact, Poll, Audio, Video, Animation, Image, Document, Invoice} response_attachments = {Location, Contact, Poll, Audio, Video, Animation, Image, Document, Keyboard} - def __init__(self, token: str) -> None: + def __init__(self, token: str, attachments_directory: Optional[Path] = None) -> None: + super().__init__(attachments_directory) if not telegram_available: raise ImportError("`python-telegram-bot` package is missing.\nTry to run `pip install dff[telegram]`.") @@ -64,7 +64,7 @@ def __init__(self, token: str) -> None: async def populate_attachment(self, attachment: DataAttachment) -> None: # pragma: no cover if attachment.title is not None and attachment.id is not None: - file_name = Path(gettempdir()) / str(attachment.title) + file_name = self.attachments_directory / str(attachment.title) if not file_name.exists(): await (await self.application.bot.get_file(attachment.id)).download_to_drive(file_name) attachment.source = FilePath(file_name) diff --git a/dff/messengers/telegram/interface.py b/dff/messengers/telegram/interface.py index 76597a65e..6234956b4 100644 --- a/dff/messengers/telegram/interface.py +++ b/dff/messengers/telegram/interface.py @@ -1,3 +1,5 @@ +from pathlib import Path +from typing import Optional from telegram import Update from dff.pipeline.types import PipelineRunnerFunction @@ -6,8 +8,8 @@ class PollingTelegramInterface(_AbstractTelegramInterface): # pragma: no cover - def __init__(self, token: str, interval: int = 2, timeout: int = 20) -> None: - super().__init__(token) + def __init__(self, token: str, attachments_directory: Optional[Path] = None, interval: int = 2, timeout: int = 20) -> None: + super().__init__(token, attachments_directory) self.interval = interval self.timeout = timeout @@ -19,8 +21,8 @@ async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs class CallbackTelegramInterface(_AbstractTelegramInterface): # pragma: no cover - def __init__(self, token: str, host: str = "localhost", port: int = 844): - super().__init__(token) + def __init__(self, token: str, attachments_directory: Optional[Path] = None, host: str = "localhost", port: int = 844): + super().__init__(token, attachments_directory) self.listen = host self.port = port From fe99c4c05523e116fbda50e65fa6626cb9ca7566 Mon Sep 17 00:00:00 2001 From: pseusys Date: Wed, 17 Apr 2024 00:06:12 +0200 Subject: [PATCH 048/140] attachment message clarified --- dff/messengers/common/interface.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/dff/messengers/common/interface.py b/dff/messengers/common/interface.py index 2c80e968a..f820b0e5a 100644 --- a/dff/messengers/common/interface.py +++ b/dff/messengers/common/interface.py @@ -32,11 +32,18 @@ class MessengerInterface(abc.ABC): response_attachments = set() def __init__(self, attachments_directory: Optional[Path] = None) -> None: - if attachments_directory is not None: + tempdir = gettempdir() + if attachments_directory is not None and not str(attachments_directory.absolute()).startswith(tempdir): self.attachments_directory = attachments_directory else: - self.attachments_directory = Path(gettempdir()) - logger.warning(f"Attachments directory for {type(self)} messenger interface is set to tempdir!") + warning_start = f"Attachments directory for {type(self)} messenger interface" + warning_end = "attachment data won't be preserved locally!" + if attachments_directory is None: + self.attachments_directory = Path(tempdir) + logger.warning(f"{warning_start} is None, so will be set to tempdir and {warning_end}") + else: + self.attachments_directory = attachments_directory + logger.warning(f"{warning_start} is in tempdir, so {warning_end}") @abc.abstractmethod async def connect(self, pipeline_runner: PipelineRunnerFunction): From 595d55d8e43a5279c92f386a691e4ceb3eb2e4ac Mon Sep 17 00:00:00 2001 From: pseusys Date: Thu, 18 Apr 2024 10:57:38 +0200 Subject: [PATCH 049/140] single attachment sending proposal + sticker --- dff/messengers/telegram/abstract.py | 196 +++++++++++++++++----------- dff/script/core/message.py | 38 +----- 2 files changed, 129 insertions(+), 105 deletions(-) diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index bda827a89..9df97a32a 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -7,12 +7,10 @@ from pathlib import Path from tempfile import gettempdir -from typing import Callable, Optional, Sequence +from typing import Callable from pydantic import FilePath from telegram import ( - InlineKeyboardButton, - InlineKeyboardMarkup, InputMediaAnimation, InputMediaAudio, InputMediaDocument, @@ -29,18 +27,17 @@ from dff.script.core.message import ( Animation, Audio, - Button, CallbackQuery, Contact, DataAttachment, Document, Image, Invoice, - Keyboard, Location, Message, Poll, PollOption, + Sticker, Video, ) @@ -184,31 +181,9 @@ def extract_message_from_telegram(self, update: TelegramMessage) -> Message: # return message - def _create_keyboard( - self, buttons: Sequence[Sequence[Button]] - ) -> Optional[InlineKeyboardMarkup]: # pragma: no cover - button_list = None - if len(buttons) > 0: - button_list = [ - [ - InlineKeyboardButton( - text=button.text, - url=button.__pydantic_extra__.get("url", None), - callback_data=button.data if button.data is not None else button.text, - ) - for button in row - ] - for row in buttons - ] - if button_list is None: - return None - else: - return InlineKeyboardMarkup(button_list) - async def cast_message_to_telegram_and_send( self, bot: ExtBot, chat_id: int, message: Message ) -> None: # pragma: no cover - buttons = list() if message.attachments is not None: files = list() for attachment in message.attachments: @@ -220,7 +195,7 @@ async def cast_message_to_telegram_and_send( horizontal_accuracy=attachment.__pydantic_extra__.get("horizontal_accuracy", None), disable_notification=attachment.__pydantic_extra__.get("disable_notification", None), protect_content=attachment.__pydantic_extra__.get("protect_content", None), - reply_markup=self._create_keyboard(buttons), + reply_markup=attachment.__pydantic_extra__.get("reply_markup", None), ) if isinstance(attachment, Contact): await bot.send_contact( @@ -231,7 +206,7 @@ async def cast_message_to_telegram_and_send( vcard=attachment.__pydantic_extra__.get("vcard", None), disable_notification=attachment.__pydantic_extra__.get("disable_notification", None), protect_content=attachment.__pydantic_extra__.get("protect_content", None), - reply_markup=self._create_keyboard(buttons), + reply_markup=attachment.__pydantic_extra__.get("reply_markup", None), ) if isinstance(attachment, Poll): await bot.send_poll( @@ -245,82 +220,157 @@ async def cast_message_to_telegram_and_send( explanation=attachment.__pydantic_extra__.get("explanation", None), explanation_parse_mode=attachment.__pydantic_extra__.get("explanation_parse_mode", None), open_period=attachment.__pydantic_extra__.get("open_period", None), - is_close=attachment.__pydantic_extra__.get("is_close", None), + is_closed=attachment.__pydantic_extra__.get("is_closed", None), disable_notification=attachment.__pydantic_extra__.get("disable_notification", None), protect_content=attachment.__pydantic_extra__.get("protect_content", None), - reply_markup=self._create_keyboard(buttons), + reply_markup=attachment.__pydantic_extra__.get("reply_markup", None), + ) + if isinstance(attachment, Sticker): + sticker = await attachment.get_bytes(self) if attachment.id is None else attachment.id + await bot.send_sticker( + chat_id, + sticker, + disable_notification=attachment.__pydantic_extra__.get("disable_notification", None), + reply_markup=attachment.__pydantic_extra__.get("reply_markup", None), + emoji=attachment.__pydantic_extra__.get("emoji", None), ) if isinstance(attachment, Audio): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: - files += [ - InputMediaAudio( - attachment_bytes, - filename=attachment.__pydantic_extra__.get("filename", None), - caption=attachment.__pydantic_extra__.get("caption", None), - parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), - has_spoiler=attachment.__pydantic_extra__.get("has_spoiler", None), + if len(message.attachments) > 1: + files += [ + InputMediaAudio( + attachment_bytes, + filename=attachment.__pydantic_extra__.get("filename", None), + caption=attachment.__pydantic_extra__.get("caption", None), + parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), + performer=attachment.__pydantic_extra__.get("performer", None), + title=attachment.__pydantic_extra__.get("title", None), + thumbnail=attachment.__pydantic_extra__.get("thumbnail", None), + ), + ] + else: + await bot.send_audio( + chat_id, + audio=attachment_bytes, performer=attachment.__pydantic_extra__.get("performer", None), title=attachment.__pydantic_extra__.get("title", None), + caption=message.text, + disable_notification=attachment.__pydantic_extra__.get("disable_notification", None), + reply_markup=attachment.__pydantic_extra__.get("reply_markup", None), + parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), thumbnail=attachment.__pydantic_extra__.get("thumbnail", None), - ), - ] + ) + return if isinstance(attachment, Video): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: - files += [ - InputMediaVideo( + if len(message.attachments) > 1: + files += [ + InputMediaVideo( + attachment_bytes, + filename=attachment.__pydantic_extra__.get("filename", None), + caption=attachment.__pydantic_extra__.get("caption", None), + parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), + supports_streaming=attachment.__pydantic_extra__.get("supports_streaming", None), + has_spoiler=attachment.__pydantic_extra__.get("has_spoiler", None), + thumbnail=attachment.__pydantic_extra__.get("thumbnail", None), + ), + ] + else: + await bot.send_video( + chat_id, attachment_bytes, - filename=attachment.__pydantic_extra__.get("filename", None), - caption=attachment.__pydantic_extra__.get("caption", None), + caption=message.text, + disable_notification=attachment.__pydantic_extra__.get("disable_notification", None), + reply_markup=attachment.__pydantic_extra__.get("reply_markup", None), parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), supports_streaming=attachment.__pydantic_extra__.get("supports_streaming", None), has_spoiler=attachment.__pydantic_extra__.get("has_spoiler", None), thumbnail=attachment.__pydantic_extra__.get("thumbnail", None), - ), - ] + filename=attachment.__pydantic_extra__.get("filename", None), + ) + return if isinstance(attachment, Animation): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: - files += [ - InputMediaAnimation( + if len(message.attachments) > 1: + files += [ + InputMediaAnimation( + attachment_bytes, + filename=attachment.__pydantic_extra__.get("filename", None), + caption=attachment.__pydantic_extra__.get("caption", None), + parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), + has_spoiler=attachment.__pydantic_extra__.get("has_spoiler", None), + thumbnail=attachment.__pydantic_extra__.get("thumbnail", None), + ), + ] + else: + await bot.send_animation( + chat_id, attachment_bytes, - filename=attachment.__pydantic_extra__.get("filename", None), - caption=attachment.__pydantic_extra__.get("caption", None), + caption=message.text, parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), + disable_notification=attachment.__pydantic_extra__.get("disable_notification", None), + reply_markup=attachment.__pydantic_extra__.get("reply_markup", None), has_spoiler=attachment.__pydantic_extra__.get("has_spoiler", None), thumbnail=attachment.__pydantic_extra__.get("thumbnail", None), - ), - ] + filename=attachment.__pydantic_extra__.get("filename", None), + ) + return if isinstance(attachment, Image): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: - files += [ - InputMediaPhoto( + if len(message.attachments) > 1: + files += [ + InputMediaPhoto( + attachment_bytes, + filename=attachment.__pydantic_extra__.get("filename", None), + caption=attachment.__pydantic_extra__.get("caption", None), + parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), + has_spoiler=attachment.__pydantic_extra__.get("has_spoiler", None), + ), + ] + else: + await bot.send_photo( + chat_id, attachment_bytes, - filename=attachment.__pydantic_extra__.get("filename", None), - caption=attachment.__pydantic_extra__.get("caption", None), + caption=message.text, + disable_notification=attachment.__pydantic_extra__.get("disable_notification", None), + reply_markup=attachment.__pydantic_extra__.get("reply_markup", None), parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), has_spoiler=attachment.__pydantic_extra__.get("has_spoiler", None), - ), - ] + filename=attachment.__pydantic_extra__.get("filename", None), + ) + return if isinstance(attachment, Document): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: - files += [ - InputMediaDocument( + if len(message.attachments) > 1: + files += [ + InputMediaDocument( + attachment_bytes, + filename=attachment.__pydantic_extra__.get("filename", None), + caption=attachment.__pydantic_extra__.get("caption", None), + parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), + disable_content_type_detection=attachment.__pydantic_extra__.get( + "disable_content_type_detection", None + ), + thumbnail=attachment.__pydantic_extra__.get("thumbnail", None), + ), + ] + else: + await bot.send_document( + chat_id, attachment_bytes, - filename=attachment.__pydantic_extra__.get("filename", None), - caption=attachment.__pydantic_extra__.get("caption", None), + caption=message.text, + disable_notification=attachment.__pydantic_extra__.get("disable_notification", None), + reply_markup=attachment.__pydantic_extra__.get("reply_markup", None), parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), - disable_content_type_detection=attachment.__pydantic_extra__.get( - "disable_content_type_detection", None - ), thumbnail=attachment.__pydantic_extra__.get("thumbnail", None), - ), - ] - if isinstance(attachment, Keyboard): - buttons = attachment.buttons + filename=attachment.__pydantic_extra__.get("filename", None), + ) + return if len(files) > 0: await bot.send_media_group( chat_id, @@ -334,10 +384,10 @@ async def cast_message_to_telegram_and_send( await bot.send_message( chat_id, message.text, - parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), + parse_mode=message.__pydantic_extra__.get("parse_mode", None), disable_notification=message.__pydantic_extra__.get("disable_notification", None), protect_content=message.__pydantic_extra__.get("protect_content", None), - reply_markup=self._create_keyboard(buttons), + reply_markup=message.__pydantic_extra__.get("reply_markup", None), ) async def _on_event( @@ -346,7 +396,7 @@ async def _on_event( if update.effective_chat is not None and update.message is not None: message = create_message(update) message.original_message = update - resp = await self.callback(message, update.effective_chat.id) + resp = await self.pipeline_runner(message, update.effective_chat.id) if resp.last_response is not None: await self.cast_message_to_telegram_and_send( self.application.bot, update.effective_chat.id, resp.last_response @@ -361,4 +411,4 @@ async def on_callback(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> Non ) async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs): - self.callback = pipeline_runner + self.pipeline_runner = pipeline_runner diff --git a/dff/script/core/message.py b/dff/script/core/message.py index abc4f52a8..4702f8f87 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -10,7 +10,7 @@ from pathlib import Path from urllib.request import urlopen -from pydantic import field_validator, Field, FilePath, HttpUrl, BaseModel, model_validator +from pydantic import field_validator, FilePath, HttpUrl, BaseModel, model_validator from dff.messengers.common.interface import MessengerInterface @@ -162,42 +162,16 @@ class Image(DataAttachment): pass -class Document(DataAttachment): - """Represents a document file attachment.""" +class Sticker(Image): + """Represents a sticker as a file attachment.""" pass -class Button(DataModel): - """Represents a button of an inline keyboard.""" - - text: str - data: Optional[str] = None - - @field_validator("data") - @classmethod - def data_length_should_be_constrained(cls, value: Optional[str]) -> Optional[str]: - if value is None: - return value - value_size = len(value.encode("utf-8")) - if 1 <= value_size <= 64 and value: - return value - else: - raise ValueError(f"Unexpected data length: {value_size} bytes") - - -class Keyboard(Attachment): - """ - This class is an Attachment that represents a keyboard object - that can be used for a chatbot or messaging application. - """ - - buttons: List[List[Button]] = Field(default_factory=list, min_length=1) +class Document(DataAttachment): + """Represents a document file attachment.""" - def __eq__(self, other): - if isinstance(other, Keyboard): - return self.buttons == other.buttons - return NotImplemented + pass class Message(DataModel): From b31ea18df88bf4374162d6f68acd2adcc2f5d757 Mon Sep 17 00:00:00 2001 From: pseusys Date: Thu, 18 Apr 2024 11:06:03 +0200 Subject: [PATCH 050/140] request and response attachments sorted --- dff/messengers/telegram/abstract.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index 9df97a32a..53d5a0081 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -43,8 +43,8 @@ class _AbstractTelegramInterface(MessengerInterface): # pragma: no cover - request_attachments = {Location, Contact, Invoice, Poll, Audio, Video, Animation, Image, Document} - response_attachments = {Location, Contact, Poll, Audio, Video, Animation, Image, Document, Keyboard} + request_attachments = {Location, Contact, Poll, Audio, Video, Animation, Image, Document, Invoice} + response_attachments = {Location, Contact, Poll, Audio, Video, Animation, Image, Document} def __init__(self, token: str) -> None: self.application = Application.builder().token(token).build() From dd57886e44337572793a691b7da4fa9986139f0c Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 23 Apr 2024 00:34:56 +0200 Subject: [PATCH 051/140] attachments tutorial added --- dff/messengers/common/interface.py | 2 +- dff/messengers/telegram/abstract.py | 13 +- dff/script/core/message.py | 11 +- .../messengers/telegram/2_attachments.py | 211 ++++++++++++++++++ 4 files changed, 229 insertions(+), 8 deletions(-) create mode 100644 tutorials/messengers/telegram/2_attachments.py diff --git a/dff/messengers/common/interface.py b/dff/messengers/common/interface.py index f820b0e5a..5c9d8fc07 100644 --- a/dff/messengers/common/interface.py +++ b/dff/messengers/common/interface.py @@ -36,7 +36,7 @@ def __init__(self, attachments_directory: Optional[Path] = None) -> None: if attachments_directory is not None and not str(attachments_directory.absolute()).startswith(tempdir): self.attachments_directory = attachments_directory else: - warning_start = f"Attachments directory for {type(self)} messenger interface" + warning_start = f"Attachments directory for {type(self).__name__} messenger interface" warning_end = "attachment data won't be preserved locally!" if attachments_directory is None: self.attachments_directory = Path(tempdir) diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index cb9bd35fb..a1faca25d 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -49,8 +49,8 @@ class _AbstractTelegramInterface(MessengerInterface): # pragma: no cover - request_attachments = {Location, Contact, Poll, Audio, Video, Animation, Image, Document, Invoice} - response_attachments = {Location, Contact, Poll, Audio, Video, Animation, Image, Document} + request_attachments = {Location, Contact, Poll, Sticker, Audio, Video, Animation, Image, Document, Invoice} + response_attachments = {Location, Contact, Poll, Sticker, Audio, Video, Animation, Image, Document} def __init__(self, token: str, attachments_directory: Optional[Path] = None) -> None: super().__init__(attachments_directory) @@ -110,6 +110,15 @@ def extract_message_from_telegram(self, update: TelegramMessage) -> Message: # open_period=update.poll.open_period, ) ] + if update.sticker is not None: + message.attachments += [ + Sticker( + id=update.sticker.file_id, + is_animated=update.sticker.is_animated, + is_video=update.sticker.is_video, + type=update.sticker.type, + ) + ] if update.audio is not None: thumbnail = ( Image(id=update.audio.thumbnail.file_id, title=update.audio.thumbnail.file_unique_id) diff --git a/dff/script/core/message.py b/dff/script/core/message.py index 3a9540818..5d437fcfc 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -10,7 +10,8 @@ from pathlib import Path from urllib.request import urlopen -from pydantic import field_validator, FilePath, HttpUrl, BaseModel, model_validator +from pydantic import Field, field_validator, FilePath, HttpUrl, BaseModel, model_validator +from pydantic_core import Url from dff.messengers.common.interface import MessengerInterface @@ -106,9 +107,9 @@ async def get_bytes(self, from_messenger_interface: MessengerInterface) -> Optio if isinstance(self.source, Path): with open(self.source, "rb") as file: return file.read() - elif isinstance(self.source, HttpUrl): - with urlopen(self.source.unicode_string()) as file: - return file.read() + elif isinstance(self.source, Url): + with urlopen(self.source.unicode_string()) as url: + return url.read() else: return None @@ -162,7 +163,7 @@ class Image(DataAttachment): pass -class Sticker(Image): +class Sticker(DataAttachment): """Represents a sticker as a file attachment.""" pass diff --git a/tutorials/messengers/telegram/2_attachments.py b/tutorials/messengers/telegram/2_attachments.py new file mode 100644 index 000000000..23b675352 --- /dev/null +++ b/tutorials/messengers/telegram/2_attachments.py @@ -0,0 +1,211 @@ +# %% [markdown] +""" +# Telegram: 2. Attachments + +The following tutorial shows how to run a regular DFF script in Telegram. +It asks users for the '/start' command and then loops in one place. + +Here, %mddoclink(api,messengers.telegram,PollingTelegramInterface) +class and [telebot](https://pytba.readthedocs.io/en/latest/index.html) +library are used for accessing telegram API in polling mode. + +Telegram API token is required to access telegram API. +""" + +# %pip install dff[telegram] + +# %% +import os + +from pydantic import HttpUrl + +from dff.script import conditions as cnd +from dff.script import GLOBAL, RESPONSE, TRANSITIONS, Message +from dff.messengers.telegram import PollingTelegramInterface +from dff.pipeline import Pipeline +from dff.script.core.message import Animation, Audio, Contact, Document, Location, Image, Poll, PollOption, Sticker, Video +from dff.utils.testing.common import is_interactive_mode + + +# %% [markdown] +""" +In order to integrate your script with Telegram, you need an instance of +`TelegramMessenger` class and one of the following interfaces: +`PollingMessengerInterface` or `WebhookMessengerInterface`. + +`TelegramMessenger` encapsulates the bot logic. Like Telebot, +`TelegramMessenger` only requires a token to run. However, all parameters +from the Telebot class can be passed as keyword arguments. + +The two interfaces connect the bot to Telegram. They can be passed directly +to the DFF `Pipeline` instance. +""" + +# %% + +location_data = {"latitude": 50.65, "longitude": 3.916667} + +contact_data = {"phone_number": "8-900-555-35-35", "first_name": "Hope", "last_name": "Credit"} + +sticker_data = { + "id": "CAACAgIAAxkBAAErAAFXZibO5ksphCKSXSe1CYiw5588yqsAAkEAAzyKVxogmx2BPCogYDQE", + "title": "A sticker I've just found", +} + +audio_data = { + "source": HttpUrl("https://commondatastorage.googleapis.com/codeskulptor-assets/Evillaugh.ogg"), + "title": "Evil laughter (scary alert!)", +} + +video_data = { + # TODO: I need help, this video results in doenloading timeout, we need another example. + "source": HttpUrl("https://archive.org/download/Rick_Astley_Never_Gonna_Give_You_Up/Rick_Astley_Never_Gonna_Give_You_Up.mp4"), + "title": "Totally not suspicious video...", +} + +animation_data = { + # For some reason, if we don't define filename explicitly, animation is sent as file. + "source": HttpUrl("https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExMmFuMGk5ODY0dG5pd242ODR6anB4bm4wZGN3cjg1N3A1M2ZxMjluYiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/SvP3FgHsFVm7zwMdH6/giphy.gif"), + "title": "Some random free gif :/", + "filename": "random.gif", +} + +image_data = { + "source": HttpUrl("https://avatars.githubusercontent.com/u/29918795?s=200&v=4"), + "title": "DeepPavlov logo", +} + +document_data = { + "source": HttpUrl("https://aclanthology.org/P18-4021.pdf"), + "title": "DeepPavlov article", +} + + +# %% +script = { + GLOBAL: { + TRANSITIONS: { + ("main_flow", "location_node"): cnd.exact_match(Message("location")), + ("main_flow", "contact_node"): cnd.exact_match(Message("contact")), + ("main_flow", "poll_node"): cnd.exact_match(Message("poll")), + ("main_flow", "sticker_node"): cnd.exact_match(Message("sticker")), + ("main_flow", "audio_node"): cnd.exact_match(Message("audio")), + ("main_flow", "video_node"): cnd.exact_match(Message("video")), + ("main_flow", "animation_node"): cnd.exact_match(Message("animation")), + ("main_flow", "image_node"): cnd.exact_match(Message("image")), + ("main_flow", "document_node"): cnd.exact_match(Message("document")), + } + }, + "main_flow": { + "start_node": { + TRANSITIONS: {"intro_node": cnd.exact_match(Message("/start"))}, + }, + "intro_node": { + RESPONSE: Message( + 'Type "location", "contact", "poll", "sticker" ' + '"audio", "video", "animation", "image", ' + '"document" or to receive a corresponding attachment!' + ), + }, + "location_node": { + RESPONSE: Message( + "Here's your location!", + attachments=[Location(**location_data)], + ), + }, + "contact_node": { + RESPONSE: Message( + "Here's your contact!", + attachments=[Contact(**contact_data)], + ), + }, + "poll_node": { + RESPONSE: Message( + "Here's your poll!", + attachments=[ + Poll( + question="What is the poll question?", + options=[ + PollOption(text="This one!"), + PollOption(text="Not this one :("), + ], + ), + ], + ), + }, + "sticker_node": { + RESPONSE: Message( + "Here's your sticker!", + attachments=[Sticker(**sticker_data)], + ), + }, + "audio_node": { + RESPONSE: Message( + "Here's your audio!", + attachments=[Audio(**audio_data)], + ), + }, + "video_node": { + RESPONSE: Message( + "Here's your video!", + attachments=[Video(**video_data)], + ), + }, + "animation_node": { + RESPONSE: Message( + "Here's your animation!", + attachments=[Animation(**animation_data)], + ), + }, + "image_node": { + RESPONSE: Message( + "Here's your image!", + attachments=[Image(**image_data)], + ), + }, + "document_node": { + RESPONSE: Message( + "Here's your document!", + attachments=[Document(**document_data)], + ), + }, + "fallback_node": { + RESPONSE: Message( + "Unknown attachment type, try again! " + 'Supported attachments are: "location", ' + '"contact", "poll", "sticker", "audio", ' + '"video", "animation", "image" and "document".' + ), + }, + } +} + +# this variable is only for testing +happy_path = ( + (Message("/start"), Message("Hi")), + (Message("Hi"), Message("Hi")), + (Message("Bye"), Message("Hi")), +) + + +# %% +interface = PollingTelegramInterface(token=os.environ["TG_BOT_TOKEN"]) + + +# %% +pipeline = Pipeline.from_script( + script=script, + start_label=("main_flow", "start_node"), + fallback_label=("main_flow", "fallback_node"), + messenger_interface=interface, + # The interface can be passed as a pipeline argument. +) + + +def main(): + pipeline.run() + + +if __name__ == "__main__" and is_interactive_mode(): + # prevent run during doc building + main() From 33c72cb1792cc36db6cdfb478d1aed9f4f5c4ca7 Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 23 Apr 2024 21:36:56 +0200 Subject: [PATCH 052/140] advanced tutorial added --- dff/messengers/telegram/abstract.py | 3 +- tutorials/messengers/telegram/2_buttons.py | 175 --------------- tutorials/messengers/telegram/3_advanced.py | 187 ++++++++++++++++ .../telegram/3_buttons_with_callback.py | 177 --------------- tutorials/messengers/telegram/4_conditions.py | 148 ------------ .../telegram/5_conditions_with_media.py | 211 ------------------ .../telegram/6_conditions_extras.py | 144 ------------ .../messengers/telegram/7_polling_setup.py | 61 ----- .../messengers/telegram/8_webhook_setup.py | 59 ----- 9 files changed, 189 insertions(+), 976 deletions(-) delete mode 100644 tutorials/messengers/telegram/2_buttons.py create mode 100644 tutorials/messengers/telegram/3_advanced.py delete mode 100644 tutorials/messengers/telegram/3_buttons_with_callback.py delete mode 100644 tutorials/messengers/telegram/4_conditions.py delete mode 100644 tutorials/messengers/telegram/5_conditions_with_media.py delete mode 100644 tutorials/messengers/telegram/6_conditions_extras.py delete mode 100644 tutorials/messengers/telegram/7_polling_setup.py delete mode 100644 tutorials/messengers/telegram/8_webhook_setup.py diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index a1faca25d..6b62ccdfc 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -412,7 +412,8 @@ async def cast_message_to_telegram_and_send( async def _on_event( self, update: Update, _: ContextTypes.DEFAULT_TYPE, create_message: Callable[[Update], Message] ) -> None: - if update.effective_chat is not None and update.message is not None: + data_available = update.message is not None or update.callback_query is not None + if update.effective_chat is not None and data_available: message = create_message(update) message.original_message = update resp = await self.pipeline_runner(message, update.effective_chat.id) diff --git a/tutorials/messengers/telegram/2_buttons.py b/tutorials/messengers/telegram/2_buttons.py deleted file mode 100644 index e3926df12..000000000 --- a/tutorials/messengers/telegram/2_buttons.py +++ /dev/null @@ -1,175 +0,0 @@ -# %% [markdown] -""" -# Telegram: 2. Buttons - -This tutorial shows how to display and hide a basic keyboard in Telegram. - -Different %mddoclink(api,script.core.message,message) -classes are used for representing different common message features, -like Attachment, Audio, Button, Image, etc. -""" - - -# %pip install dff[telegram] - -# %% -import os - -import dff.script.conditions as cnd -from dff.script import TRANSITIONS, RESPONSE -from dff.script.core.message import Button, Keyboard, Message -from dff.pipeline import Pipeline -from dff.messengers.telegram import PollingTelegramInterface -from dff.utils.testing.common import is_interactive_mode - - -# %% [markdown] -""" -To display or hide a keyboard, you can utilize the `ui` field of the -`TelegramMessage` class. It can be initialized either with -a `TelegramUI` instance or with a custom telebot keyboard. - -Passing an instance of `RemoveKeyboard` to the `ui` field -will indicate that the keyboard should be removed. -""" - - -# %% -script = { - "root": { - "start": { - TRANSITIONS: { - ("general", "native_keyboard"): ( - lambda ctx, _: ctx.last_request.text - in ("/start", "/restart") - ), - }, - }, - "fallback": { - RESPONSE: Message( - text="Finishing test, send /restart command to restart" - ), - TRANSITIONS: { - ("general", "native_keyboard"): ( - lambda ctx, _: ctx.last_request.text - in ("/start", "/restart") - ), - }, - }, - }, - "general": { - "native_keyboard": { - RESPONSE: Message( - text="Question: What's 2 + 2?", - attachments=[ - Keyboard( - buttons=[ - [ - Button(text="5"), - Button(text="4"), - ], - ], - ), - ], - ), - TRANSITIONS: { - ("general", "success"): cnd.has_callback_query("4"), - ("general", "fail"): cnd.true(), - }, - }, - "success": { - RESPONSE: Message(text="Success!"), - TRANSITIONS: {("root", "fallback"): cnd.true()}, - }, - "fail": { - RESPONSE: Message( - text="Incorrect answer, type anything to try again" - ), - TRANSITIONS: {("general", "native_keyboard"): cnd.true()}, - }, - }, -} - -interface = PollingTelegramInterface(token=os.environ["TG_BOT_TOKEN"]) - -# this variable is only for testing -happy_path = ( - ( - Message(text="/start"), - Message( - text="Question: What's 2 + 2?", - attachments=[ - Keyboard( - buttons=[ - [ - Button(text="5"), - Button(text="4"), - ], - ], - ), - ], - ), - ), - ( - Message(text="5"), - Message(text="Incorrect answer, type anything to try again"), - ), - ( - Message(text="ok"), - Message( - text="Question: What's 2 + 2?", - attachments=[ - Keyboard( - buttons=[ - [ - Button(text="5"), - Button(text="4"), - ], - ], - ), - ], - ), - ), - ( - Message(text="4"), - Message(text="Success!"), - ), - ( - Message(text="Yay!"), - Message(text="Finishing test, send /restart command to restart"), - ), - ( - Message(text="/start"), - Message( - text="Question: What's 2 + 2?", - attachments=[ - Keyboard( - buttons=[ - [ - Button(text="5"), - Button(text="4"), - ], - ], - ), - ], - ), - ), -) - - -# %% -pipeline = Pipeline.from_script( - script=script, - start_label=("root", "start"), - fallback_label=("root", "fallback"), - messenger_interface=interface, -) - - -def main(): - pipeline.run() - - -if __name__ == "__main__" and is_interactive_mode(): - # prevent run during doc building - main() diff --git a/tutorials/messengers/telegram/3_advanced.py b/tutorials/messengers/telegram/3_advanced.py new file mode 100644 index 000000000..72bc54d4f --- /dev/null +++ b/tutorials/messengers/telegram/3_advanced.py @@ -0,0 +1,187 @@ +# %% [markdown] +""" +# Telegram: 3. Advanced + +The following tutorial shows how to run a regular DFF script in Telegram. +It asks users for the '/start' command and then loops in one place. + +Here, %mddoclink(api,messengers.telegram,PollingTelegramInterface) +class and [telebot](https://pytba.readthedocs.io/en/latest/index.html) +library are used for accessing telegram API in polling mode. + +Telegram API token is required to access telegram API. +""" + +# %pip install dff[telegram] + +# %% +import asyncio +import os + +from pydantic import HttpUrl +from telegram import InlineKeyboardButton, InlineKeyboardMarkup +from telegram.constants import ParseMode + +from dff.script import conditions as cnd +from dff.script import RESPONSE, TRANSITIONS, Message +from dff.messengers.telegram import PollingTelegramInterface +from dff.pipeline import Pipeline +from dff.script.core.keywords import GLOBAL +from dff.script.core.message import Document, Image, Location, Sticker +from dff.utils.testing.common import is_interactive_mode + + +# %% [markdown] +""" +In order to integrate your script with Telegram, you need an instance of +`TelegramMessenger` class and one of the following interfaces: +`PollingMessengerInterface` or `WebhookMessengerInterface`. + +`TelegramMessenger` encapsulates the bot logic. Like Telebot, +`TelegramMessenger` only requires a token to run. However, all parameters +from the Telebot class can be passed as keyword arguments. + +The two interfaces connect the bot to Telegram. They can be passed directly +to the DFF `Pipeline` instance. +""" + +#%% + +image_url = HttpUrl("https://avatars.githubusercontent.com/u/29918795?s=200&v=4") + +formatted_text = r""" +Here's your formatted text\! +You can see **text in bold** and _text in italic_\. +\> Here's a [link](https://github.com/deeppavlov/dialog_flow_framework) in a quote\. +Run /start command again to restart\. +""" + +location_data = {"latitude": 59.9386, "longitude": 30.3141} + +sticker_data = { + "id": "CAACAgIAAxkBAAErBZ1mKAbZvEOmhscojaIL5q0u8vgp1wACRygAAiSjCUtLa7RHZy76ezQE", +} + +image_data = { + "source": image_url, + "title": "DeepPavlov logo", + "has_spoiler": True, + "filename": "deeppavlov_logo.png", +} + +document_thumbnail = asyncio.run(Image(source=image_url).get_bytes(None)) + +document_data = { + "source": HttpUrl("https://aclanthology.org/P18-4021.pdf"), + "title": "DeepPavlov article", + "filename": "deeppavlov_article.pdf", + "thumbnail": document_thumbnail, +} + + +# %% +script = { + GLOBAL: { + TRANSITIONS: { + ("main_flow", "hmmm_node"): cnd.exact_match(Message("/start")), + } + }, + "main_flow": { + "start_node": {}, + "hmmm_node": { + RESPONSE: Message( + attachments=[ + Location( + latitude=58.431610, + longitude=27.792887, + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton("Cute formatted text!", callback_data="formatted"), + ], + [ + InlineKeyboardButton("Multiple attachments!", callback_data="attachments"), + ], + [ + InlineKeyboardButton("Secret image!", callback_data="secret"), + ], + [ + InlineKeyboardButton("Document with thumbnail!", callback_data="thumbnail"), + ], + [ + InlineKeyboardButton("Restart!", callback_data="restart"), + InlineKeyboardButton("Quit!", callback_data="quit"), + ], + ], + ), + ), + ], + ), + TRANSITIONS: { + "formatted_node": cnd.has_callback_query("formatted"), + "attachments_node": cnd.has_callback_query("attachments"), + "secret_node": cnd.has_callback_query("secret"), + "thumbnail_node": cnd.has_callback_query("thumbnail"), + "hmmm_node": cnd.has_callback_query("restart"), + "fallback_node": cnd.has_callback_query("quit"), + } + }, + "formatted_node": { + RESPONSE: Message(formatted_text, parse_mode=ParseMode.MARKDOWN_V2), + }, + "attachments_node": { + RESPONSE: Message( + "Here's your message with multiple attachments (a location and a sticker)!\nRun /start command again to restart.", + attachments=[ + Location(**location_data), + Sticker(**sticker_data), + ], + ), + }, + "secret_node": { + RESPONSE: Message( + "Here's your secret image! Run /start command again to restart.", + attachments=[Image(**image_data)], + ), + }, + "thumbnail_node": { + RESPONSE: Message( + "Here's your document with tumbnail! Run /start command again to restart.", + attachments=[Document(**document_data)], + ), + }, + "fallback_node": { + RESPONSE: Message("Bot has entered unrecoverable state :/\nRun /start command again to restart."), + }, + } +} + +# this variable is only for testing +happy_path = ( + (Message("/start"), Message("Hi")), + (Message("Hi"), Message("Hi")), + (Message("Bye"), Message("Hi")), +) + + +# %% +interface = PollingTelegramInterface(token=os.environ["TG_BOT_TOKEN"]) + + +# %% +pipeline = Pipeline.from_script( + script=script, + start_label=("main_flow", "start_node"), + fallback_label=("main_flow", "fallback_node"), + messenger_interface=interface, + # The interface can be passed as a pipeline argument. +) + + +def main(): + pipeline.run() + + +if __name__ == "__main__" and is_interactive_mode(): + # prevent run during doc building + main() diff --git a/tutorials/messengers/telegram/3_buttons_with_callback.py b/tutorials/messengers/telegram/3_buttons_with_callback.py deleted file mode 100644 index d993672a8..000000000 --- a/tutorials/messengers/telegram/3_buttons_with_callback.py +++ /dev/null @@ -1,177 +0,0 @@ -# %% [markdown] -""" -# Telegram: 3. Buttons with Callback - -This tutorial demonstrates, how to add an inline keyboard and utilize -inline queries. - -Different %mddoclink(api,script.core.message,message) -classes are used for representing different common message features, -like Attachment, Audio, Button, Image, etc. -""" - - -# %pip install dff[telegram] - -# %% -import os - -import dff.script.conditions as cnd -from dff.script import TRANSITIONS, RESPONSE -from dff.pipeline import Pipeline -from dff.script.core.message import Button, Keyboard, Message -from dff.messengers.telegram import PollingTelegramInterface -from dff.utils.testing.common import is_interactive_mode - - -# %% [markdown] -""" -If you want to send an inline keyboard to your Telegram chat, -set `is_inline` field of the `TelegramUI` instance to `True` -(note that it is inline by default, so you could also omit it). - -Pushing a button of an inline keyboard results in a callback -query being sent to your bot. The data of the query -is stored in the `callback_query` field of a user `TelegramMessage`. -""" - - -# %% -script = { - "root": { - "start": { - TRANSITIONS: { - ("general", "keyboard"): ( - lambda ctx, _: ctx.last_request.text - in ("/start", "/restart") - ), - }, - }, - "fallback": { - RESPONSE: Message( - text="Finishing test, send /restart command to restart" - ), - TRANSITIONS: { - ("general", "keyboard"): ( - lambda ctx, _: ctx.last_request.text - in ("/start", "/restart") - ) - }, - }, - }, - "general": { - "keyboard": { - RESPONSE: Message( - text="Starting test! What's 9 + 10?", - attachments=[ - Keyboard( - buttons=[ - [ - Button(text="19", data="correct"), - Button(text="21", data="wrong"), - ], - ], - ), - ], - ), - TRANSITIONS: { - ("general", "success"): cnd.has_callback_query("correct"), - ("general", "fail"): cnd.has_callback_query("wrong"), - }, - }, - "success": { - RESPONSE: Message(text="Success!"), - TRANSITIONS: {("root", "fallback"): cnd.true()}, - }, - "fail": { - RESPONSE: Message( - text="Incorrect answer, type anything to try again" - ), - TRANSITIONS: {("general", "keyboard"): cnd.true()}, - }, - }, -} - -# this variable is only for testing -happy_path = ( - ( - Message(text="/start"), - Message( - text="Starting test! What's 9 + 10?", - attachments=[ - Keyboard( - buttons=[ - [ - Button(text="19", data="correct"), - Button(text="21", data="wrong"), - ], - ], - ), - ], - ), - ), - ( - Message(text="wrong"), - Message(text="Incorrect answer, type anything to try again"), - ), - ( - Message(text="try again"), - Message( - text="Starting test! What's 9 + 10?", - attachments=[ - Keyboard( - buttons=[ - [ - Button(text="19", data="correct"), - Button(text="21", data="wrong"), - ], - ], - ), - ], - ), - ), - ( - Message(text="correct"), - Message(text="Success!"), - ), - ( - Message(text="Yay!"), - Message(text="Finishing test, send /restart command to restart"), - ), - ( - Message(text="/restart"), - Message( - text="Starting test! What's 9 + 10?", - attachments=[ - Keyboard( - buttons=[ - [ - Button(text="19", data="correct"), - Button(text="21", data="wrong"), - ], - ], - ), - ], - ), - ), -) - -interface = PollingTelegramInterface(token=os.environ["TG_BOT_TOKEN"]) - - -# %% -pipeline = Pipeline.from_script( - script=script, - start_label=("root", "start"), - fallback_label=("root", "fallback"), - messenger_interface=interface, -) - - -def main(): - pipeline.run() - - -if __name__ == "__main__" and is_interactive_mode(): - # prevent run during doc building - main() diff --git a/tutorials/messengers/telegram/4_conditions.py b/tutorials/messengers/telegram/4_conditions.py deleted file mode 100644 index 38d65d894..000000000 --- a/tutorials/messengers/telegram/4_conditions.py +++ /dev/null @@ -1,148 +0,0 @@ -# %% [markdown] -""" -# Telegram: 4. Conditions - -This tutorial shows how to process Telegram updates in your script -and reuse handler triggers from the `pytelegrambotapi` library. - -Here, %mddoclink(api,messengers.telegram) -message `original_message` component used -for graph navigation according to Telegram events. -""" - - -# %pip install dff[telegram] - -# %% -import os - -from dff.script import TRANSITIONS, RESPONSE - -import dff.script.conditions as cnd -from dff.messengers.telegram import PollingTelegramInterface -from dff.pipeline import Pipeline -from dff.script.core.context import Context -from dff.script.core.message import Message -from dff.utils.testing.common import is_interactive_mode - - -# %% [markdown] -""" -In our Telegram module, we adopted the system of filters -available in the `pytelegrambotapi` library. - -- Setting the `update_type` will allow filtering by update type: - if you want the condition to trigger only on updates of the type - `edited_message`, set it to `UpdateType.EDITED_MESSAGE`. - The field defaults to `message`. -- `func` argument on the other hand allows you to define arbitrary conditions. -- `regexp` creates a regular expression filter, etc. - -Note: -It is possible to use `cnd.exact_match` as a condition -(as seen in previous tutorials). However, the functionality -of that approach is lacking: - -At this moment only two fields of `Message` are set during update processing: - -- `text` stores the `text` field of `message` updates -- `callback_query` stores the `data` field of `callback_query` updates - -For more information see tutorial `3_buttons_with_callback.py`. -""" - - -# %% -def latest_message_test_has_music(ctx: Context, _: Pipeline) -> bool: - if ctx.last_request is None: - return False - if ctx.last_request.original_message is None: - return False - if ctx.last_request.original_message.message is None: - return False - if ctx.last_request.original_message.message.text is None: - return False - return "music" in ctx.last_request.original_message.message.text - - -# %% -script = { - "greeting_flow": { - "start_node": { - TRANSITIONS: { - "node1": cnd.any( - [ - cnd.exact_match(Message(text="/start")), - cnd.exact_match(Message(text="/restart")), - ] - ) - }, - }, - "node1": { - RESPONSE: Message(text="Hi, how are you?"), - TRANSITIONS: {"node2": cnd.regexp("fine")}, - }, - "node2": { - RESPONSE: Message(text="Good. What do you want to talk about?"), - TRANSITIONS: {"node3": latest_message_test_has_music}, - }, - "node3": { - RESPONSE: Message(text="Sorry, I can not talk about music now."), - TRANSITIONS: {"node4": lambda _, __: True}, - # This condition is true for any type of update - }, - "node4": { - RESPONSE: Message(text="bye"), - TRANSITIONS: {"node1": lambda _, __: True}, - # This condition is true if the last update is of type `message` - }, - "fallback_node": { - RESPONSE: Message(text="Ooops"), - TRANSITIONS: { - "node1": cnd.any( - [ - cnd.exact_match(Message(text="/start")), - cnd.exact_match(Message(text="/restart")), - ] - ) - }, - }, - } -} - -# this variable is only for testing -happy_path = ( - (Message(text="/start"), Message(text="Hi, how are you?")), - ( - Message(text="I'm fine"), - Message(text="Good. What do you want to talk about?"), - ), - ( - Message(text="About music"), - Message(text="Sorry, I can not talk about music now."), - ), - (Message(text="ok"), Message(text="bye")), - (Message(text="bye"), Message(text="Hi, how are you?")), -) - - -# %% -interface = PollingTelegramInterface(token=os.environ["TG_BOT_TOKEN"]) - - -# %% -pipeline = Pipeline.from_script( - script=script, - start_label=("greeting_flow", "start_node"), - fallback_label=("greeting_flow", "fallback_node"), - messenger_interface=interface, -) - - -def main(): - pipeline.run() - - -if __name__ == "__main__" and is_interactive_mode(): - # prevent run during doc building - main() diff --git a/tutorials/messengers/telegram/5_conditions_with_media.py b/tutorials/messengers/telegram/5_conditions_with_media.py deleted file mode 100644 index 646df8f39..000000000 --- a/tutorials/messengers/telegram/5_conditions_with_media.py +++ /dev/null @@ -1,211 +0,0 @@ -# %% [markdown] -""" -# Telegram: 5. Conditions with Media - -This tutorial shows how to use media-related logic in your script. - -Here, %mddoclink(api,messengers.telegram) -message `original_message` component used -for graph navigation according to Telegram events. - -Different %mddoclink(api,script.core.message,message) -classes are used for representing different common message features, -like Attachment, Audio, Button, Image, etc. -""" - - -# %pip install dff[telegram] - -# %% -import os - -from pydantic import HttpUrl - -import dff.script.conditions as cnd -from dff.script import TRANSITIONS, RESPONSE -from dff.script.core.context import Context -from dff.script.core.message import Message, Image -from dff.messengers.telegram import PollingTelegramInterface -from dff.pipeline import Pipeline -from dff.utils.testing.common import is_interactive_mode - - -# %% - -picture_url = HttpUrl( - "https://avatars.githubusercontent.com/u/29918795?s=200&v=4" -) - - -# %% [markdown] -""" -To filter user messages depending on whether or not media files were sent, -you can use the `content_types` parameter of the -`Context.last_request.original_message.message.document`. -""" - - -# %% -interface = PollingTelegramInterface(token=os.environ["TG_BOT_TOKEN"]) - - -def latest_message_has_photos(ctx: Context, _: Pipeline) -> bool: - if ctx.last_request is None: - return False - if ctx.last_request.original_message is None: - return False - if ctx.last_request.original_message.message is None: - return False - if ctx.last_request.original_message.message.photo is None: - return False - return len(ctx.last_request.original_message.message.photo) > 0 - - -def latest_message_has_images(ctx: Context, _: Pipeline) -> bool: - if ctx.last_request is None: - return False - if ctx.last_request.original_message is None: - return False - if ctx.last_request.original_message.message is None: - return False - if ctx.last_request.original_message.message.document is None: - return False - return ( - ctx.last_request.original_message.message.document.mime_type - == "image/jpeg" - ) - - -def latest_message_has_text(ctx: Context, _: Pipeline) -> bool: - if ctx.last_request is None: - return False - if ctx.last_request.original_message is None: - return False - if ctx.last_request.original_message.message is None: - return False - return ctx.last_request.original_message.message.text is None - - -# %% -script = { - "root": { - "start": { - TRANSITIONS: { - ("pics", "ask_picture"): cnd.any( - [ - cnd.exact_match(Message(text="/start")), - cnd.exact_match(Message(text="/restart")), - ] - ) - }, - }, - "fallback": { - RESPONSE: Message( - text="Finishing test, send /restart command to restart" - ), - TRANSITIONS: { - ("pics", "ask_picture"): cnd.any( - [ - cnd.exact_match(Message(text="/start")), - cnd.exact_match(Message(text="/restart")), - ] - ) - }, - }, - }, - "pics": { - "ask_picture": { - RESPONSE: Message(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. - latest_message_has_photos, - latest_message_has_images, - ] - ), - ( - "pics", - "send_many", - ): latest_message_has_text, - ("pics", "ask_picture"): cnd.true(), - }, - }, - "send_one": { - # An HTTP path or a path to a local file can be used here. - RESPONSE: Message( - text="Here's my picture!", - attachments=[Image(source=picture_url)], - ), - TRANSITIONS: {("root", "fallback"): cnd.true()}, - }, - "send_many": { - RESPONSE: Message( - text="Look at my pictures!", - # An HTTP path or a path to a local file can be used here. - attachments=[Image(source=picture_url)] * 2, - ), - TRANSITIONS: {("root", "fallback"): cnd.true()}, - }, - }, -} - - -# testing -happy_path = ( - ( - Message(text="/start"), - Message(text="Send me a picture"), - ), - ( - Message(attachments=[Image(source=picture_url)]), - Message( - text="Here's my picture!", - attachments=[Image(source=picture_url)], - ), - ), - ( - Message(text="ok"), - Message(text="Finishing test, send /restart command to restart"), - ), - ( - Message(text="/restart"), - Message(text="Send me a picture"), - ), - ( - Message(text="No"), - Message( - text="Look at my pictures!", - attachments=list(tuple([Image(source=picture_url)] * 2)), - ), - ), - ( - Message(text="ok"), - Message(text="Finishing test, send /restart command to restart"), - ), - ( - Message(text="/restart"), - Message(text="Send me a picture"), - ), -) - - -# %% -pipeline = Pipeline.from_script( - script=script, - start_label=("root", "start"), - fallback_label=("root", "fallback"), - messenger_interface=interface, -) - - -def main(): - pipeline.run() - - -if __name__ == "__main__" and is_interactive_mode(): - # prevent run during doc building - main() diff --git a/tutorials/messengers/telegram/6_conditions_extras.py b/tutorials/messengers/telegram/6_conditions_extras.py deleted file mode 100644 index f74faa993..000000000 --- a/tutorials/messengers/telegram/6_conditions_extras.py +++ /dev/null @@ -1,144 +0,0 @@ -# %% [markdown] -""" -# Telegram: 6. Conditions Extras - -This tutorial shows how to use additional update filters -inherited from the `pytelegrambotapi` library. - -Here, %mddoclink(api,messengers.telegram) -message `original_message` component -is used for telegram message type checking. -""" - - -# %pip install dff[telegram] - -# %% -import os - -from dff.script import TRANSITIONS, RESPONSE, GLOBAL -import dff.script.conditions as cnd -from dff.messengers.telegram import PollingTelegramInterface -from dff.pipeline import Pipeline -from dff.script.core.context import Context -from dff.script.core.message import Message -from dff.utils.testing.common import is_interactive_mode - - -# %% [markdown] -""" -In our Telegram module, we adopted the system of filters -available in the `pytelegrambotapi` library. - -Aside from `MESSAGE` you can use -other triggers to interact with the api. In this tutorial, we use -handlers of other type as global conditions that trigger a response -from the bot. - -Here, we use the following triggers: - -* `chat_join_request`: join request is sent to the chat where the bot is. -* `my_chat_member`: triggered when the bot is invited to a chat. -* `inline_query`: triggered when an inline query is being sent to the bot. - -The other available conditions are: - -* `channel_post`: new post is created in a channel the bot is subscribed to; -* `edited_channel_post`: post is edited in a channel the bot is subscribed to; -* `shipping_query`: shipping query is sent by the user; -* `pre_checkout_query`: order confirmation is sent by the user; -* `poll`: poll is sent to the chat; -* `poll_answer`: users answered the poll sent by the bot. - -You can read more on those in the Telegram documentation -or in the docs for the `telebot` library. -""" - - -# %% -def latest_message_is_new_chat_member( - ctx: Context, _: Pipeline -) -> bool: - if ctx.last_request is None: - return False - if ctx.last_request.original_message is None: - return False - if ctx.last_request.original_message.message is None: - return False - return ( - ctx.last_request.original_message.message.new_chat_members is not None - ) - - -def latest_message_is_callback_query( - ctx: Context, _: Pipeline -) -> bool: - if ctx.last_request is None: - return False - if ctx.last_request.original_message is None: - return False - return ctx.last_request.original_message.inline_query is not None - - -# %% -script = { - GLOBAL: { - TRANSITIONS: { - # say hi when someone enters the chat - ( - "greeting_flow", - "node1", - ): latest_message_is_new_chat_member, - # send a message when inline query is received - ( - "greeting_flow", - "node2", - ): latest_message_is_callback_query, - }, - }, - "greeting_flow": { - "start_node": { - TRANSITIONS: { - "node1": cnd.any( - [ - cnd.exact_match(Message(text="/start")), - cnd.exact_match(Message(text="/restart")), - ] - ) - }, - }, - "node1": { - RESPONSE: Message(text="Hi"), - TRANSITIONS: {"start_node": cnd.true()}, - }, - "node2": { - RESPONSE: Message(text="Inline query received."), - TRANSITIONS: {"start_node": cnd.true()}, - }, - "fallback_node": { - RESPONSE: Message(text="Ooops"), - }, - }, -} - - -# %% -interface = PollingTelegramInterface(token=os.environ["TG_BOT_TOKEN"]) - - -# %% -pipeline = Pipeline.from_script( - script=script, - start_label=("greeting_flow", "start_node"), - fallback_label=("greeting_flow", "fallback_node"), - messenger_interface=interface, -) - - -def main(): - pipeline.run() - - -if __name__ == "__main__" and is_interactive_mode(): - # prevent run during doc building - main() diff --git a/tutorials/messengers/telegram/7_polling_setup.py b/tutorials/messengers/telegram/7_polling_setup.py deleted file mode 100644 index 81ded86ac..000000000 --- a/tutorials/messengers/telegram/7_polling_setup.py +++ /dev/null @@ -1,61 +0,0 @@ -# %% [markdown] -""" -# Telegram: 7. Polling Setup - -The following tutorial shows how to configure `PollingTelegramInterface`. - -See %mddoclink(api,messengers.telegram,PollingTelegramInterface) -for more information. -""" - -# %pip install dff[telegram] - -# %% -import os - -from dff.messengers.telegram import PollingTelegramInterface -from dff.pipeline import Pipeline - -from dff.utils.testing.common import is_interactive_mode -from dff.utils.testing.toy_script import TOY_SCRIPT_ARGS, HAPPY_PATH - - -# %% [markdown] -""" -`PollingTelegramInterface` can be configured with the same parameters -that are used in the `pytelegrambotapi` library, specifically: - -* interval - time between calls to the API. -* allowed updates - updates that should be fetched. -* timeout - general timeout. -* long polling timeout - timeout for polling. -""" - - -# %% -interface = PollingTelegramInterface( - token=os.environ["TG_BOT_TOKEN"], - interval=2, - timeout=30, -) - - -# testing -happy_path = HAPPY_PATH - - -# %% -pipeline = Pipeline.from_script( - *TOY_SCRIPT_ARGS, - messenger_interface=interface, - # The interface can be passed as a pipeline argument -) - - -def main(): - pipeline.run() - - -if __name__ == "__main__" and is_interactive_mode(): - # prevent run during doc building - main() diff --git a/tutorials/messengers/telegram/8_webhook_setup.py b/tutorials/messengers/telegram/8_webhook_setup.py deleted file mode 100644 index f29e24620..000000000 --- a/tutorials/messengers/telegram/8_webhook_setup.py +++ /dev/null @@ -1,59 +0,0 @@ -# %% [markdown] -""" -# Telegram: 8. Webhook Setup - -The following tutorial shows how to use `CallbackTelegramInterface` -that makes your bot accessible through a public webhook. - -See %mddoclink(api,messengers.common.interface,CallbackMessengerInterface) -for more information. -""" - -# %pip install dff[telegram] flask - -# %% -import os - -from dff.messengers.telegram import CallbackTelegramInterface -from dff.pipeline import Pipeline -from dff.utils.testing.toy_script import TOY_SCRIPT_ARGS, HAPPY_PATH -from dff.utils.testing.common import is_interactive_mode - - -# %% [markdown] -""" -To set up a webhook, you need a messenger and a web application instance. -This class can be configured with the following parameters: - -* app - Flask application. You can pass an application with an arbitrary - number of pre-configured routes. Created automatically if not set. -* host - application host. -* port - application port. -* endpoint - bot access endpoint. -* full_uri - full public address of the endpoint. HTTPS should be enabled - for successful configuration. -""" - - -# %% -interface = CallbackTelegramInterface(token=os.environ["TG_BOT_TOKEN"]) - - -# %% -pipeline = Pipeline.from_script( - *TOY_SCRIPT_ARGS, - messenger_interface=interface, - # The interface can be passed as a pipeline argument -) - -# testing -happy_path = HAPPY_PATH - - -def main(): - pipeline.run() - - -if __name__ == "__main__" and is_interactive_mode(): - # prevent run during doc building - main() From 17280e37046986f4d3ebe36693089190fcc0e9ed Mon Sep 17 00:00:00 2001 From: pseusys Date: Fri, 26 Apr 2024 15:00:19 +0200 Subject: [PATCH 053/140] `cached_filename` field added --- dff/messengers/common/interface.py | 8 +++++--- dff/messengers/telegram/abstract.py | 13 ++++++------- dff/script/core/message.py | 21 ++++++++++++++++----- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/dff/messengers/common/interface.py b/dff/messengers/common/interface.py index 5c9d8fc07..01ec650ba 100644 --- a/dff/messengers/common/interface.py +++ b/dff/messengers/common/interface.py @@ -56,9 +56,8 @@ async def connect(self, pipeline_runner: PipelineRunnerFunction): """ raise NotImplementedError - async def populate_attachment(self, attachment: DataAttachment) -> None: - if attachment.source is None: - raise NotImplementedError + async def populate_attachment(self, attachment: DataAttachment) -> bytes: + raise RuntimeError(f"Messanger interface {type(self).__name__} can't populate attachment {attachment}!") class PollingMessengerInterface(MessengerInterface): @@ -135,6 +134,9 @@ async def connect( self._on_exception(e) break + async def populate_attachment(self, attachment: DataAttachment) -> bytes: + raise RuntimeError(f"Plain pollin") + class CallbackMessengerInterface(MessengerInterface): """ diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index 6b62ccdfc..23f83977f 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -61,14 +61,13 @@ def __init__(self, token: str, attachments_directory: Optional[Path] = None) -> self.application.add_handler(MessageHandler(ALL, self.on_message)) self.application.add_handler(CallbackQueryHandler(self.on_callback)) - async def populate_attachment(self, attachment: DataAttachment) -> None: # pragma: no cover - if attachment.title is not None and attachment.id is not None: - file_name = self.attachments_directory / str(attachment.title) - if not file_name.exists(): - await (await self.application.bot.get_file(attachment.id)).download_to_drive(file_name) - attachment.source = FilePath(file_name) + async def populate_attachment(self, attachment: DataAttachment) -> bytes: # pragma: no cover + if attachment.id is not None: + file = await self.application.bot.get_file(attachment.id) + data = await file.download_as_bytearray() + return bytes(data) else: - raise ValueError(f"For attachment {attachment} title or id is not defined!") + raise ValueError(f"For attachment {attachment} id is not defined!") def extract_message_from_telegram(self, update: TelegramMessage) -> Message: # pragma: no cover message = Message() diff --git a/dff/script/core/message.py b/dff/script/core/message.py index 5d437fcfc..73e8bc1fa 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -9,6 +9,7 @@ from enum import Enum, auto from pathlib import Path from urllib.request import urlopen +from uuid import uuid4 from pydantic import Field, field_validator, FilePath, HttpUrl, BaseModel, model_validator from pydantic_core import Url @@ -98,20 +99,30 @@ class DataAttachment(Attachment): """ source: Optional[Union[HttpUrl, FilePath]] = None + cached_filename: Optional[FilePath] = None id: Optional[str] = None # id field is made separate to simplify type validation title: Optional[str] = None - async def get_bytes(self, from_messenger_interface: MessengerInterface) -> Optional[bytes]: - if self.source is None: - await from_messenger_interface.populate_attachment(self) + async def _cache_attachment(self, data: bytes, directory: Path) -> None: + title = str(uuid4()) if self.title is None else self.title + self.cached_filename = directory / title + with open(self.cached_filename, "wb") as file: + file.write(data) + + async def get_bytes(self, from_interface: MessengerInterface) -> Optional[bytes]: if isinstance(self.source, Path): with open(self.source, "rb") as file: return file.read() + elif isinstance(self.cached_filename, Path): + with open(self.cached_filename, "rb") as file: + return file.read() elif isinstance(self.source, Url): with urlopen(self.source.unicode_string()) as url: - return url.read() + attachment_data = url.read() else: - return None + attachment_data = await from_interface.populate_attachment(self) + await self._cache_attachment(attachment_data, from_interface.attachments_directory) + return attachment_data def __eq__(self, other): if isinstance(other, DataAttachment): From 2a1f01a0f4d0b7303ab0d99f54f1ec5e2052b69d Mon Sep 17 00:00:00 2001 From: pseusys Date: Fri, 26 Apr 2024 17:12:36 +0200 Subject: [PATCH 054/140] Tutorial texts altered. --- tutorials/messengers/telegram/1_basic.py | 12 ++-- .../messengers/telegram/2_attachments.py | 26 ++++----- tutorials/messengers/telegram/3_advanced.py | 56 ++++++++++++++----- 3 files changed, 60 insertions(+), 34 deletions(-) diff --git a/tutorials/messengers/telegram/1_basic.py b/tutorials/messengers/telegram/1_basic.py index ce750afea..9252e96f1 100644 --- a/tutorials/messengers/telegram/1_basic.py +++ b/tutorials/messengers/telegram/1_basic.py @@ -5,8 +5,8 @@ The following tutorial shows how to run a regular DFF script in Telegram. It asks users for the '/start' command and then loops in one place. -Here, %mddoclink(api,messengers.telegram,PollingTelegramInterface) -class and [telebot](https://pytba.readthedocs.io/en/latest/index.html) +Here, %mddoclink(api,messengers.telegram.interface,PollingTelegramInterface) +class and [python-telegram-bot](https://docs.python-telegram-bot.org/) library are used for accessing telegram API in polling mode. Telegram API token is required to access telegram API. @@ -29,11 +29,11 @@ class and [telebot](https://pytba.readthedocs.io/en/latest/index.html) """ In order to integrate your script with Telegram, you need an instance of `TelegramMessenger` class and one of the following interfaces: -`PollingMessengerInterface` or `WebhookMessengerInterface`. +`PollingMessengerInterface` or `CallbackTelegramInterface`. -`TelegramMessenger` encapsulates the bot logic. Like Telebot, -`TelegramMessenger` only requires a token to run. However, all parameters -from the Telebot class can be passed as keyword arguments. +`TelegramMessenger` encapsulates the bot logic. The` only required +argument for a bot to run is a token. Some other parameters +(such as host, port, interval, etc.) can be passed as keyword arguments. The two interfaces connect the bot to Telegram. They can be passed directly to the DFF `Pipeline` instance. diff --git a/tutorials/messengers/telegram/2_attachments.py b/tutorials/messengers/telegram/2_attachments.py index 23b675352..7e144c29a 100644 --- a/tutorials/messengers/telegram/2_attachments.py +++ b/tutorials/messengers/telegram/2_attachments.py @@ -2,11 +2,11 @@ """ # Telegram: 2. Attachments -The following tutorial shows how to run a regular DFF script in Telegram. -It asks users for the '/start' command and then loops in one place. +The following tutorial shows how to send different attachments using +telegram interfaces. -Here, %mddoclink(api,messengers.telegram,PollingTelegramInterface) -class and [telebot](https://pytba.readthedocs.io/en/latest/index.html) +Here, %mddoclink(api,messengers.telegram.interface,PollingTelegramInterface) +class and [python-telegram-bot](https://docs.python-telegram-bot.org/) library are used for accessing telegram API in polling mode. Telegram API token is required to access telegram API. @@ -29,16 +29,9 @@ class and [telebot](https://pytba.readthedocs.io/en/latest/index.html) # %% [markdown] """ -In order to integrate your script with Telegram, you need an instance of -`TelegramMessenger` class and one of the following interfaces: -`PollingMessengerInterface` or `WebhookMessengerInterface`. - -`TelegramMessenger` encapsulates the bot logic. Like Telebot, -`TelegramMessenger` only requires a token to run. However, all parameters -from the Telebot class can be passed as keyword arguments. - -The two interfaces connect the bot to Telegram. They can be passed directly -to the DFF `Pipeline` instance. +Example attachment data is specified below in form of dictionaries. +List of attachments that telegram messenger interface can send can be found here: +%mddoclink(api,messengers.telegram.abstract,_AbstractTelegramInterface#response_attachments). """ # %% @@ -81,6 +74,11 @@ class and [telebot](https://pytba.readthedocs.io/en/latest/index.html) } +# %% [markdown] +""" +The bot below sends different attachments on request. +""" + # %% script = { GLOBAL: { diff --git a/tutorials/messengers/telegram/3_advanced.py b/tutorials/messengers/telegram/3_advanced.py index 72bc54d4f..be6b90336 100644 --- a/tutorials/messengers/telegram/3_advanced.py +++ b/tutorials/messengers/telegram/3_advanced.py @@ -2,11 +2,10 @@ """ # Telegram: 3. Advanced -The following tutorial shows how to run a regular DFF script in Telegram. -It asks users for the '/start' command and then loops in one place. +The following tutorial shows several advanced cases of user-to-bot interaction. -Here, %mddoclink(api,messengers.telegram,PollingTelegramInterface) -class and [telebot](https://pytba.readthedocs.io/en/latest/index.html) +Here, %mddoclink(api,messengers.telegram.interface,PollingTelegramInterface) +class and [python-telegram-bot](https://docs.python-telegram-bot.org/) library are used for accessing telegram API in polling mode. Telegram API token is required to access telegram API. @@ -26,6 +25,7 @@ class and [telebot](https://pytba.readthedocs.io/en/latest/index.html) from dff.script import RESPONSE, TRANSITIONS, Message from dff.messengers.telegram import PollingTelegramInterface from dff.pipeline import Pipeline +from dff.script.core.context import Context from dff.script.core.keywords import GLOBAL from dff.script.core.message import Document, Image, Location, Sticker from dff.utils.testing.common import is_interactive_mode @@ -33,16 +33,19 @@ class and [telebot](https://pytba.readthedocs.io/en/latest/index.html) # %% [markdown] """ -In order to integrate your script with Telegram, you need an instance of -`TelegramMessenger` class and one of the following interfaces: -`PollingMessengerInterface` or `WebhookMessengerInterface`. - -`TelegramMessenger` encapsulates the bot logic. Like Telebot, -`TelegramMessenger` only requires a token to run. However, all parameters -from the Telebot class can be passed as keyword arguments. - -The two interfaces connect the bot to Telegram. They can be passed directly -to the DFF `Pipeline` instance. +This bot shows different special telegram messenger interface use cases, +such as: + +1. Interactive keyboard with buttons. +2. Text formatted with Markdown V2. +3. Multiple attachments of different kind handling. +4. Image with a spoiler. +5. Document with a thumbnail. +6. Raw representation of different data user can send to the bot. + +Last option ("Raw attachments!") button might be especially interesting, +because it shows how bot precepts different telegram attachments sent by user +in terms and datastructures of Dialog Flow Framework. """ #%% @@ -79,6 +82,20 @@ class and [telebot](https://pytba.readthedocs.io/en/latest/index.html) } +# %% +formatted_request = r""" +Here's your previous request\! +```json +{} +``` +Run /start command again to restart\. +""" + +def stringify_previous_request(ctx: Context, _: Pipeline) -> Message: + dump = ctx.last_request.model_dump_json(indent=4) + return Message(formatted_request.format(dump), parse_mode=ParseMode.MARKDOWN_V2) + + # %% script = { GLOBAL: { @@ -108,6 +125,9 @@ class and [telebot](https://pytba.readthedocs.io/en/latest/index.html) [ InlineKeyboardButton("Document with thumbnail!", callback_data="thumbnail"), ], + [ + InlineKeyboardButton("Raw attachments!", callback_data="raw"), + ], [ InlineKeyboardButton("Restart!", callback_data="restart"), InlineKeyboardButton("Quit!", callback_data="quit"), @@ -122,6 +142,7 @@ class and [telebot](https://pytba.readthedocs.io/en/latest/index.html) "attachments_node": cnd.has_callback_query("attachments"), "secret_node": cnd.has_callback_query("secret"), "thumbnail_node": cnd.has_callback_query("thumbnail"), + "raw_init_node": cnd.has_callback_query("raw"), "hmmm_node": cnd.has_callback_query("restart"), "fallback_node": cnd.has_callback_query("quit"), } @@ -150,6 +171,13 @@ class and [telebot](https://pytba.readthedocs.io/en/latest/index.html) attachments=[Document(**document_data)], ), }, + "raw_init_node": { + RESPONSE: Message("Alright! Now send me any message and I'll send you it's raw data!"), + TRANSITIONS: { "raw_request_node": cnd.true }, + }, + "raw_request_node": { + RESPONSE: stringify_previous_request, + }, "fallback_node": { RESPONSE: Message("Bot has entered unrecoverable state :/\nRun /start command again to restart."), }, From fb58bda9bc8f1a913478186d6b403c3eb8085e3e Mon Sep 17 00:00:00 2001 From: pseusys Date: Fri, 26 Apr 2024 21:41:09 +0200 Subject: [PATCH 055/140] happy path removed, use cache flag added --- dff/script/core/message.py | 6 ++++-- tutorials/messengers/telegram/1_basic.py | 7 ------- tutorials/messengers/telegram/2_attachments.py | 7 ------- tutorials/messengers/telegram/3_advanced.py | 7 ------- 4 files changed, 4 insertions(+), 23 deletions(-) diff --git a/dff/script/core/message.py b/dff/script/core/message.py index 73e8bc1fa..d02742cbf 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -102,6 +102,7 @@ class DataAttachment(Attachment): cached_filename: Optional[FilePath] = None id: Optional[str] = None # id field is made separate to simplify type validation title: Optional[str] = None + use_cache: bool = True async def _cache_attachment(self, data: bytes, directory: Path) -> None: title = str(uuid4()) if self.title is None else self.title @@ -113,7 +114,7 @@ async def get_bytes(self, from_interface: MessengerInterface) -> Optional[bytes] if isinstance(self.source, Path): with open(self.source, "rb") as file: return file.read() - elif isinstance(self.cached_filename, Path): + elif self.cached_filename is not None: with open(self.cached_filename, "rb") as file: return file.read() elif isinstance(self.source, Url): @@ -121,7 +122,8 @@ async def get_bytes(self, from_interface: MessengerInterface) -> Optional[bytes] attachment_data = url.read() else: attachment_data = await from_interface.populate_attachment(self) - await self._cache_attachment(attachment_data, from_interface.attachments_directory) + if self.use_cache: + await self._cache_attachment(attachment_data, from_interface.attachments_directory) return attachment_data def __eq__(self, other): diff --git a/tutorials/messengers/telegram/1_basic.py b/tutorials/messengers/telegram/1_basic.py index 9252e96f1..5d557c604 100644 --- a/tutorials/messengers/telegram/1_basic.py +++ b/tutorials/messengers/telegram/1_basic.py @@ -57,13 +57,6 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) } } -# this variable is only for testing -happy_path = ( - (Message("/start"), Message("Hi")), - (Message("Hi"), Message("Hi")), - (Message("Bye"), Message("Hi")), -) - # %% interface = PollingTelegramInterface(token=os.environ["TG_BOT_TOKEN"]) diff --git a/tutorials/messengers/telegram/2_attachments.py b/tutorials/messengers/telegram/2_attachments.py index 7e144c29a..9694bbba8 100644 --- a/tutorials/messengers/telegram/2_attachments.py +++ b/tutorials/messengers/telegram/2_attachments.py @@ -178,13 +178,6 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) } } -# this variable is only for testing -happy_path = ( - (Message("/start"), Message("Hi")), - (Message("Hi"), Message("Hi")), - (Message("Bye"), Message("Hi")), -) - # %% interface = PollingTelegramInterface(token=os.environ["TG_BOT_TOKEN"]) diff --git a/tutorials/messengers/telegram/3_advanced.py b/tutorials/messengers/telegram/3_advanced.py index be6b90336..1ba3bf5a9 100644 --- a/tutorials/messengers/telegram/3_advanced.py +++ b/tutorials/messengers/telegram/3_advanced.py @@ -184,13 +184,6 @@ def stringify_previous_request(ctx: Context, _: Pipeline) -> Message: } } -# this variable is only for testing -happy_path = ( - (Message("/start"), Message("Hi")), - (Message("Hi"), Message("Hi")), - (Message("Bye"), Message("Hi")), -) - # %% interface = PollingTelegramInterface(token=os.environ["TG_BOT_TOKEN"]) From a2e0ca3e2d40d7888676fd3c261540be527af4c2 Mon Sep 17 00:00:00 2001 From: pseusys Date: Thu, 2 May 2024 01:08:30 +0200 Subject: [PATCH 056/140] literals added + tests started --- dff/messengers/telegram/abstract.py | 3 - dff/script/core/message.py | 20 ++++-- .../messengers/telegram/test_happy_paths.json | 11 +++ tests/messengers/telegram/test_tutorials.py | 18 +++++ tests/messengers/telegram/utils.py | 69 +++++++++++++++++++ 5 files changed, 111 insertions(+), 10 deletions(-) create mode 100644 tests/messengers/telegram/test_happy_paths.json create mode 100644 tests/messengers/telegram/test_tutorials.py create mode 100644 tests/messengers/telegram/utils.py diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index 23f83977f..3033f7f10 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -7,12 +7,9 @@ from pathlib import Path from typing import Callable, Optional -from pydantic import FilePath try: from telegram import ( - InlineKeyboardButton, - InlineKeyboardMarkup, InputMediaAnimation, InputMediaAudio, InputMediaDocument, diff --git a/dff/script/core/message.py b/dff/script/core/message.py index d02742cbf..8abb05fa2 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -5,7 +5,7 @@ DFF. It only contains types and properties that are compatible with most messaging services. """ -from typing import Any, Optional, List, Union +from typing import Any, Literal, Optional, List, Union from enum import Enum, auto from pathlib import Path from urllib.request import urlopen @@ -49,6 +49,7 @@ class Attachment(DataModel): class CallbackQuery(Attachment): query_string: Optional[str] + type: Literal["callback_query"] = "callback_query" class Location(Attachment): @@ -62,6 +63,7 @@ class Location(Attachment): longitude: float latitude: float + type: Literal["location"] = "location" def __eq__(self, other): if isinstance(other, Location): @@ -73,6 +75,7 @@ class Contact(Attachment): phone_number: str first_name: str last_name: Optional[str] + type: Literal["contact"] = "contact" class Invoice(Attachment): @@ -80,16 +83,19 @@ class Invoice(Attachment): description: str currency: str amount: int + type: Literal["invoice"] = "invoice" class PollOption(DataModel): text: str votes: int = Field(default=0) + type: Literal["poll_option"] = "poll_option" class Poll(Attachment): question: str options: List[PollOption] + type: Literal["poll"] = "poll" class DataAttachment(Attachment): @@ -155,37 +161,37 @@ def validate_source(cls, value): class Audio(DataAttachment): """Represents an audio file attachment.""" - pass + type: Literal["audio"] = "audio" class Video(DataAttachment): """Represents a video file attachment.""" - pass + type: Literal["video"] = "video" class Animation(DataAttachment): """Represents an animation file attachment.""" - pass + type: Literal["animation"] = "animation" class Image(DataAttachment): """Represents an image file attachment.""" - pass + type: Literal["image"] = "image" class Sticker(DataAttachment): """Represents a sticker as a file attachment.""" - pass + type: Literal["sticker"] = "sticker" class Document(DataAttachment): """Represents a document file attachment.""" - pass + type: Literal["document"] = "document" class Message(DataModel): diff --git a/tests/messengers/telegram/test_happy_paths.json b/tests/messengers/telegram/test_happy_paths.json new file mode 100644 index 000000000..145f62094 --- /dev/null +++ b/tests/messengers/telegram/test_happy_paths.json @@ -0,0 +1,11 @@ +{ + "1_basic": { + + }, + "2_attachments": { + + }, + "3_advanced": { + + } +} diff --git a/tests/messengers/telegram/test_tutorials.py b/tests/messengers/telegram/test_tutorials.py new file mode 100644 index 000000000..17d20074a --- /dev/null +++ b/tests/messengers/telegram/test_tutorials.py @@ -0,0 +1,18 @@ +from importlib import import_module +from pathlib import Path +import pytest + +from tests.test_utils import get_path_from_tests_to_current_dir + +dot_path_to_addon = get_path_from_tests_to_current_dir(__file__) +happy_paths_file = Path(__file__).parent / "test_happy_paths.json" + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + ["tutorial_module_name"], + [("1_basic"), ("2_attachments"), ("3_advanced")], +) +async def test_tutorials_memory(tutorial_module_name: str): + module = import_module(f"tutorials.{dot_path_to_addon}.{tutorial_module_name}") + module.interface.application diff --git a/tests/messengers/telegram/utils.py b/tests/messengers/telegram/utils.py new file mode 100644 index 000000000..b087cd1b3 --- /dev/null +++ b/tests/messengers/telegram/utils.py @@ -0,0 +1,69 @@ +from asyncio import get_event_loop +from typing import Any, Dict, Hashable, List, Optional, Tuple, TypeAlias +from pydantic import BaseModel + +from telegram import File, Update +from telegram.ext import ExtBot + +from dff.messengers.telegram.abstract import _AbstractTelegramInterface +from dff.script import Message +from dff.script.core.context import Context + +PathStep: TypeAlias = Tuple[Dict, Message, Message, Tuple[str, Tuple, Dict]] + + +class MockBot(BaseModel): + _original_bot: ExtBot + _latest_trace: Optional[Tuple[str, Tuple, Dict]] = None + + async def get_file(self, file_id: str) -> File: + return await self._original_bot.get_file(file_id) + + def __getattribute__(self, name: str) -> Any: + def set_trace(*args, **kwargs): + self._latest_trace = (name, args, kwargs) + + if hasattr(self, name): + return super().__getattribute__(name) + else: + return set_trace + + +class MockApplication(BaseModel): + mock_bot: MockBot + happy_path: List[PathStep] + _interface: _AbstractTelegramInterface + _latest_ctx: Optional[Context] = None + + @classmethod + def create(cls, interface: _AbstractTelegramInterface, happy_path: List[PathStep]) -> "MockApplication": + mock_bot = MockBot(_original_bot=interface.application.bot) + instance = cls(mock_bot=mock_bot, happy_path=happy_path, _interface=interface) + interface.pipeline_runner = instance._wrapped_pipeline_runner + return instance + + async def _wrapped_pipeline_runner(self, message: Message, ctx_id: Optional[Hashable] = None, update_ctx_misc: Optional[dict] = None) -> Context: + self._latest_ctx = await self._interface.pipeline_runner(message, ctx_id, update_ctx_misc) + return self._latest_ctx + + def _run_bot(self) -> None: + loop = get_event_loop() + for update, received, response, trace in self.happy_path: + if update["is_message"]: + update = Update() + loop.run_until_complete(self._interface.on_message(update, None)) # type: ignore + else: + update = Update() + loop.run_until_complete(self._interface.on_callback(update, None)) # type: ignore + assert self._latest_ctx is not None, "During pipeline runner execution, no context was produced!" + assert self._latest_ctx.last_request == received, "Expected request message is not equal to expected!" + assert self._latest_ctx.last_response == response, "Expected response message is not equal to expected!" + assert self.mock_bot._latest_trace == trace, "Expected trace is not equal to expected!" + self.mock_bot._latest_trace = None + self._latest_ctx = None + + def run_polling(self, poll_interval: float, timeout: int, allowed_updates: List[str]) -> None: + return self._run_bot() + + def run_webhook(self, listen: str, port: str, allowed_updates: List[str]) -> None: + return self._run_bot() From a07b73bbbef35f97de15cf0acb94b86d9f9724f5 Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 7 May 2024 19:51:35 +0200 Subject: [PATCH 057/140] Attachment links updated --- .../messengers/telegram/2_attachments.py | 19 +++++++++---------- tutorials/messengers/telegram/3_advanced.py | 4 ++-- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/tutorials/messengers/telegram/2_attachments.py b/tutorials/messengers/telegram/2_attachments.py index 9694bbba8..a2bb077bd 100644 --- a/tutorials/messengers/telegram/2_attachments.py +++ b/tutorials/messengers/telegram/2_attachments.py @@ -46,30 +46,29 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) } audio_data = { - "source": HttpUrl("https://commondatastorage.googleapis.com/codeskulptor-assets/Evillaugh.ogg"), - "title": "Evil laughter (scary alert!)", + "source": HttpUrl("https://github.com/deeppavlov/dialog_flow_framework/blob/example-attachments/separation-william-king.mp3"), + "title": '"Separation" melody by William King', } video_data = { - # TODO: I need help, this video results in doenloading timeout, we need another example. - "source": HttpUrl("https://archive.org/download/Rick_Astley_Never_Gonna_Give_You_Up/Rick_Astley_Never_Gonna_Give_You_Up.mp4"), - "title": "Totally not suspicious video...", + "source": HttpUrl("https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/crownfall-lags-nkognit0.mp4"), + "title": "Epic Dota2 gameplay by Nkognit0", } animation_data = { # For some reason, if we don't define filename explicitly, animation is sent as file. - "source": HttpUrl("https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExMmFuMGk5ODY0dG5pd242ODR6anB4bm4wZGN3cjg1N3A1M2ZxMjluYiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/SvP3FgHsFVm7zwMdH6/giphy.gif"), - "title": "Some random free gif :/", - "filename": "random.gif", + "source": HttpUrl("https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/hong-kong-simplyart4794.mp4"), + "title": "Hong Kong skyscraper views by Simplyart4794", + "filename": "hk.mp4", } image_data = { - "source": HttpUrl("https://avatars.githubusercontent.com/u/29918795?s=200&v=4"), + "source": HttpUrl("https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/deeppavlov.png"), "title": "DeepPavlov logo", } document_data = { - "source": HttpUrl("https://aclanthology.org/P18-4021.pdf"), + "source": HttpUrl("https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/deeppavlov-article.pdf"), "title": "DeepPavlov article", } diff --git a/tutorials/messengers/telegram/3_advanced.py b/tutorials/messengers/telegram/3_advanced.py index 1ba3bf5a9..fff967218 100644 --- a/tutorials/messengers/telegram/3_advanced.py +++ b/tutorials/messengers/telegram/3_advanced.py @@ -50,7 +50,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) #%% -image_url = HttpUrl("https://avatars.githubusercontent.com/u/29918795?s=200&v=4") +image_url = HttpUrl("https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/deeppavlov.png") formatted_text = r""" Here's your formatted text\! @@ -75,7 +75,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) document_thumbnail = asyncio.run(Image(source=image_url).get_bytes(None)) document_data = { - "source": HttpUrl("https://aclanthology.org/P18-4021.pdf"), + "source": HttpUrl("https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/deeppavlov-article.pdf"), "title": "DeepPavlov article", "filename": "deeppavlov_article.pdf", "thumbnail": document_thumbnail, From 87001c0df6c77cef658ab5ffc15b671eaf982d76 Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 7 May 2024 20:38:28 +0200 Subject: [PATCH 058/140] new_attachment_tests_added --- .gitignore | 3 + tests/script/core/test_message.py | 235 +++++++++++----- tutorials/messengers/telegram/3_advanced.py | 28 +- tutorials/script/responses/2_buttons.py | 259 ------------------ .../responses/{3_media.py => 2_media.py} | 2 +- ...{4_multi_message.py => 3_multi_message.py} | 2 +- 6 files changed, 196 insertions(+), 333 deletions(-) delete mode 100644 tutorials/script/responses/2_buttons.py rename tutorials/script/responses/{3_media.py => 2_media.py} (99%) rename tutorials/script/responses/{4_multi_message.py => 3_multi_message.py} (99%) diff --git a/.gitignore b/.gitignore index 7f6151613..bed9d05fb 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,6 @@ dbs benchmarks benchmark_results_files.json uploaded_benchmarks +tests/script/core/serialization_database.json +tests/script/core/cache/ +tests/script/core/local/ diff --git a/tests/script/core/test_message.py b/tests/script/core/test_message.py index 96134e6fb..e3e75d424 100644 --- a/tests/script/core/test_message.py +++ b/tests/script/core/test_message.py @@ -1,72 +1,169 @@ +from pathlib import Path +from shutil import rmtree +from typing import Hashable, Optional, TextIO +from urllib.request import urlopen + import pytest from pydantic import ValidationError, HttpUrl, FilePath -from dff.script.core.message import Location, DataAttachment, Keyboard, Button - - -def test_location(): - loc1 = Location(longitude=-0.1, latitude=-0.1) - loc2 = Location(longitude=-0.09999, latitude=-0.09998) - loc3 = Location(longitude=-0.10002, latitude=-0.10001) - - assert loc1 == loc2 - assert loc3 == loc1 - assert loc2 != loc3 - - assert loc1 != 1 - - -@pytest.mark.parametrize( - "attachment1,attachment2,equal", - [ - ( - DataAttachment(source="https://github.com/mathiasbynens/small/raw/master/pdf.pdf", title="File"), - DataAttachment(source="https://raw.githubusercontent.com/mathiasbynens/small/master/pdf.pdf", title="File"), - True, - ), - ( - DataAttachment(source="https://github.com/mathiasbynens/small/raw/master/pdf.pdf", title="1"), - DataAttachment(source="https://raw.githubusercontent.com/mathiasbynens/small/master/pdf.pdf", title="2"), - False, - ), - ( - DataAttachment(source=__file__, title="File"), - DataAttachment(source=__file__, title="File"), - True, - ), - ( - DataAttachment(source=__file__, title="1"), - DataAttachment(source=__file__, title="2"), - False, - ), - ( - DataAttachment(id="1", title="File"), - DataAttachment(id="2", title="File"), - False, - ), - ], -) -def test_attachment(attachment1, attachment2, equal): - assert (attachment1 == attachment2) == equal - - -def test_missing_error(): - with pytest.raises(ValidationError) as e: - _ = DataAttachment(source=HttpUrl("http://google.com"), id="123") - assert e - - with pytest.raises(ValidationError) as e: - _ = DataAttachment(source=FilePath("/etc/missing_file")) - assert e - - -def test_empty_keyboard(): - with pytest.raises(ValidationError) as e: - _ = Keyboard(buttons=[]) - assert e - - -def test_long_button_data(): - with pytest.raises(ValidationError) as error: - Button(text="", data="test" * 64) - assert error +from dff.context_storages import DBContextStorage, JSONContextStorage +from dff.messengers.common.interface import MessengerInterface +from dff.messengers.console import CLIMessengerInterface +from dff.script.core.context import Context +from dff.script.core.message import Animation, Audio, CallbackQuery, Contact, Document, Image, Invoice, Location, DataAttachment, Message, Poll, PollOption, Sticker, Video + +EXAMPLE_SOURCE = "https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments" + + +class DFFCLIMessengerInterface(CLIMessengerInterface): + example_attachments_repo = "" + + def __init__(self, attachments_directory: Optional[Path] = None): + MessengerInterface.__init__(self, attachments_directory) + self._ctx_id: Optional[Hashable] = None + self._intro: Optional[str] = None + self._prompt_request: str = "request: " + self._prompt_response: str = "response: " + self._descriptor: Optional[TextIO] = None + + async def populate_attachment(self, attachment: DataAttachment) -> bytes: + if attachment.id is not None: + with urlopen(f"{EXAMPLE_SOURCE}/{attachment.id}") as url: + return url.read() + else: + raise ValueError(f"For attachment {attachment} id is not defined!") + + +class TestMessage: + @pytest.fixture + def json_context_storage(self) -> DBContextStorage: + return JSONContextStorage(str(Path(__file__).parent / "serialization_database.json")) + + def clear_and_create_dir(self, dir: Path) -> Path: + rmtree(dir, ignore_errors=True) + dir.mkdir(parents=True, exist_ok=True) + return dir + + def test_location(self): + loc1 = Location(longitude=-0.1, latitude=-0.1) + loc2 = Location(longitude=-0.09999, latitude=-0.09998) + loc3 = Location(longitude=-0.10002, latitude=-0.10001) + + assert loc1 == loc2 + assert loc3 == loc1 + assert loc2 != loc3 + + assert loc1 != 1 + + @pytest.mark.parametrize( + "attachment1,attachment2,equal", + [ + ( + DataAttachment(source="https://github.com/mathiasbynens/small/raw/master/pdf.pdf", title="File"), + DataAttachment(source="https://raw.githubusercontent.com/mathiasbynens/small/master/pdf.pdf", title="File"), + True, + ), + ( + DataAttachment(source="https://github.com/mathiasbynens/small/raw/master/pdf.pdf", title="1"), + DataAttachment(source="https://raw.githubusercontent.com/mathiasbynens/small/master/pdf.pdf", title="2"), + False, + ), + ( + DataAttachment(source=__file__, title="File"), + DataAttachment(source=__file__, title="File"), + True, + ), + ( + DataAttachment(source=__file__, title="1"), + DataAttachment(source=__file__, title="2"), + False, + ), + ( + DataAttachment(id="1", title="File"), + DataAttachment(id="2", title="File"), + False, + ), + ], + ) + def test_attachment_equal(self, attachment1: DataAttachment, attachment2: DataAttachment, equal: bool): + assert (attachment1 == attachment2) == equal + + @pytest.mark.parametrize( + "attachment", + [ + ( + CallbackQuery(query_string="some_callback_query_data"), + ), + ( + Location(longitude=53.055955, latitude=102.891407), + ), + ( + Contact(phone_number="8-900-555-35-35", first_name="Hope", last_name="Credit") + ), + ( + Invoice(title="Payment", description="No comment", currency="USD", amount=300) + ), + ( + Poll(question="Which?", options=[PollOption(text="1", votes=2), PollOption(text="2", votes=5)]) + ), + ( + Audio(source="https://github.com/deeppavlov/dialog_flow_framework/blob/example-attachments/separation-william-king.mp3") + ), + ( + Video(source="https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/crownfall-lags-nkognit0.mp4") + ), + ( + Animation(source="https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/hong-kong-simplyart4794.mp4") + ), + ( + Image(source="https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/deeppavlov.png") + ), + ( + Sticker(id="some_sticker_identifier") + ), + ( + Document(source="https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/deeppavlov-article.pdf") + ), + ], + ) + def test_attachment_serialize(self, json_context_storage: DBContextStorage, attachment: DataAttachment): + name = type(attachment).__name__ + json_context_storage[name] = Context(requests={0: Message(attachments=[attachment])}) + retrieved = json_context_storage[name].requests[0].attachments[0] + assert attachment == retrieved + + def test_getting_attachment_bytes(self): + root_dir = Path(__file__).parent + local_path = self.clear_and_create_dir(root_dir / "local") + local_document = local_path / "pre-saved-document.pdf" + cache_path = self.clear_and_create_dir(root_dir / "cache") + cache_document = cache_path / "pre-saved-document.pdf" + cli_iface = DFFCLIMessengerInterface(cache_path) + + document_name = "deeppavlov-article.pdf" + remote_document_url = f"{EXAMPLE_SOURCE}/{document_name}" + with urlopen(remote_document_url) as url: + document_bytes = url.read() + local_document.write_bytes(document_bytes) + cache_document.write_bytes(document_bytes) + + remote_document_att = Document(source=HttpUrl(remote_document_url)) + cached_document_att = Document(cached_filename=HttpUrl(remote_document_url)) + local_document_att = Document(source=FilePath(local_document)) + iface_document_att = Document(id=document_name) + + for document in (remote_document_att, cached_document_att, local_document_att, iface_document_att): + doc_bytes = document.get_bytes(cli_iface) + assert document_bytes, doc_bytes + if not isinstance(document.source, Path): + cached_bytes = document.cached_filename.read_bytes() + assert document_bytes, cached_bytes + + def test_missing_error(self): + with pytest.raises(ValidationError) as e: + _ = DataAttachment(source=HttpUrl("http://google.com"), id="123") + assert e + + with pytest.raises(ValidationError) as e: + _ = DataAttachment(source=FilePath("/etc/missing_file")) + assert e diff --git a/tutorials/messengers/telegram/3_advanced.py b/tutorials/messengers/telegram/3_advanced.py index 1ba3bf5a9..3cbea64f5 100644 --- a/tutorials/messengers/telegram/3_advanced.py +++ b/tutorials/messengers/telegram/3_advanced.py @@ -27,7 +27,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) from dff.pipeline import Pipeline from dff.script.core.context import Context from dff.script.core.keywords import GLOBAL -from dff.script.core.message import Document, Image, Location, Sticker +from dff.script.core.message import DataAttachment, Document, Image, Location, Sticker from dff.utils.testing.common import is_interactive_mode @@ -50,7 +50,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) #%% -image_url = HttpUrl("https://avatars.githubusercontent.com/u/29918795?s=200&v=4") +image_url = HttpUrl("https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/deeppavlov.png") formatted_text = r""" Here's your formatted text\! @@ -75,7 +75,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) document_thumbnail = asyncio.run(Image(source=image_url).get_bytes(None)) document_data = { - "source": HttpUrl("https://aclanthology.org/P18-4021.pdf"), + "source": HttpUrl("https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/deeppavlov-article.pdf"), "title": "DeepPavlov article", "filename": "deeppavlov_article.pdf", "thumbnail": document_thumbnail, @@ -96,6 +96,17 @@ def stringify_previous_request(ctx: Context, _: Pipeline) -> Message: return Message(formatted_request.format(dump), parse_mode=ParseMode.MARKDOWN_V2) +# %% +def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: + atch = [a for a in ctx.last_request.attachments if isinstance(a, DataAttachment)] + if len(atch) > 0: + atch_hash = hash(atch[0].get_bytes(pipe.messenger_interface)) + resp_format = r"Here's your previous request hash: `{}`\!\nRun /start command again to restart\." + return Message(resp_format.format(atch_hash, parse_mode=ParseMode.MARKDOWN_V2)) + else: + return Message("Last request did not contain any data attachment!\nRun /start command again to restart.") + + # %% script = { GLOBAL: { @@ -128,6 +139,9 @@ def stringify_previous_request(ctx: Context, _: Pipeline) -> Message: [ InlineKeyboardButton("Raw attachments!", callback_data="raw"), ], + [ + InlineKeyboardButton("Attachment bytes hash!", callback_data="hash"), + ], [ InlineKeyboardButton("Restart!", callback_data="restart"), InlineKeyboardButton("Quit!", callback_data="quit"), @@ -143,6 +157,7 @@ def stringify_previous_request(ctx: Context, _: Pipeline) -> Message: "secret_node": cnd.has_callback_query("secret"), "thumbnail_node": cnd.has_callback_query("thumbnail"), "raw_init_node": cnd.has_callback_query("raw"), + "hash_init_node": cnd.has_callback_query("hash"), "hmmm_node": cnd.has_callback_query("restart"), "fallback_node": cnd.has_callback_query("quit"), } @@ -178,6 +193,13 @@ def stringify_previous_request(ctx: Context, _: Pipeline) -> Message: "raw_request_node": { RESPONSE: stringify_previous_request, }, + "hash_init_node": { + RESPONSE: Message("Alright! Now send me a message with data attachment (audio, video, animation, image, sticker or document)!"), + TRANSITIONS: { "hash_request_node": cnd.true }, + }, + "hash_request_node": { + RESPONSE: hash_data_attachment_request, + }, "fallback_node": { RESPONSE: Message("Bot has entered unrecoverable state :/\nRun /start command again to restart."), }, diff --git a/tutorials/script/responses/2_buttons.py b/tutorials/script/responses/2_buttons.py deleted file mode 100644 index f12a73675..000000000 --- a/tutorials/script/responses/2_buttons.py +++ /dev/null @@ -1,259 +0,0 @@ -# %% [markdown] -""" -# Responses: 2. Buttons - -In this tutorial %mddoclink(api,script.core.message,Button) -class is demonstrated. -Buttons are one of %mddoclink(api,script.core.message,Message) fields. -They can be attached to any message but will only work if the chosen -[messenger interface](%doclink(api,index_messenger_interfaces)) supports them. -""" - - -# %pip install dff - -# %% -import dff.script.conditions as cnd -import dff.script.labels as lbl -from dff.script import Context, TRANSITIONS, RESPONSE - -from dff.script.core.message import Button, Keyboard, Message -from dff.pipeline import Pipeline -from dff.utils.testing import ( - check_happy_path, - is_interactive_mode, - run_interactive_mode, -) - - -# %% -def check_button_payload(value: str): - def payload_check_inner(ctx: Context, _: Pipeline): - if ctx.last_request.text is not None: - return ctx.last_request.text == value - else: - return False - - return payload_check_inner - - -# %% -toy_script = { - "root": { - "start": { - RESPONSE: Message(""), - TRANSITIONS: { - ("general", "question_1"): cnd.true(), - }, - }, - "fallback": {RESPONSE: Message("Finishing test")}, - }, - "general": { - "question_1": { - RESPONSE: Message( - **{ - "text": "Starting test! What's 2 + 2?" - " (type in the index of the correct option)", - "attachments": [ - Keyboard( - buttons=[ - [ - Button(text="5", data="5"), - Button(text="4", data="4"), - ] - ] - ), - ], - } - ), - TRANSITIONS: { - lbl.forward(): check_button_payload("4"), - ("general", "question_1"): check_button_payload("5"), - }, - }, - "question_2": { - RESPONSE: Message( - **{ - "text": "Next question: what's 6 * 8?" - " (type in the index of the correct option)", - "attachments": [ - Keyboard( - buttons=[ - [ - Button(text="38", data="38"), - Button(text="48", data="48"), - ] - ] - ), - ], - } - ), - TRANSITIONS: { - lbl.forward(): check_button_payload("48"), - ("general", "question_2"): check_button_payload("38"), - }, - }, - "question_3": { - RESPONSE: Message( - **{ - "text": "What's 114 + 115? " - "(type in the index of the correct option)", - "attachments": [ - Keyboard( - buttons=[ - [ - Button(text="229", data="229"), - Button(text="283", data="283"), - ] - ] - ), - ], - } - ), - TRANSITIONS: { - lbl.forward(): check_button_payload("229"), - ("general", "question_3"): check_button_payload("283"), - }, - }, - "success": { - RESPONSE: Message("Success!"), - TRANSITIONS: {("root", "fallback"): cnd.true()}, - }, - }, -} - -happy_path = ( - ( - Message("Hi"), - Message( - **{ - "text": "Starting test! What's 2 + 2? " - "(type in the index of the correct option)", - "attachments": [ - Keyboard( - buttons=[ - [ - Button(text="5", data="5"), - Button(text="4", data="4"), - ] - ] - ) - ], - } - ), - ), - ( - Message("5"), - Message( - **{ - "text": "Starting test! What's 2 + 2? " - "(type in the index of the correct option)", - "attachments": [ - Keyboard( - buttons=[ - [ - Button(text="5", data="5"), - Button(text="4", data="4"), - ] - ] - ), - ], - } - ), - ), - ( - Message("4"), - Message( - **{ - "text": "Next question: what's 6 * 8? " - "(type in the index of the correct option)", - "attachments": [ - Keyboard( - buttons=[ - [ - Button(text="38", data="38"), - Button(text="48", data="48"), - ] - ] - ), - ], - } - ), - ), - ( - Message("38"), - Message( - **{ - "text": "Next question: what's 6 * 8? " - "(type in the index of the correct option)", - "attachments": [ - Keyboard( - buttons=[ - [ - Button(text="38", data="38"), - Button(text="48", data="48"), - ] - ] - ), - ], - } - ), - ), - ( - Message("48"), - Message( - **{ - "text": "What's 114 + 115? " - "(type in the index of the correct option)", - "attachments": [ - Keyboard( - buttons=[ - [ - Button(text="229", data="229"), - Button(text="283", data="283"), - ] - ] - ), - ], - } - ), - ), - ( - Message("283"), - Message( - **{ - "text": "What's 114 + 115? " - "(type in the index of the correct option)", - "attachments": [ - Keyboard( - buttons=[ - [ - Button(text="229", data="229"), - Button(text="283", data="283"), - ] - ] - ), - ], - } - ), - ), - (Message("229"), Message("Success!")), - (Message("ok"), Message("Finishing test")), -) - - -# %% -pipeline = Pipeline.from_script( - toy_script, - start_label=("root", "start"), - fallback_label=("root", "fallback"), -) - -if __name__ == "__main__": - check_happy_path( - pipeline, - happy_path, - ) # For response object with `happy_path` string comparing, - # a special `generics_comparer` comparator is used - if is_interactive_mode(): - run_interactive_mode(pipeline) diff --git a/tutorials/script/responses/3_media.py b/tutorials/script/responses/2_media.py similarity index 99% rename from tutorials/script/responses/3_media.py rename to tutorials/script/responses/2_media.py index 3072c7587..0179a35ef 100644 --- a/tutorials/script/responses/3_media.py +++ b/tutorials/script/responses/2_media.py @@ -1,6 +1,6 @@ # %% [markdown] """ -# Responses: 3. Media +# Responses: 2. Media Here, %mddoclink(api,script.core.message,Attachment) class is shown. Attachments can be used for attaching different media elements diff --git a/tutorials/script/responses/4_multi_message.py b/tutorials/script/responses/3_multi_message.py similarity index 99% rename from tutorials/script/responses/4_multi_message.py rename to tutorials/script/responses/3_multi_message.py index 323ccffec..e8642f276 100644 --- a/tutorials/script/responses/4_multi_message.py +++ b/tutorials/script/responses/3_multi_message.py @@ -1,6 +1,6 @@ # %% [markdown] """ -# Responses: 4. Multi Message +# Responses: 3. Multi Message This tutorial shows how to store several messages inside a single one. This might be useful if you want DFF Pipeline to send `response` candidates From f1a140adb11f668bc680780d883ba3be6d026ab8 Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 7 May 2024 23:13:14 +0200 Subject: [PATCH 059/140] parametrize fixed for telegram testing --- tests/messengers/telegram/test_tutorials.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/messengers/telegram/test_tutorials.py b/tests/messengers/telegram/test_tutorials.py index 17d20074a..137df360b 100644 --- a/tests/messengers/telegram/test_tutorials.py +++ b/tests/messengers/telegram/test_tutorials.py @@ -11,7 +11,7 @@ @pytest.mark.asyncio @pytest.mark.parametrize( ["tutorial_module_name"], - [("1_basic"), ("2_attachments"), ("3_advanced")], + [("1_basic",), ("2_attachments",), ("3_advanced",)], ) async def test_tutorials_memory(tutorial_module_name: str): module = import_module(f"tutorials.{dot_path_to_addon}.{tutorial_module_name}") From 3b04c0364453b117f19edfda8cf2c2bd5934b41d Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 7 May 2024 23:26:13 +0200 Subject: [PATCH 060/140] Some lint fixed --- dff/messengers/common/interface.py | 3 - dff/messengers/telegram/interface.py | 8 +- tests/messengers/telegram/test_tutorials.py | 2 +- tests/messengers/telegram/utils.py | 8 +- tests/script/core/test_message.py | 69 +++++++------- .../messengers/telegram/2_attachments.py | 54 ++++++++--- tutorials/messengers/telegram/3_advanced.py | 95 ++++++++++++++----- 7 files changed, 156 insertions(+), 83 deletions(-) diff --git a/dff/messengers/common/interface.py b/dff/messengers/common/interface.py index 01ec650ba..e7255a416 100644 --- a/dff/messengers/common/interface.py +++ b/dff/messengers/common/interface.py @@ -134,9 +134,6 @@ async def connect( self._on_exception(e) break - async def populate_attachment(self, attachment: DataAttachment) -> bytes: - raise RuntimeError(f"Plain pollin") - class CallbackMessengerInterface(MessengerInterface): """ diff --git a/dff/messengers/telegram/interface.py b/dff/messengers/telegram/interface.py index 6234956b4..af294a62c 100644 --- a/dff/messengers/telegram/interface.py +++ b/dff/messengers/telegram/interface.py @@ -8,7 +8,9 @@ class PollingTelegramInterface(_AbstractTelegramInterface): # pragma: no cover - def __init__(self, token: str, attachments_directory: Optional[Path] = None, interval: int = 2, timeout: int = 20) -> None: + def __init__( + self, token: str, attachments_directory: Optional[Path] = None, interval: int = 2, timeout: int = 20 + ) -> None: super().__init__(token, attachments_directory) self.interval = interval self.timeout = timeout @@ -21,7 +23,9 @@ async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs class CallbackTelegramInterface(_AbstractTelegramInterface): # pragma: no cover - def __init__(self, token: str, attachments_directory: Optional[Path] = None, host: str = "localhost", port: int = 844): + def __init__( + self, token: str, attachments_directory: Optional[Path] = None, host: str = "localhost", port: int = 844 + ): super().__init__(token, attachments_directory) self.listen = host self.port = port diff --git a/tests/messengers/telegram/test_tutorials.py b/tests/messengers/telegram/test_tutorials.py index 137df360b..a8bb2ff43 100644 --- a/tests/messengers/telegram/test_tutorials.py +++ b/tests/messengers/telegram/test_tutorials.py @@ -5,7 +5,7 @@ from tests.test_utils import get_path_from_tests_to_current_dir dot_path_to_addon = get_path_from_tests_to_current_dir(__file__) -happy_paths_file = Path(__file__).parent / "test_happy_paths.json" +happy_paths_file = Path(__file__).parent / "test_happy_paths.json" @pytest.mark.asyncio diff --git a/tests/messengers/telegram/utils.py b/tests/messengers/telegram/utils.py index b087cd1b3..257b5db25 100644 --- a/tests/messengers/telegram/utils.py +++ b/tests/messengers/telegram/utils.py @@ -18,7 +18,7 @@ class MockBot(BaseModel): async def get_file(self, file_id: str) -> File: return await self._original_bot.get_file(file_id) - + def __getattribute__(self, name: str) -> Any: def set_trace(*args, **kwargs): self._latest_trace = (name, args, kwargs) @@ -42,7 +42,9 @@ def create(cls, interface: _AbstractTelegramInterface, happy_path: List[PathStep interface.pipeline_runner = instance._wrapped_pipeline_runner return instance - async def _wrapped_pipeline_runner(self, message: Message, ctx_id: Optional[Hashable] = None, update_ctx_misc: Optional[dict] = None) -> Context: + async def _wrapped_pipeline_runner( + self, message: Message, ctx_id: Optional[Hashable] = None, update_ctx_misc: Optional[dict] = None + ) -> Context: self._latest_ctx = await self._interface.pipeline_runner(message, ctx_id, update_ctx_misc) return self._latest_ctx @@ -55,7 +57,7 @@ def _run_bot(self) -> None: else: update = Update() loop.run_until_complete(self._interface.on_callback(update, None)) # type: ignore - assert self._latest_ctx is not None, "During pipeline runner execution, no context was produced!" + assert self._latest_ctx is not None, "During pipeline runner execution, no context was produced!" assert self._latest_ctx.last_request == received, "Expected request message is not equal to expected!" assert self._latest_ctx.last_response == response, "Expected response message is not equal to expected!" assert self.mock_bot._latest_trace == trace, "Expected trace is not equal to expected!" diff --git a/tests/script/core/test_message.py b/tests/script/core/test_message.py index e3e75d424..1fd5c3100 100644 --- a/tests/script/core/test_message.py +++ b/tests/script/core/test_message.py @@ -10,7 +10,22 @@ from dff.messengers.common.interface import MessengerInterface from dff.messengers.console import CLIMessengerInterface from dff.script.core.context import Context -from dff.script.core.message import Animation, Audio, CallbackQuery, Contact, Document, Image, Invoice, Location, DataAttachment, Message, Poll, PollOption, Sticker, Video +from dff.script.core.message import ( + Animation, + Audio, + CallbackQuery, + Contact, + Document, + Image, + Invoice, + Location, + DataAttachment, + Message, + Poll, + PollOption, + Sticker, + Video, +) EXAMPLE_SOURCE = "https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments" @@ -60,12 +75,16 @@ def test_location(self): [ ( DataAttachment(source="https://github.com/mathiasbynens/small/raw/master/pdf.pdf", title="File"), - DataAttachment(source="https://raw.githubusercontent.com/mathiasbynens/small/master/pdf.pdf", title="File"), + DataAttachment( + source="https://raw.githubusercontent.com/mathiasbynens/small/master/pdf.pdf", title="File" + ), True, ), ( DataAttachment(source="https://github.com/mathiasbynens/small/raw/master/pdf.pdf", title="1"), - DataAttachment(source="https://raw.githubusercontent.com/mathiasbynens/small/master/pdf.pdf", title="2"), + DataAttachment( + source="https://raw.githubusercontent.com/mathiasbynens/small/master/pdf.pdf", title="2" + ), False, ), ( @@ -91,39 +110,17 @@ def test_attachment_equal(self, attachment1: DataAttachment, attachment2: DataAt @pytest.mark.parametrize( "attachment", [ - ( - CallbackQuery(query_string="some_callback_query_data"), - ), - ( - Location(longitude=53.055955, latitude=102.891407), - ), - ( - Contact(phone_number="8-900-555-35-35", first_name="Hope", last_name="Credit") - ), - ( - Invoice(title="Payment", description="No comment", currency="USD", amount=300) - ), - ( - Poll(question="Which?", options=[PollOption(text="1", votes=2), PollOption(text="2", votes=5)]) - ), - ( - Audio(source="https://github.com/deeppavlov/dialog_flow_framework/blob/example-attachments/separation-william-king.mp3") - ), - ( - Video(source="https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/crownfall-lags-nkognit0.mp4") - ), - ( - Animation(source="https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/hong-kong-simplyart4794.mp4") - ), - ( - Image(source="https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/deeppavlov.png") - ), - ( - Sticker(id="some_sticker_identifier") - ), - ( - Document(source="https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/deeppavlov-article.pdf") - ), + (CallbackQuery(query_string="some_callback_query_data"),), + (Location(longitude=53.055955, latitude=102.891407),), + (Contact(phone_number="8-900-555-35-35", first_name="Hope", last_name="Credit")), + (Invoice(title="Payment", description="No comment", currency="USD", amount=300)), + (Poll(question="Which?", options=[PollOption(text="1", votes=2), PollOption(text="2", votes=5)])), + (Audio(source="https://example.com/some_audio.mp3")), + (Video(source="https://example.com/some_video.mp4")), + (Animation(source="https://example.com/some_animation.gif")), + (Image(source="https://example.com/some_image.png")), + (Sticker(id="some_sticker_identifier")), + (Document(source="https://example.com/some_document.pdf")), ], ) def test_attachment_serialize(self, json_context_storage: DBContextStorage, attachment: DataAttachment): diff --git a/tutorials/messengers/telegram/2_attachments.py b/tutorials/messengers/telegram/2_attachments.py index a2bb077bd..dd5a38349 100644 --- a/tutorials/messengers/telegram/2_attachments.py +++ b/tutorials/messengers/telegram/2_attachments.py @@ -23,22 +23,40 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) from dff.script import GLOBAL, RESPONSE, TRANSITIONS, Message from dff.messengers.telegram import PollingTelegramInterface from dff.pipeline import Pipeline -from dff.script.core.message import Animation, Audio, Contact, Document, Location, Image, Poll, PollOption, Sticker, Video +from dff.script.core.message import ( + Animation, + Audio, + Contact, + Document, + Location, + Image, + Poll, + PollOption, + Sticker, + Video, +) from dff.utils.testing.common import is_interactive_mode # %% [markdown] """ Example attachment data is specified below in form of dictionaries. -List of attachments that telegram messenger interface can send can be found here: +List of attachments that telegram messenger interface can send can +be found here: %mddoclink(api,messengers.telegram.abstract,_AbstractTelegramInterface#response_attachments). """ # %% +EXAMPLE_ATTACHMENT_SOURCE = "https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments" + location_data = {"latitude": 50.65, "longitude": 3.916667} -contact_data = {"phone_number": "8-900-555-35-35", "first_name": "Hope", "last_name": "Credit"} +contact_data = { + "phone_number": "8-900-555-35-35", + "first_name": "Hope", + "last_name": "Credit", +} sticker_data = { "id": "CAACAgIAAxkBAAErAAFXZibO5ksphCKSXSe1CYiw5588yqsAAkEAAzyKVxogmx2BPCogYDQE", @@ -46,29 +64,35 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) } audio_data = { - "source": HttpUrl("https://github.com/deeppavlov/dialog_flow_framework/blob/example-attachments/separation-william-king.mp3"), + "source": HttpUrl( + f"{EXAMPLE_ATTACHMENT_SOURCE}/separation-william-king.mp3" + ), "title": '"Separation" melody by William King', } video_data = { - "source": HttpUrl("https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/crownfall-lags-nkognit0.mp4"), + "source": HttpUrl( + f"{EXAMPLE_ATTACHMENT_SOURCE}/crownfall-lags-nkognit0.mp4" + ), "title": "Epic Dota2 gameplay by Nkognit0", } animation_data = { # For some reason, if we don't define filename explicitly, animation is sent as file. - "source": HttpUrl("https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/hong-kong-simplyart4794.mp4"), + "source": HttpUrl( + f"{EXAMPLE_ATTACHMENT_SOURCE}/hong-kong-simplyart4794.mp4" + ), "title": "Hong Kong skyscraper views by Simplyart4794", "filename": "hk.mp4", } image_data = { - "source": HttpUrl("https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/deeppavlov.png"), + "source": HttpUrl(f"{EXAMPLE_ATTACHMENT_SOURCE}/deeppavlov.png"), "title": "DeepPavlov logo", } document_data = { - "source": HttpUrl("https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/deeppavlov-article.pdf"), + "source": HttpUrl(f"{EXAMPLE_ATTACHMENT_SOURCE}/deeppavlov-article.pdf"), "title": "DeepPavlov article", } @@ -82,15 +106,21 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) script = { GLOBAL: { TRANSITIONS: { - ("main_flow", "location_node"): cnd.exact_match(Message("location")), + ("main_flow", "location_node"): cnd.exact_match( + Message("location") + ), ("main_flow", "contact_node"): cnd.exact_match(Message("contact")), ("main_flow", "poll_node"): cnd.exact_match(Message("poll")), ("main_flow", "sticker_node"): cnd.exact_match(Message("sticker")), ("main_flow", "audio_node"): cnd.exact_match(Message("audio")), ("main_flow", "video_node"): cnd.exact_match(Message("video")), - ("main_flow", "animation_node"): cnd.exact_match(Message("animation")), + ("main_flow", "animation_node"): cnd.exact_match( + Message("animation") + ), ("main_flow", "image_node"): cnd.exact_match(Message("image")), - ("main_flow", "document_node"): cnd.exact_match(Message("document")), + ("main_flow", "document_node"): cnd.exact_match( + Message("document") + ), } }, "main_flow": { @@ -174,7 +204,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) '"video", "animation", "image" and "document".' ), }, - } + }, } diff --git a/tutorials/messengers/telegram/3_advanced.py b/tutorials/messengers/telegram/3_advanced.py index 3cbea64f5..486272312 100644 --- a/tutorials/messengers/telegram/3_advanced.py +++ b/tutorials/messengers/telegram/3_advanced.py @@ -27,7 +27,13 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) from dff.pipeline import Pipeline from dff.script.core.context import Context from dff.script.core.keywords import GLOBAL -from dff.script.core.message import DataAttachment, Document, Image, Location, Sticker +from dff.script.core.message import ( + DataAttachment, + Document, + Image, + Location, + Sticker, +) from dff.utils.testing.common import is_interactive_mode @@ -48,14 +54,16 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) in terms and datastructures of Dialog Flow Framework. """ -#%% +# %% + +EXAMPLE_ATTACHMENT_SOURCE = "https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments" -image_url = HttpUrl("https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/deeppavlov.png") +image_url = HttpUrl(f"{EXAMPLE_ATTACHMENT_SOURCE}/deeppavlov.png") formatted_text = r""" -Here's your formatted text\! -You can see **text in bold** and _text in italic_\. -\> Here's a [link](https://github.com/deeppavlov/dialog_flow_framework) in a quote\. +Here's your formatted text\! +You can see **text in bold** and _text in italic_\. +\> Here's a [link](https://github.com/deeppavlov/dialog_flow_framework) in a quote\. Run /start command again to restart\. """ @@ -75,7 +83,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) document_thumbnail = asyncio.run(Image(source=image_url).get_bytes(None)) document_data = { - "source": HttpUrl("https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/deeppavlov-article.pdf"), + "source": HttpUrl(f"{EXAMPLE_ATTACHMENT_SOURCE}/deeppavlov-article.pdf"), "title": "DeepPavlov article", "filename": "deeppavlov_article.pdf", "thumbnail": document_thumbnail, @@ -91,20 +99,29 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) Run /start command again to restart\. """ + def stringify_previous_request(ctx: Context, _: Pipeline) -> Message: dump = ctx.last_request.model_dump_json(indent=4) - return Message(formatted_request.format(dump), parse_mode=ParseMode.MARKDOWN_V2) + return Message( + formatted_request.format(dump), parse_mode=ParseMode.MARKDOWN_V2 + ) # %% def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: - atch = [a for a in ctx.last_request.attachments if isinstance(a, DataAttachment)] + atch = [ + a for a in ctx.last_request.attachments if isinstance(a, DataAttachment) + ] if len(atch) > 0: atch_hash = hash(atch[0].get_bytes(pipe.messenger_interface)) resp_format = r"Here's your previous request hash: `{}`\!\nRun /start command again to restart\." - return Message(resp_format.format(atch_hash, parse_mode=ParseMode.MARKDOWN_V2)) + return Message( + resp_format.format(atch_hash, parse_mode=ParseMode.MARKDOWN_V2) + ) else: - return Message("Last request did not contain any data attachment!\nRun /start command again to restart.") + return Message( + "Last request did not contain any data attachment!\nRun /start command again to restart." + ) # %% @@ -125,26 +142,46 @@ def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: reply_markup=InlineKeyboardMarkup( [ [ - InlineKeyboardButton("Cute formatted text!", callback_data="formatted"), + InlineKeyboardButton( + "Cute formatted text!", + callback_data="formatted", + ), ], [ - InlineKeyboardButton("Multiple attachments!", callback_data="attachments"), + InlineKeyboardButton( + "Multiple attachments!", + callback_data="attachments", + ), ], [ - InlineKeyboardButton("Secret image!", callback_data="secret"), + InlineKeyboardButton( + "Secret image!", callback_data="secret" + ), ], [ - InlineKeyboardButton("Document with thumbnail!", callback_data="thumbnail"), + InlineKeyboardButton( + "Document with thumbnail!", + callback_data="thumbnail", + ), ], [ - InlineKeyboardButton("Raw attachments!", callback_data="raw"), + InlineKeyboardButton( + "Raw attachments!", callback_data="raw" + ), ], [ - InlineKeyboardButton("Attachment bytes hash!", callback_data="hash"), + InlineKeyboardButton( + "Attachment bytes hash!", + callback_data="hash", + ), ], [ - InlineKeyboardButton("Restart!", callback_data="restart"), - InlineKeyboardButton("Quit!", callback_data="quit"), + InlineKeyboardButton( + "Restart!", callback_data="restart" + ), + InlineKeyboardButton( + "Quit!", callback_data="quit" + ), ], ], ), @@ -160,7 +197,7 @@ def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: "hash_init_node": cnd.has_callback_query("hash"), "hmmm_node": cnd.has_callback_query("restart"), "fallback_node": cnd.has_callback_query("quit"), - } + }, }, "formatted_node": { RESPONSE: Message(formatted_text, parse_mode=ParseMode.MARKDOWN_V2), @@ -187,23 +224,29 @@ def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: ), }, "raw_init_node": { - RESPONSE: Message("Alright! Now send me any message and I'll send you it's raw data!"), - TRANSITIONS: { "raw_request_node": cnd.true }, + RESPONSE: Message( + "Alright! Now send me any message and I'll send you it's raw data!" + ), + TRANSITIONS: {"raw_request_node": cnd.true}, }, "raw_request_node": { RESPONSE: stringify_previous_request, }, "hash_init_node": { - RESPONSE: Message("Alright! Now send me a message with data attachment (audio, video, animation, image, sticker or document)!"), - TRANSITIONS: { "hash_request_node": cnd.true }, + RESPONSE: Message( + "Alright! Now send me a message with data attachment (audio, video, animation, image, sticker or document)!" + ), + TRANSITIONS: {"hash_request_node": cnd.true}, }, "hash_request_node": { RESPONSE: hash_data_attachment_request, }, "fallback_node": { - RESPONSE: Message("Bot has entered unrecoverable state :/\nRun /start command again to restart."), + RESPONSE: Message( + "Bot has entered unrecoverable state :/\nRun /start command again to restart." + ), }, - } + }, } From 4db3d6f9d1275ed999a257f877a6ab442b3192d4 Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 7 May 2024 23:44:27 +0200 Subject: [PATCH 061/140] file path fixed --- tests/script/core/test_message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/script/core/test_message.py b/tests/script/core/test_message.py index 1fd5c3100..b5a3c29c8 100644 --- a/tests/script/core/test_message.py +++ b/tests/script/core/test_message.py @@ -52,7 +52,7 @@ async def populate_attachment(self, attachment: DataAttachment) -> bytes: class TestMessage: @pytest.fixture def json_context_storage(self) -> DBContextStorage: - return JSONContextStorage(str(Path(__file__).parent / "serialization_database.json")) + return JSONContextStorage(f"file://{Path(__file__).parent / 'serialization_database.json'}") def clear_and_create_dir(self, dir: Path) -> Path: rmtree(dir, ignore_errors=True) From 74a0402f78aae7aeeb0f4b0cccaeff1cf7c398dd Mon Sep 17 00:00:00 2001 From: pseusys Date: Wed, 8 May 2024 20:54:03 +0200 Subject: [PATCH 062/140] attachments type fixed --- dff/script/core/message.py | 2 +- tests/script/core/test_message.py | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/dff/script/core/message.py b/dff/script/core/message.py index 8abb05fa2..2bc0e8c80 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -202,7 +202,7 @@ class level variables to store message information. text: Optional[str] = None commands: Optional[List[Command]] = None - attachments: Optional[List[Attachment]] = None + attachments: Optional[List[Union[CallbackQuery, Location, Contact, Invoice, Poll, Audio, Video, Animation, Image, Sticker, Document]]] = None annotations: Optional[dict] = None misc: Optional[dict] = None original_message: Optional[Any] = None diff --git a/tests/script/core/test_message.py b/tests/script/core/test_message.py index b5a3c29c8..d03b625b7 100644 --- a/tests/script/core/test_message.py +++ b/tests/script/core/test_message.py @@ -110,17 +110,17 @@ def test_attachment_equal(self, attachment1: DataAttachment, attachment2: DataAt @pytest.mark.parametrize( "attachment", [ - (CallbackQuery(query_string="some_callback_query_data"),), - (Location(longitude=53.055955, latitude=102.891407),), - (Contact(phone_number="8-900-555-35-35", first_name="Hope", last_name="Credit")), - (Invoice(title="Payment", description="No comment", currency="USD", amount=300)), - (Poll(question="Which?", options=[PollOption(text="1", votes=2), PollOption(text="2", votes=5)])), - (Audio(source="https://example.com/some_audio.mp3")), - (Video(source="https://example.com/some_video.mp4")), - (Animation(source="https://example.com/some_animation.gif")), - (Image(source="https://example.com/some_image.png")), - (Sticker(id="some_sticker_identifier")), - (Document(source="https://example.com/some_document.pdf")), + CallbackQuery(query_string="some_callback_query_data"), + Location(longitude=53.055955, latitude=102.891407), + Contact(phone_number="8-900-555-35-35", first_name="Hope", last_name="Credit"), + Invoice(title="Payment", description="No comment", currency="USD", amount=300), + Poll(question="Which?", options=[PollOption(text="1", votes=2), PollOption(text="2", votes=5)]), + Audio(source="https://example.com/some_audio.mp3"), + Video(source="https://example.com/some_video.mp4"), + Animation(source="https://example.com/some_animation.gif"), + Image(source="https://example.com/some_image.png"), + Sticker(id="some_sticker_identifier"), + Document(source="https://example.com/some_document.pdf"), ], ) def test_attachment_serialize(self, json_context_storage: DBContextStorage, attachment: DataAttachment): From 9bc4b3c35eb53702b9e8909e77139876e16ac8d2 Mon Sep 17 00:00:00 2001 From: pseusys Date: Wed, 8 May 2024 20:57:13 +0200 Subject: [PATCH 063/140] supported attachment sets renamed --- dff/messengers/common/interface.py | 4 ++-- dff/messengers/console.py | 4 ++-- dff/messengers/telegram/abstract.py | 4 ++-- tests/script/core/test_message.py | 2 -- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/dff/messengers/common/interface.py b/dff/messengers/common/interface.py index e7255a416..c1646ec39 100644 --- a/dff/messengers/common/interface.py +++ b/dff/messengers/common/interface.py @@ -28,8 +28,8 @@ class MessengerInterface(abc.ABC): It is responsible for connection between user and pipeline, as well as for request-response transactions. """ - request_attachments = set() - response_attachments = set() + supported_request_attachment_types = set() + supported_response_attachment_types = set() def __init__(self, attachments_directory: Optional[Path] = None) -> None: tempdir = gettempdir() diff --git a/dff/messengers/console.py b/dff/messengers/console.py index 6a9dfcb64..12d3ee669 100644 --- a/dff/messengers/console.py +++ b/dff/messengers/console.py @@ -12,8 +12,8 @@ class CLIMessengerInterface(PollingMessengerInterface): This message interface can maintain dialog with one user at a time only. """ - request_attachments = set() - response_attachments = set() + supported_request_attachment_types = set() + supported_response_attachment_types = set() def __init__( self, diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index 3033f7f10..75ad0431f 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -46,8 +46,8 @@ class _AbstractTelegramInterface(MessengerInterface): # pragma: no cover - request_attachments = {Location, Contact, Poll, Sticker, Audio, Video, Animation, Image, Document, Invoice} - response_attachments = {Location, Contact, Poll, Sticker, Audio, Video, Animation, Image, Document} + supported_request_attachment_types = {Location, Contact, Poll, Sticker, Audio, Video, Animation, Image, Document, Invoice} + supported_response_attachment_types = {Location, Contact, Poll, Sticker, Audio, Video, Animation, Image, Document} def __init__(self, token: str, attachments_directory: Optional[Path] = None) -> None: super().__init__(attachments_directory) diff --git a/tests/script/core/test_message.py b/tests/script/core/test_message.py index d03b625b7..0f79103d4 100644 --- a/tests/script/core/test_message.py +++ b/tests/script/core/test_message.py @@ -31,8 +31,6 @@ class DFFCLIMessengerInterface(CLIMessengerInterface): - example_attachments_repo = "" - def __init__(self, attachments_directory: Optional[Path] = None): MessengerInterface.__init__(self, attachments_directory) self._ctx_id: Optional[Hashable] = None From cb28fe3849701685fbdff83bc8e2d8fcbea0d75d Mon Sep 17 00:00:00 2001 From: pseusys Date: Wed, 8 May 2024 21:09:10 +0200 Subject: [PATCH 064/140] final message test fixed --- tests/script/core/test_message.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/tests/script/core/test_message.py b/tests/script/core/test_message.py index 0f79103d4..12e9cf3c2 100644 --- a/tests/script/core/test_message.py +++ b/tests/script/core/test_message.py @@ -52,11 +52,6 @@ class TestMessage: def json_context_storage(self) -> DBContextStorage: return JSONContextStorage(f"file://{Path(__file__).parent / 'serialization_database.json'}") - def clear_and_create_dir(self, dir: Path) -> Path: - rmtree(dir, ignore_errors=True) - dir.mkdir(parents=True, exist_ok=True) - return dir - def test_location(self): loc1 = Location(longitude=-0.1, latitude=-0.1) loc2 = Location(longitude=-0.09999, latitude=-0.09998) @@ -130,24 +125,23 @@ def test_attachment_serialize(self, json_context_storage: DBContextStorage, atta def test_getting_attachment_bytes(self): root_dir = Path(__file__).parent local_path = self.clear_and_create_dir(root_dir / "local") + rmtree(local_path, ignore_errors=True) + local_path.mkdir() + local_document = local_path / "pre-saved-document.pdf" - cache_path = self.clear_and_create_dir(root_dir / "cache") - cache_document = cache_path / "pre-saved-document.pdf" - cli_iface = DFFCLIMessengerInterface(cache_path) + cli_iface = DFFCLIMessengerInterface(root_dir / "cache") document_name = "deeppavlov-article.pdf" remote_document_url = f"{EXAMPLE_SOURCE}/{document_name}" with urlopen(remote_document_url) as url: document_bytes = url.read() local_document.write_bytes(document_bytes) - cache_document.write_bytes(document_bytes) remote_document_att = Document(source=HttpUrl(remote_document_url)) - cached_document_att = Document(cached_filename=HttpUrl(remote_document_url)) local_document_att = Document(source=FilePath(local_document)) iface_document_att = Document(id=document_name) - for document in (remote_document_att, cached_document_att, local_document_att, iface_document_att): + for document in (remote_document_att, local_document_att, iface_document_att): doc_bytes = document.get_bytes(cli_iface) assert document_bytes, doc_bytes if not isinstance(document.source, Path): From d30358b332bbb84b11e1d1667c9f135d9db14fa4 Mon Sep 17 00:00:00 2001 From: pseusys Date: Wed, 8 May 2024 21:12:48 +0200 Subject: [PATCH 065/140] dot path used instead of slash --- tests/messengers/telegram/test_tutorials.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/messengers/telegram/test_tutorials.py b/tests/messengers/telegram/test_tutorials.py index a8bb2ff43..9448ba9ca 100644 --- a/tests/messengers/telegram/test_tutorials.py +++ b/tests/messengers/telegram/test_tutorials.py @@ -4,7 +4,7 @@ from tests.test_utils import get_path_from_tests_to_current_dir -dot_path_to_addon = get_path_from_tests_to_current_dir(__file__) +dot_path_to_addon = get_path_from_tests_to_current_dir(__file__, separator=".") happy_paths_file = Path(__file__).parent / "test_happy_paths.json" From eaad6d26bebae6aef54e2e8eb7b34ee8ab89970e Mon Sep 17 00:00:00 2001 From: pseusys Date: Wed, 8 May 2024 21:34:34 +0200 Subject: [PATCH 066/140] tutorial tests fixed --- tests/messengers/telegram/test_tutorials.py | 5 ++++- tests/messengers/telegram/utils.py | 2 +- tests/script/core/test_message.py | 2 +- tutorials/messengers/telegram/1_basic.py | 6 +----- tutorials/messengers/telegram/2_attachments.py | 6 +----- tutorials/messengers/telegram/3_advanced.py | 12 +++--------- 6 files changed, 11 insertions(+), 22 deletions(-) diff --git a/tests/messengers/telegram/test_tutorials.py b/tests/messengers/telegram/test_tutorials.py index 9448ba9ca..d497e25f0 100644 --- a/tests/messengers/telegram/test_tutorials.py +++ b/tests/messengers/telegram/test_tutorials.py @@ -1,7 +1,9 @@ from importlib import import_module from pathlib import Path + import pytest +from tests.messengers.telegram.utils import MockApplication from tests.test_utils import get_path_from_tests_to_current_dir dot_path_to_addon = get_path_from_tests_to_current_dir(__file__, separator=".") @@ -15,4 +17,5 @@ ) async def test_tutorials_memory(tutorial_module_name: str): module = import_module(f"tutorials.{dot_path_to_addon}.{tutorial_module_name}") - module.interface.application + module.interface.application = MockApplication.create(module.interface, list()) + module.pipeline.run() diff --git a/tests/messengers/telegram/utils.py b/tests/messengers/telegram/utils.py index 257b5db25..0e8aacd3f 100644 --- a/tests/messengers/telegram/utils.py +++ b/tests/messengers/telegram/utils.py @@ -1,7 +1,7 @@ from asyncio import get_event_loop from typing import Any, Dict, Hashable, List, Optional, Tuple, TypeAlias -from pydantic import BaseModel +from pydantic import BaseModel from telegram import File, Update from telegram.ext import ExtBot diff --git a/tests/script/core/test_message.py b/tests/script/core/test_message.py index 12e9cf3c2..2b160c186 100644 --- a/tests/script/core/test_message.py +++ b/tests/script/core/test_message.py @@ -124,7 +124,7 @@ def test_attachment_serialize(self, json_context_storage: DBContextStorage, atta def test_getting_attachment_bytes(self): root_dir = Path(__file__).parent - local_path = self.clear_and_create_dir(root_dir / "local") + local_path = root_dir / "local" rmtree(local_path, ignore_errors=True) local_path.mkdir() diff --git a/tutorials/messengers/telegram/1_basic.py b/tutorials/messengers/telegram/1_basic.py index 5d557c604..6b7ac7c73 100644 --- a/tutorials/messengers/telegram/1_basic.py +++ b/tutorials/messengers/telegram/1_basic.py @@ -72,10 +72,6 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) ) -def main(): - pipeline.run() - - if __name__ == "__main__" and is_interactive_mode(): # prevent run during doc building - main() + pipeline.run() diff --git a/tutorials/messengers/telegram/2_attachments.py b/tutorials/messengers/telegram/2_attachments.py index dd5a38349..b8604b3f9 100644 --- a/tutorials/messengers/telegram/2_attachments.py +++ b/tutorials/messengers/telegram/2_attachments.py @@ -222,10 +222,6 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) ) -def main(): - pipeline.run() - - if __name__ == "__main__" and is_interactive_mode(): # prevent run during doc building - main() + pipeline.run() diff --git a/tutorials/messengers/telegram/3_advanced.py b/tutorials/messengers/telegram/3_advanced.py index 486272312..f372f019e 100644 --- a/tutorials/messengers/telegram/3_advanced.py +++ b/tutorials/messengers/telegram/3_advanced.py @@ -14,8 +14,8 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) # %pip install dff[telegram] # %% -import asyncio import os +from urllib.request import urlopen from pydantic import HttpUrl from telegram import InlineKeyboardButton, InlineKeyboardMarkup @@ -80,13 +80,11 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) "filename": "deeppavlov_logo.png", } -document_thumbnail = asyncio.run(Image(source=image_url).get_bytes(None)) - document_data = { "source": HttpUrl(f"{EXAMPLE_ATTACHMENT_SOURCE}/deeppavlov-article.pdf"), "title": "DeepPavlov article", "filename": "deeppavlov_article.pdf", - "thumbnail": document_thumbnail, + "thumbnail": urlopen(image_url).read(), } @@ -264,10 +262,6 @@ def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: ) -def main(): - pipeline.run() - - if __name__ == "__main__" and is_interactive_mode(): # prevent run during doc building - main() + pipeline.run() From c77f41f3147afefd8ca96a2a44aa7120d7965081 Mon Sep 17 00:00:00 2001 From: pseusys Date: Wed, 8 May 2024 22:00:52 +0200 Subject: [PATCH 067/140] fixed getattribute stack overflow exception --- tests/messengers/telegram/utils.py | 12 ++++++------ tutorials/messengers/telegram/3_advanced.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/messengers/telegram/utils.py b/tests/messengers/telegram/utils.py index 0e8aacd3f..f6fc2a1a6 100644 --- a/tests/messengers/telegram/utils.py +++ b/tests/messengers/telegram/utils.py @@ -23,14 +23,14 @@ def __getattribute__(self, name: str) -> Any: def set_trace(*args, **kwargs): self._latest_trace = (name, args, kwargs) - if hasattr(self, name): - return super().__getattribute__(name) - else: + try: + return object.__getattribute__(self, name) + except AttributeError: return set_trace class MockApplication(BaseModel): - mock_bot: MockBot + bot: MockBot happy_path: List[PathStep] _interface: _AbstractTelegramInterface _latest_ctx: Optional[Context] = None @@ -60,8 +60,8 @@ def _run_bot(self) -> None: assert self._latest_ctx is not None, "During pipeline runner execution, no context was produced!" assert self._latest_ctx.last_request == received, "Expected request message is not equal to expected!" assert self._latest_ctx.last_response == response, "Expected response message is not equal to expected!" - assert self.mock_bot._latest_trace == trace, "Expected trace is not equal to expected!" - self.mock_bot._latest_trace = None + assert self.bot._latest_trace == trace, "Expected trace is not equal to expected!" + self.bot._latest_trace = None self._latest_ctx = None def run_polling(self, poll_interval: float, timeout: int, allowed_updates: List[str]) -> None: diff --git a/tutorials/messengers/telegram/3_advanced.py b/tutorials/messengers/telegram/3_advanced.py index f372f019e..052bc2e75 100644 --- a/tutorials/messengers/telegram/3_advanced.py +++ b/tutorials/messengers/telegram/3_advanced.py @@ -84,7 +84,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) "source": HttpUrl(f"{EXAMPLE_ATTACHMENT_SOURCE}/deeppavlov-article.pdf"), "title": "DeepPavlov article", "filename": "deeppavlov_article.pdf", - "thumbnail": urlopen(image_url).read(), + "thumbnail": urlopen(str(image_url)).read(), } From 28d7f8a7237801b952505ae700c2869de4dabd1e Mon Sep 17 00:00:00 2001 From: pseusys Date: Wed, 8 May 2024 22:17:56 +0200 Subject: [PATCH 068/140] true transition fixed --- tests/script/core/test_message.py | 6 +++--- tutorials/messengers/telegram/3_advanced.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/script/core/test_message.py b/tests/script/core/test_message.py index 2b160c186..994284f8d 100644 --- a/tests/script/core/test_message.py +++ b/tests/script/core/test_message.py @@ -122,7 +122,7 @@ def test_attachment_serialize(self, json_context_storage: DBContextStorage, atta retrieved = json_context_storage[name].requests[0].attachments[0] assert attachment == retrieved - def test_getting_attachment_bytes(self): + async def test_getting_attachment_bytes(self): root_dir = Path(__file__).parent local_path = root_dir / "local" rmtree(local_path, ignore_errors=True) @@ -142,8 +142,8 @@ def test_getting_attachment_bytes(self): iface_document_att = Document(id=document_name) for document in (remote_document_att, local_document_att, iface_document_att): - doc_bytes = document.get_bytes(cli_iface) - assert document_bytes, doc_bytes + read_bytes = await document.get_bytes(cli_iface) + assert document_bytes, read_bytes if not isinstance(document.source, Path): cached_bytes = document.cached_filename.read_bytes() assert document_bytes, cached_bytes diff --git a/tutorials/messengers/telegram/3_advanced.py b/tutorials/messengers/telegram/3_advanced.py index 052bc2e75..9d7cdc362 100644 --- a/tutorials/messengers/telegram/3_advanced.py +++ b/tutorials/messengers/telegram/3_advanced.py @@ -225,7 +225,7 @@ def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: RESPONSE: Message( "Alright! Now send me any message and I'll send you it's raw data!" ), - TRANSITIONS: {"raw_request_node": cnd.true}, + TRANSITIONS: {"raw_request_node": cnd.true()}, }, "raw_request_node": { RESPONSE: stringify_previous_request, @@ -234,7 +234,7 @@ def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: RESPONSE: Message( "Alright! Now send me a message with data attachment (audio, video, animation, image, sticker or document)!" ), - TRANSITIONS: {"hash_request_node": cnd.true}, + TRANSITIONS: {"hash_request_node": cnd.true()}, }, "hash_request_node": { RESPONSE: hash_data_attachment_request, From 786b3c7411d692997bebbd18b739382ca1151aee Mon Sep 17 00:00:00 2001 From: pseusys Date: Wed, 8 May 2024 22:26:26 +0200 Subject: [PATCH 069/140] async function marked --- tests/messengers/telegram/utils.py | 2 +- tests/script/core/test_message.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/messengers/telegram/utils.py b/tests/messengers/telegram/utils.py index f6fc2a1a6..a95f5414c 100644 --- a/tests/messengers/telegram/utils.py +++ b/tests/messengers/telegram/utils.py @@ -38,7 +38,7 @@ class MockApplication(BaseModel): @classmethod def create(cls, interface: _AbstractTelegramInterface, happy_path: List[PathStep]) -> "MockApplication": mock_bot = MockBot(_original_bot=interface.application.bot) - instance = cls(mock_bot=mock_bot, happy_path=happy_path, _interface=interface) + instance = cls(bot=mock_bot, happy_path=happy_path, _interface=interface) interface.pipeline_runner = instance._wrapped_pipeline_runner return instance diff --git a/tests/script/core/test_message.py b/tests/script/core/test_message.py index 994284f8d..89fb22ffa 100644 --- a/tests/script/core/test_message.py +++ b/tests/script/core/test_message.py @@ -122,6 +122,7 @@ def test_attachment_serialize(self, json_context_storage: DBContextStorage, atta retrieved = json_context_storage[name].requests[0].attachments[0] assert attachment == retrieved + @pytest.mark.asyncio async def test_getting_attachment_bytes(self): root_dir = Path(__file__).parent local_path = root_dir / "local" From 2277af7a5c0c931bea0d803ec7a78303bcd6a0e1 Mon Sep 17 00:00:00 2001 From: pseusys Date: Wed, 8 May 2024 22:34:06 +0200 Subject: [PATCH 070/140] cache dir clear and create reversed --- tests/script/core/test_message.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/script/core/test_message.py b/tests/script/core/test_message.py index 89fb22ffa..1922b5bbf 100644 --- a/tests/script/core/test_message.py +++ b/tests/script/core/test_message.py @@ -52,6 +52,11 @@ class TestMessage: def json_context_storage(self) -> DBContextStorage: return JSONContextStorage(f"file://{Path(__file__).parent / 'serialization_database.json'}") + def clear_and_create_dir(self, dir: Path) -> Path: + rmtree(dir, ignore_errors=True) + dir.mkdir() + return dir + def test_location(self): loc1 = Location(longitude=-0.1, latitude=-0.1) loc2 = Location(longitude=-0.09999, latitude=-0.09998) @@ -125,12 +130,10 @@ def test_attachment_serialize(self, json_context_storage: DBContextStorage, atta @pytest.mark.asyncio async def test_getting_attachment_bytes(self): root_dir = Path(__file__).parent - local_path = root_dir / "local" - rmtree(local_path, ignore_errors=True) - local_path.mkdir() + local_path = self.clear_and_create_dir(root_dir / "local") local_document = local_path / "pre-saved-document.pdf" - cli_iface = DFFCLIMessengerInterface(root_dir / "cache") + cli_iface = DFFCLIMessengerInterface(self.clear_and_create_dir(root_dir / "cache")) document_name = "deeppavlov-article.pdf" remote_document_url = f"{EXAMPLE_SOURCE}/{document_name}" From 6e218b618fc6919b44646673630c0240a615a4e3 Mon Sep 17 00:00:00 2001 From: pseusys Date: Wed, 8 May 2024 22:41:26 +0200 Subject: [PATCH 071/140] pytest warning fixed --- tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index dd8301879..85ba92404 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,7 @@ import pytest -def pytest_report_header(config, start_path, startdir): +def pytest_report_header(config, start_path): print(f"allow_skip: {config.getoption('--allow-skip') }") From e6d1226471d19c3370910ecc9c3fbe4b19dbb9d3 Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 21 May 2024 23:41:39 +0200 Subject: [PATCH 072/140] tutorial testing done --- dff/messengers/telegram/abstract.py | 6 +- dff/script/core/message.py | 78 ++++-- dff/utils/pydantic/__init__.py | 43 ++++ .../messengers/telegram/test_happy_paths.json | 229 +++++++++++++++++- tests/messengers/telegram/test_tutorials.py | 29 ++- tests/messengers/telegram/utils.py | 88 ++++--- .../messengers/telegram/2_attachments.py | 4 +- tutorials/messengers/telegram/3_advanced.py | 40 +-- 8 files changed, 411 insertions(+), 106 deletions(-) create mode 100644 dff/utils/pydantic/__init__.py diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index 75ad0431f..6c7e7be83 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -419,12 +419,10 @@ async def _on_event( ) async def on_message(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> None: - await self._on_event(update, _, lambda u: self.extract_message_from_telegram(u.message)) + await self._on_event(update, _, lambda s: self.extract_message_from_telegram(s.message)) async def on_callback(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> None: - await self._on_event( - update, _, lambda u: Message(attachments=[CallbackQuery(query_string=u.callback_query.data)]) - ) + await self._on_event(update, _, lambda s: Message(attachments=[CallbackQuery(query_string=s.callback_query.data)])) async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs): self.pipeline_runner = pipeline_runner diff --git a/dff/script/core/message.py b/dff/script/core/message.py index 2bc0e8c80..d982f9d0b 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -5,33 +5,50 @@ DFF. It only contains types and properties that are compatible with most messaging services. """ -from typing import Any, Literal, Optional, List, Union +from typing import Any, Callable, Dict, Literal, Optional, List, Union from enum import Enum, auto from pathlib import Path from urllib.request import urlopen from uuid import uuid4 -from pydantic import Field, field_validator, FilePath, HttpUrl, BaseModel, model_validator +from pydantic import Field, field_validator, FilePath, HttpUrl, BaseModel, model_serializer, model_validator from pydantic_core import Url from dff.messengers.common.interface import MessengerInterface +from dff.utils.pydantic import json_pickle_serializer, json_pickle_validator -class Session(Enum): +class DataModel(BaseModel, extra="allow", arbitrary_types_allowed=True): """ - An enumeration that defines two possible states of a session. + This class is a Pydantic BaseModel that serves as a base class for all DFF models. """ - ACTIVE = auto() - FINISHED = auto() + pass -class DataModel(BaseModel, extra="allow", arbitrary_types_allowed=True): +# TODO: inline once annotated __pydantic_extra__ will be available in pydantic +def _json_extra_serializer(model: DataModel, original_serializer: Callable[[DataModel], Dict[str, Any]]) -> Dict[str, Any]: + model_copy = model.model_copy(deep=True) + for extra_name in model.model_extra.keys(): + delattr(model_copy, extra_name) + model_dict = original_serializer(model_copy) + model_dict.update(json_pickle_serializer(model.model_extra, original_serializer)) + return model_dict + + +# TODO: inline once annotated __pydantic_extra__ will be available in pydantic +def _json_extra_validator(model: DataModel) -> DataModel: + model.__pydantic_extra__ = json_pickle_validator(model.__pydantic_extra__) + return model + + +class Session(Enum): """ - This class is a Pydantic BaseModel that serves as a base class for all DFF models. + An enumeration that defines two possible states of a session. """ - pass + ACTIVE = auto() + FINISHED = auto() class Command(DataModel): @@ -44,12 +61,21 @@ class Command(DataModel): class Attachment(DataModel): - pass + """ + """ + + @model_validator(mode="after") + def extra_validator(self) -> "Attachment": + return _json_extra_validator(self) + + @model_serializer(mode="wrap", when_used="json") + def extra_serializer(self, original_serializer: Callable[["Attachment"], Dict[str, Any]]) -> Dict[str, Any]: + return _json_extra_serializer(self, original_serializer) class CallbackQuery(Attachment): query_string: Optional[str] - type: Literal["callback_query"] = "callback_query" + dff_attachment_type: Literal["callback_query"] = "callback_query" class Location(Attachment): @@ -63,7 +89,7 @@ class Location(Attachment): longitude: float latitude: float - type: Literal["location"] = "location" + dff_attachment_type: Literal["location"] = "location" def __eq__(self, other): if isinstance(other, Location): @@ -75,7 +101,7 @@ class Contact(Attachment): phone_number: str first_name: str last_name: Optional[str] - type: Literal["contact"] = "contact" + dff_attachment_type: Literal["contact"] = "contact" class Invoice(Attachment): @@ -83,19 +109,19 @@ class Invoice(Attachment): description: str currency: str amount: int - type: Literal["invoice"] = "invoice" + dff_attachment_type: Literal["invoice"] = "invoice" class PollOption(DataModel): text: str votes: int = Field(default=0) - type: Literal["poll_option"] = "poll_option" + dff_attachment_type: Literal["poll_option"] = "poll_option" class Poll(Attachment): question: str options: List[PollOption] - type: Literal["poll"] = "poll" + dff_attachment_type: Literal["poll"] = "poll" class DataAttachment(Attachment): @@ -161,37 +187,37 @@ def validate_source(cls, value): class Audio(DataAttachment): """Represents an audio file attachment.""" - type: Literal["audio"] = "audio" + dff_attachment_type: Literal["audio"] = "audio" class Video(DataAttachment): """Represents a video file attachment.""" - type: Literal["video"] = "video" + dff_attachment_type: Literal["video"] = "video" class Animation(DataAttachment): """Represents an animation file attachment.""" - type: Literal["animation"] = "animation" + dff_attachment_type: Literal["animation"] = "animation" class Image(DataAttachment): """Represents an image file attachment.""" - type: Literal["image"] = "image" + dff_attachment_type: Literal["image"] = "image" class Sticker(DataAttachment): """Represents a sticker as a file attachment.""" - type: Literal["sticker"] = "sticker" + dff_attachment_type: Literal["sticker"] = "sticker" class Document(DataAttachment): """Represents a document file attachment.""" - type: Literal["document"] = "document" + dff_attachment_type: Literal["document"] = "document" class Message(DataModel): @@ -236,3 +262,11 @@ def __eq__(self, other): def __repr__(self) -> str: return " ".join([f"{key}='{value}'" for key, value in self.model_dump(exclude_none=True).items()]) + + @model_validator(mode="after") + def extra_validator(self) -> "Message": + return _json_extra_validator(self) + + @model_serializer(mode="wrap", when_used="json") + def extra_serializer(self, original_serializer: Callable[["Message"], Dict[str, Any]]) -> Dict[str, Any]: + return _json_extra_serializer(self, original_serializer) diff --git a/dff/utils/pydantic/__init__.py b/dff/utils/pydantic/__init__.py new file mode 100644 index 000000000..a4d2b09c1 --- /dev/null +++ b/dff/utils/pydantic/__init__.py @@ -0,0 +1,43 @@ +from base64 import decodebytes, encodebytes +from copy import deepcopy +from pickle import dumps, loads +from typing import Annotated, Any, Callable, Dict +from pydantic import PydanticSchemaGenerationError, TypeAdapter, WrapSerializer, WrapValidator + +_JSON_EXTRA_FIELDS_KEYS = "__pickled_extra_fields__" + + +def json_pickle_serializer(model: Dict[str, Any], original_serializer: Callable[[Dict[str, Any]], Dict[str, Any]]) -> Dict[str, Any]: + extra_fields = list() + model_copy = deepcopy(model) + + for field_name, field_value in model_copy.items(): + try: + if isinstance(field_value, bytes): + raise PydanticSchemaGenerationError("") + else: + TypeAdapter(type(field_value)) + except PydanticSchemaGenerationError: + model_copy[field_name] = encodebytes(dumps(field_value)).decode() + extra_fields += [field_name] + + original_dump = original_serializer(model_copy) + original_dump[_JSON_EXTRA_FIELDS_KEYS] = extra_fields + return original_dump + + +def json_pickle_validator(model: Dict[str, Any]) -> Dict[str, Any]: + model_copy = deepcopy(model) + + if _JSON_EXTRA_FIELDS_KEYS in model.keys(): + for extra_key in model[_JSON_EXTRA_FIELDS_KEYS]: + extra_value = model[extra_key] + model_copy[extra_key] = loads(decodebytes(extra_value.encode())) + del model_copy[_JSON_EXTRA_FIELDS_KEYS] + + return model_copy + + +JSONPickleSerializer = WrapSerializer(json_pickle_serializer, when_used="json") +JSONPickleValidator = WrapValidator(json_pickle_validator) +JSONSerializableDict = Annotated[Dict[str, Any], JSONPickleSerializer, JSONPickleValidator] diff --git a/tests/messengers/telegram/test_happy_paths.json b/tests/messengers/telegram/test_happy_paths.json index 145f62094..0208daf7a 100644 --- a/tests/messengers/telegram/test_happy_paths.json +++ b/tests/messengers/telegram/test_happy_paths.json @@ -1,11 +1,222 @@ { - "1_basic": { - - }, - "2_attachments": { - - }, - "3_advanced": { - - } + "1_basic": [ + { + "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 8, 21, 5, 30, tzinfo=UTC), delete_chat_photo=False, entities=(MessageEntity(length=6, offset=0, type=MessageEntityType.BOT_COMMAND),), from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=1, supergroup_chat_created=False, text='/start'), update_id=1)", + "received_message": "{\"text\":\"/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_message": "{\"text\":\"Hi\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_functions": [ + "send_message(42, 'Hi', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None)" + ] + }, + { + "update":"Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 8, 21, 5, 31, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=2, supergroup_chat_created=False, text='Hi'), update_id=2)", + "received_message": "{\"text\":\"Hi\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_message": "{\"text\":\"Hi\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_functions": [ + "send_message(42, 'Hi', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None)" + ] + } + ], + "2_attachments": [ + { + "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 0, 14, tzinfo=UTC), delete_chat_photo=False, entities=(MessageEntity(length=6, offset=0, type=MessageEntityType.BOT_COMMAND),), from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=1, supergroup_chat_created=False, text='/start'), update_id=1)", + "received_message": "{\"text\":\"/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_message": "{\"text\":\"Type \\\"location\\\", \\\"contact\\\", \\\"poll\\\", \\\"sticker\\\" \\\"audio\\\", \\\"video\\\", \\\"animation\\\", \\\"image\\\", \\\"document\\\" or to receive a corresponding attachment!\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_functions": [ + "send_message(42, 'Type \"location\", \"contact\", \"poll\", \"sticker\" \"audio\", \"video\", \"animation\", \"image\", \"document\" or to receive a corresponding attachment!', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None)" + ] + }, + { + "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 0, 25, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=2, supergroup_chat_created=False, text='location'), update_id=2)", + "received_message": "{\"text\":\"location\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_message": "{\"text\":\"Here's your location!\",\"commands\":null,\"attachments\":[{\"longitude\":3.916667,\"latitude\":50.65,\"dff_attachment_type\":\"location\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_functions": [ + "send_location(42, 50.65, 3.916667, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=None)", + "send_message(42, \"Here's your location!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None)" + ] + }, + { + "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 0, 32, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=3, supergroup_chat_created=False, text='contact'), update_id=3)", + "received_message": "{\"text\":\"contact\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_message": "{\"text\":\"Here's your contact!\",\"commands\":null,\"attachments\":[{\"phone_number\":\"8-900-555-35-35\",\"first_name\":\"Hope\",\"last_name\":\"Credit\",\"dff_attachment_type\":\"contact\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_functions": [ + "send_contact(42, '8-900-555-35-35', 'Hope', 'Credit', vcard=None, disable_notification=None, protect_content=None, reply_markup=None)", + "send_message(42, \"Here's your contact!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None)" + ] + }, + { + "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 0, 36, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=4, supergroup_chat_created=False, text='poll'), update_id=4)", + "received_message": "{\"text\":\"poll\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_message": "{\"text\":\"Here's your poll!\",\"commands\":null,\"attachments\":[{\"question\":\"What is the poll question?\",\"options\":[{\"text\":\"This one!\",\"votes\":0,\"dff_attachment_type\":\"poll_option\"},{\"text\":\"Not this one :(\",\"votes\":0,\"dff_attachment_type\":\"poll_option\"}],\"dff_attachment_type\":\"poll\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_functions": [ + "send_poll(42, 'What is the poll question?', ['This one!', 'Not this one :('], is_anonymous=None, type=None, allows_multiple_answers=None, correct_option_id=None, explanation=None, explanation_parse_mode=None, open_period=None, is_closed=None, disable_notification=None, protect_content=None, reply_markup=None)", + "send_message(42, \"Here's your poll!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None)" + ] + }, + { + "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 0, 43, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=5, supergroup_chat_created=False, text='sticker'), update_id=5)", + "received_message": "{\"text\":\"sticker\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_message": "{\"text\":\"Here's your sticker!\",\"commands\":null,\"attachments\":[{\"source\":null,\"cached_filename\":null,\"id\":\"CAACAgIAAxkBAAErAAFXZibO5ksphCKSXSe1CYiw5588yqsAAkEAAzyKVxogmx2BPCogYDQE\",\"title\":\"A sticker I've just found\",\"use_cache\":true,\"dff_attachment_type\":\"sticker\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_functions": [ + "send_sticker(42, 'CAACAgIAAxkBAAErAAFXZibO5ksphCKSXSe1CYiw5588yqsAAkEAAzyKVxogmx2BPCogYDQE', disable_notification=None, reply_markup=None, emoji=None)", + "send_message(42, \"Here's your sticker!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None)" + ] + }, + { + "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 0, 46, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=6, supergroup_chat_created=False, text='audio'), update_id=6)", + "received_message": "{\"text\":\"audio\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_message": "{\"text\":\"Here's your audio!\",\"commands\":null,\"attachments\":[{\"source\":\"https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/separation-william-king.mp3\",\"cached_filename\":null,\"id\":null,\"title\":\"\\\"Separation\\\" melody by William King\",\"use_cache\":true,\"dff_attachment_type\":\"audio\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_functions": [ + "send_audio(42, audio=<>, performer=None, title=None, caption=\"Here's your audio!\", disable_notification=None, reply_markup=None, parse_mode=None, thumbnail=None)" + ] + }, + { + "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 1, 4, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=7, supergroup_chat_created=False, text='video'), update_id=7)", + "received_message": "{\"text\":\"video\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_message": "{\"text\":\"Here's your video!\",\"commands\":null,\"attachments\":[{\"source\":\"https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/crownfall-lags-nkognit0.mp4\",\"cached_filename\":null,\"id\":null,\"title\":\"Epic Dota2 gameplay by Nkognit0\",\"use_cache\":true,\"dff_attachment_type\":\"video\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_functions": [ + "send_video(42, <>, caption=\"Here's your video!\", disable_notification=None, reply_markup=None, parse_mode=None, supports_streaming=None, has_spoiler=None, thumbnail=None, filename=None)" + ] + }, + { + "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 1, 22, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=8, supergroup_chat_created=False, text='animation'), update_id=8)", + "received_message": "{\"text\":\"animation\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_message": "{\"text\":\"Here's your animation!\",\"commands\":null,\"attachments\":[{\"source\":\"https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/hong-kong-simplyart4794.gif\",\"cached_filename\":null,\"id\":null,\"title\":\"Hong Kong skyscraper views by Simplyart4794\",\"use_cache\":true,\"dff_attachment_type\":\"animation\",\"filename\":\"hk.gif\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_functions": [ + "send_animation(42, <>, caption=\"Here's your animation!\", parse_mode=None, disable_notification=None, reply_markup=None, has_spoiler=None, thumbnail=None, filename='hk.gif')" + ] + }, + { + "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 1, 35, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=9, supergroup_chat_created=False, text='image'), update_id=9)", + "received_message": "{\"text\":\"image\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_message": "{\"text\":\"Here's your image!\",\"commands\":null,\"attachments\":[{\"source\":\"https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/deeppavlov.png\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov logo\",\"use_cache\":true,\"dff_attachment_type\":\"image\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_functions": [ + "send_photo(42, <>, caption=\"Here's your image!\", disable_notification=None, reply_markup=None, parse_mode=None, has_spoiler=None, filename=None)" + ] + }, + { + "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 2, 19, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=10, supergroup_chat_created=False, text='document'), update_id=10)", + "received_message": "{\"text\":\"document\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_message": "{\"text\":\"Here's your document!\",\"commands\":null,\"attachments\":[{\"source\":\"https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/deeppavlov-article.pdf\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov article\",\"use_cache\":true,\"dff_attachment_type\":\"document\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_functions": [ + "send_document(42, <>, caption=\"Here's your document!\", disable_notification=None, reply_markup=None, parse_mode=None, thumbnail=None, filename=None)" + ] + }, + { + "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 2, 32, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=11, supergroup_chat_created=False, text='something else'), update_id=11)", + "received_message": "{\"text\":\"something else\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_message": "{\"text\":\"Unknown attachment type, try again! Supported attachments are: \\\"location\\\", \\\"contact\\\", \\\"poll\\\", \\\"sticker\\\", \\\"audio\\\", \\\"video\\\", \\\"animation\\\", \\\"image\\\" and \\\"document\\\".\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_functions": [ + "send_message(42, 'Unknown attachment type, try again! Supported attachments are: \"location\", \"contact\", \"poll\", \"sticker\", \"audio\", \"video\", \"animation\", \"image\" and \"document\".', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None)" + ] + } + ], + "3_advanced": [ + { + "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 9, 20, tzinfo=UTC), delete_chat_photo=False, entities=(MessageEntity(length=6, offset=0, type=MessageEntityType.BOT_COMMAND),), from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=1, supergroup_chat_created=False, text='/start'), update_id=1)", + "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_functions": [ + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))))" + ] + }, + { + "update": "Update(callback_query=CallbackQuery(chat_instance='-1', data='formatted', from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), id='1', message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 9, 20, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Bot', id=16, is_bot=True, username='dff_bot'), group_chat_created=False, location=Location(latitude=58.43162, longitude=27.792879), message_id=2, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), supergroup_chat_created=False)), update_id=2)", + "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"formatted\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_message": "{\"text\":\"\\nHere's your formatted text\\\\!\\nYou can see **text in bold** and _text in italic_\\\\.\\n\\\\> Here's a [link](https:\/\/github.com\/deeppavlov\/dialog_flow_framework) in a quote\\\\.\\nRun \/start command again to restart\\\\.\\n\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null,\"parse_mode\":\"MarkdownV2\",\"__pickled_extra_fields__\":[]}", + "response_functions": [ + "send_message(42, \"\\nHere's your formatted text\\\\!\\nYou can see **text in bold** and _text in italic_\\\\.\\n\\\\> Here's a [link](https://github.com/deeppavlov/dialog_flow_framework) in a quote\\\\.\\nRun /start command again to restart\\\\.\\n\", parse_mode=, disable_notification=None, protect_content=None, reply_markup=None)" + ] + }, + { + "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 9, 24, tzinfo=UTC), delete_chat_photo=False, entities=(MessageEntity(length=6, offset=0, type=MessageEntityType.BOT_COMMAND),), from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=3, supergroup_chat_created=False, text='/start'), update_id=3)", + "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_functions": [ + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))))" + ] + }, + { + "update": "Update(callback_query=CallbackQuery(chat_instance='-1', data='attachments', from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), id='3', message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 9, 24, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Bot', id=16, is_bot=True, username='dff_bot'), group_chat_created=False, location=Location(latitude=58.43162, longitude=27.792879), message_id=4, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), supergroup_chat_created=False)), update_id=4)", + "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"attachments\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_message": "{\"text\":\"Here's your message with multiple attachments (a location and a sticker)!\\nRun \/start command again to restart.\",\"commands\":null,\"attachments\":[{\"longitude\":30.3141,\"latitude\":59.9386,\"dff_attachment_type\":\"location\",\"__pickled_extra_fields__\":[]},{\"source\":null,\"cached_filename\":null,\"id\":\"CAACAgIAAxkBAAErBZ1mKAbZvEOmhscojaIL5q0u8vgp1wACRygAAiSjCUtLa7RHZy76ezQE\",\"title\":null,\"use_cache\":true,\"dff_attachment_type\":\"sticker\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_functions": [ + "send_location(42, 59.9386, 30.3141, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=None)", + "send_sticker(42, 'CAACAgIAAxkBAAErBZ1mKAbZvEOmhscojaIL5q0u8vgp1wACRygAAiSjCUtLa7RHZy76ezQE', disable_notification=None, reply_markup=None, emoji=None)", + "send_message(42, \"Here's your message with multiple attachments (a location and a sticker)!\\nRun /start command again to restart.\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None)" + ] + }, + { + "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 9, 28, tzinfo=UTC), delete_chat_photo=False, entities=(MessageEntity(length=6, offset=0, type=MessageEntityType.BOT_COMMAND),), from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=5, supergroup_chat_created=False, text='/start'), update_id=5)", + "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_functions": [ + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))))" + ] + }, + { + "update": "Update(callback_query=CallbackQuery(chat_instance='-1', data='secret', from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), id='5', message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 9, 28, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Bot', id=16, is_bot=True, username='dff_bot'), group_chat_created=False, location=Location(latitude=58.43162, longitude=27.792879), message_id=6, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), supergroup_chat_created=False)), update_id=6)", + "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"secret\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_message": "{\"text\":\"Here's your secret image! Run \/start command again to restart.\",\"commands\":null,\"attachments\":[{\"source\":\"https:\/\/cdn.jsdelivr.net\/gh\/deeppavlov\/dialog_flow_framework@example-attachments\/deeppavlov.png\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov logo\",\"use_cache\":true,\"dff_attachment_type\":\"image\",\"has_spoiler\":true,\"filename\":\"deeppavlov_logo.png\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_functions": [ + "send_photo(42, <>, caption=\"Here's your secret image! Run /start command again to restart.\", disable_notification=None, reply_markup=None, parse_mode=None, has_spoiler=True, filename='deeppavlov_logo.png')" + ] + }, + { + "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 9, 34, tzinfo=UTC), delete_chat_photo=False, entities=(MessageEntity(length=6, offset=0, type=MessageEntityType.BOT_COMMAND),), from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=7, supergroup_chat_created=False, text='/start'), update_id=7)", + "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_functions": [ + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))))" + ] + }, + { + "update": "Update(callback_query=CallbackQuery(chat_instance='-1', data='thumbnail', from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), id='7', message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 9, 34, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Bot', id=16, is_bot=True, username='dff_bot'), group_chat_created=False, location=Location(latitude=58.43162, longitude=27.792879), message_id=8, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), supergroup_chat_created=False)), update_id=8)", + "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"thumbnail\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_message": "{\"text\":\"Here's your document with tumbnail! Run \/start command again to restart.\",\"commands\":null,\"attachments\":[{\"source\":\"https:\/\/cdn.jsdelivr.net\/gh\/deeppavlov\/dialog_flow_framework@example-attachments\/deeppavlov-article.pdf\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov article\",\"use_cache\":true,\"dff_attachment_type\":\"document\",\"filename\":\"deeppavlov_article.pdf\",\"thumbnail\":\"gASVySsAAAAAAABCwisAAIlQTkcNChoKAAAADUlIRFIAAADIAAAAyAgCAAAAIjo5yQAAK4lJREFU\\neJzsnXdcFFfXx+\/sbF\/KsnSQLiiIUlSwgo+KYi9RAbvYS4wleZ7EqNFojEaTqOlqLLFh77F3jaCA\\nCEgREJDeWbb394PwIiIus7NTFrPfj3\/g7My9h+U3t557DlWj0QAjRrCGQrYBRj5MjMIyggtGYRnB\\nBaOwjOCCUVhGcMEoLCO4YBSWEVwwCssILhiFZQQXqGQbgBIlf6+qYhHZVuAMbEOzP0NhdifbDjS0\\n1xZLVbmSbBPwR1WuEsSQbQRK2quwgEZCtgWEoBGTbQFK2q2wjBg2RmEZwQWjsIzgglFYRnDBKCw0\\n8GXsEhGPL2PrU4hcRX1a4Y6dUYZFe13H0s7DEu9tTycxYflgp8Ro76sQhOipx2Ve3yZEMWH5KNfY\\nyZ1ua7nzUn7Q7ucjulrm7gz5FbWRNwv9l91fkDl1DuoSDJkPU1ilYh6DIt8QvD\/65qdcunCoS8LG\\nJ5OFCtZnAcddzCrOvex9raB7oHXWbJ+rmxPq9ZfF77Cgy8VyiYVGAzb13jvk3OYulnnPq10flnSh\\nQqqvgw8czw515FQOd31yLCuExxBM9rrDl5mk1zg3VLcrddizKo\/edmmhDsm704Z9HXywWMj74\/mI\\n9cEHm1t1JPM\/D0u7MGH5V0EHM2s6XMzrNanjPZK+Idz5YLtCGqzqyC0Jc46\/Vej\/6YO5tqyavvap\\n824vyxdYr4md8XHXs\/vShqZVO5\/L7ZNe4+zFLVjxYD4AQAMgCqQGQEOnKDtxC77scVitATFZoQo1\\nvDF+MgDgh6cfmdFFzSs6md3vyIuBEzzurX88tUzC\/Tsv6G6R76X8IImS3sKkblYvV\/c4nFdnczwr\\n1MO8JNz5ybGsAcR+K8TxYbZYTYgVTAum8GZhQHKVuylNYsuqiSvtXCs3+eT+Ig5NSgFqAMB837\/N\\naKIfnk4AAMSWek++umplwClvXsHauGl3Cv3rFGwf3qto76urHkUnlHfkyznBdi+aV5FV6xhkm\/Gf\\nDsn97FOTq9wjve5cyO39qNRnR8gvDTeoNRAF0mg0YF\/6kCflncrEFiGOqTym0Ncyj6RvhQg+WGEV\\nCS1\/SxlxIifk0sjVpSIelyGY3OkOC5axqPX\/1vQ8DEEaJ9MKAEB6tVNiRcchzvEAgP4OqSeHbWgo\\n4VhW6Ibgv\/akhQMAmFTFoq7nh1345que9b3bS75dvsCmTMzN4dt15BZtSYi8VeB37VWPdUH1n\/Y5\\nucOGXdPdOhsAcDqnz+K7S4tnRQoVzN3Ph18Yufa\/D+sHVeVi82eV7jIV7XFZpyDbTLK\/LeyB161b\\nR7YNOpNUJLBWbtVyA5WigiDIhCZdH3zQw7w0xDGlQGjzoMS3g0mln3Wuv1XO+dzeVVKzYNuMvzLD\\nxEpmgHXOZ4EnmLDcgV3dyaKwoZBO3MLYMu+JHvd9eK\/sODU+vPwqqfmSbufYNHlcWSeJitnBpJIC\\naUa7xVox6+4U+23svd+HV8BliJmwfIRrnA+vAADAospMaeJ+DmkMWOlrmXe\/2HdOlyte3CKJkpFY\\n4dXLLqNAaNPbLv19v0iB2FvNGsamwzh8i\/gCta9zhQkFdRuu515IqxQvGIFJgb1Pbv8l9OfA162L\\nAbInLXz14+VL+nb4dICLOas9dS\/txtbcasmmG3kH4ksUKizfhEsjV5vSDXo\/u1ai3Hgjb09c8ReD\\nXJf2dyLbHKS0g1mhUqVefTnHc9M\/e+KKsVUVAIDHFNIoKmzLxINSgfyTsy\/cNj688aKabFsQYegt\\n1vnUioWnMorr5GQbYhDk1UjD\/ng6pBNvX4SPgzmDbHO0YbgtVoVQPn5f8ph9yUZVteBaZnWnzY9+\\neVBAtiHaMFBh7Xtc7L0l9kxqBdmGGChCuWrJmRcDfk14Xiok25bWMThhVYrkkw+mRh9LrxIrtNym\\n1iDb\/2vnKFTaFhru5tQG73iy61GRSk2gTcgwLGHdf1nTdWvc0aSyNu88n9uLEIvIRKGCrxcEar9H\\nJFfPP5kxdt+zaq3vIfEY0DrWqr+zt95+pVQjsodNlYY5JVix6vC3ixw0GpBY4ZlU2RHh\/Xam9PPR\\n3Xo6m+NsF1IMQlg1YsWMo2kX0irJNqR9w6RSNg33WB7qTLYhwCCElVkuGrsvOaO8vR5HMTSW9u+w\\nbZQnDSZ5kEOysO69rBn95zO+tB0sUbYj+rtzL0T7kbsFRKaw9sYVLziZoUA2qDKiE64WzDuLAl14\\nLLIMIEdYagDWXc7ZcONDdkgiHWcu88QM3yCShvMkCEupUn95Oee7268IrvdfiCWbejbar58bl\/iq\\nSRDWsrOZO+4XElzpvxZTBnw+2m9ARwuC6yVaWPNPpO+KLSayRiMsGuXvOf4Ea4vQSemS0xlGVRGP\\nRKEevifpQW4NkZUSJ6z\/Xcz65WERYdUZaY5EoR79Z3JCAXEbFQQJ69ubucbROrnUSJRDdydllImI\\nqY4IYR14Urzq75cEVGREO1UixcDfEssEMgLqwl1Yj1\/xF5z8AI83tVNKBPLwXUkiOe5bHfgKK7Nc\\nNHx3klRpeO5C\/2KSioXzTqTLcfbhwlFYApky8mBqlViJXxVG0HEksWzLLXy3PfASlkoN5p\/ISCo2\\nUMdZI19dyb2agaOfEl7C+vFe\/tGnbTuCGiELDQBRh57nVuF1phIXYT15Vbf6cg4eJRvBkBqJctJf\\nKTg5l2AvLIlCFXUoVaY0OsO0A+ILBeuv4tIEYC+sZWdf5ODWwBrBnK+v5z1+xce8WIyFdTWjyrgb\\n2O6IPJgqUWC8soWlsKpE8jnH3xuRx4jBklstXXEuC9sysXSbWXAi\/Y\/22FypVeySZJqwTOQYqDSx\\naf4JpJSZ5j9kVuXAMoGKaS4zcxS49FEzzcizFS9gCDxe1jOwA2a\/GmbCupdTM+j3pwhPBRoU1k\/2\\nmL2KrZ+BQ3DRgM9lPLfGD9Qqu39+4pSlNr9ZRWOV95gtdvAnx1Y88XcwebS0B5OGTZA3bLpChUq9\\n7NyL9qgqWFRl+lpVr18ylVnOraaPOMWJLVRVf79CYvfoF0algQZq04ekYuEvDzHz7MXmhNAfj4qe\\nFrXLRXYNBQYQBWgaN86cLU1H9LTvwGX06GBWEF+5Pe7Nnc4uLr6+vjeuX5fL5X1Lz2\/9JkahUqeW\\nCp8VCwv5sttZNbXSdr95tf5a7pRAOzszDAIkYdAVVork3ltiK0WGFTsAOc4pR2gv6hsqFpt1\/NQp\\nb2\/vhusymWxu9Ox\/Hj4EAMyZO3fFZ5\/S6fSE+PiICRMBAIdjjgb3ehM\/Qq5SpxQLb2fX3M+tvZxR\\nhXmAOMJY3Nfx5\/Gd9S8HA2GtbZ8HubhMamSA7YI+jn4OpokJiTk52UOHDjUzb3lYKutFFpfLtbax\\nbroya\/qM+\/furV67ZmZ0dKslK1TqI4mlx5LKr2RUtUd9PV0R5O9oqmch+naFxXzZd3fy9SyEYAId\\nTRf1dZzZ07HpGHpg98DA7q3HdfH08mxxxdHREQCQnfXeYRYNpszo6TCjp0OZQLb19qtdsUUCWXs6\\n6r3+2sszs\/z0LETfFuvj05k\/Yzfiw5t+buabhnv0d9frvMqd27dTklM6enYcNnw4kvsVKvWeuOLV\\nl3Oq248HUeKKoAD9Gi29hFUqkDl9\/bBdTAaDnM2+Dncf2smSLANEctX2e6++u51f1x4CVQzsaHFz\\nYRuhubSjl7AWn8r49R9DP3hjxoQ3hnss6ONIegAWAMCrGunnl7JjnpYZ\/rv4bGVQNwf0jRZ6YfGl\\nSpu19+SGPf0Z2om3P9IHk\/kzhlxKq1xwMqOQT8ShBtREBdgemeqL+nH0L\/E313MNWVVUCrRrYucr\\n8wIMTVUAgBE+Vokrg0Z3sSLbEG0cfVqWVYE+aBlKYSnVmt1xBrMtqFZRheX0mnxGTV7DP0+W9OHH\\n3ef2ciTbsvdizaGfi\/bbPMKDUfuKKqoAakMceP3+CP04B2VXeCSxdMrh56hr1R8av4hdlsoue04V\\nV9FElZDmzR+mi2+XP\/fvt7Iy6PagiatXrny2YqVIKpOb2Co51mIbH7G9n5JjEMYzqJSqDSEcVCmi\\nUAor8Ic4cvZw1ErT\/Fjuiyt0YWmrn\/fr33\/vgf0UCvnjdOQ8S3q2cN688vLypisSS8+aTsMk9t1I\\ntaue3z7qtKBPBxQPohHW00JB4I+PUVSmJ5xXsVYpJ6jS97o7jhk7dsu2rVSqoedxeZfCgoIJ48ZX\\nVr51bEbKdan0j5JZIg2cjAe9XMweLe2J4kE0b\/aBeKJHV1Rhuf397+2e7NGiqtFjxnz3\/bb2qCoA\\nQAcnp1Nnzzg4ODS\/yKzNd7yzhffsGKQkLelLbH7do7xaFA\/qLCylWnOW2EwkjKoch3tb2eXafFND\\nBwzYvPU7GG5\/CSObcOzQ4fc9u1tsVkJAY5F93fbRzxQ5ac4jp5LR\/Ll1Ftbl9Mr8GuIWYFhlaQ73\\nttIk2mI7+fr6bv9pJ53eMrl3u8PHx+fnX39lMpktrnPK0+zv\/0iRCUix6nRKOYrj+DoL62Ryuc6V\\noIVZnmH\/z06KWtsWm42NzW+7d5ma6rsbbyD06dtn9Vdr373OrM23f7gDaP0qcCK3WhpfoPMxHt2E\\nJVOqL6dX6VoHOmiCUtvHu6C2vsodP\/9kb29PjEnEEBkVNSli0rvXmTV5No93k2ER2BWr84KWbsJK\\nKKirIMahT6O2jt9HlbURge6T5ct7BgURYQ+xrFm3zt3D493rpkUJJv\/vSE0kt7J1DjOpm7CQ5OXC\\nBIv0i6zqNk7oevv4fPzJUmLsIRgWi7Xz558gqJXUedaJByGllGB78qqlz4p0G+HpJqzrmUQkJKbI\\nBOY5N9u4h0LZvnMHAcaQRWdv70VLlrx7naKScTOvEG\/PZR1D0+ggrIwyUaYeu5LI4WZegeVthMpc\\nsGihR0cyVw4JYM68ue7u7u9et3hxBaiIPmHwIFe38bsOwnpCSMxdSCExzX+o\/R4nZ+ely5YRYAy5\\nmJqafva\/\/757HVIrTfP\/IdiY6y+qq0Q6rNPqICxiFhpYFZnUthYD5y9c0E5X2HUlbOjQ3n36vHud\\nU5RIsCVyleZ5qQ4Rl3UQVjIh4fk4JUnab+js7T1u\/HgCLDEQlq9c8e5FZlU28b3h5QwdVpqQCiu9\\nVJRXg\/9kRKNhad26AQDMmh3NYBic7x5+BHbv7u\/f8kQ\/rJKxKl8QbEkaHi3Wy2oiQl5RheU0sbbX\\nwsbGZviIEQRYYlAsXd7KgJImIjrT8a2cGhniANhIhXU3h4hMLLCijVlnRFQki0Vackey6NO3r5OT\\nU4uLlLYmzpgjlKmQJ7ZAKqzCWiIW5WCtm80wDE+KiCDADEODSqV+NHFCi4uwhIg1xRZkI47ViHRu\\ndVP3RX0UwFrngz2Dguzf9lgiHpXghFp0UcsNsNl0CnsQ5vWOHT9+5\/YdavWbngjSkJCW4V5OzUfd\\nbJDciUhYLyvF5UIi5iCQVnfWEaNGEmCDdjTyFLXwmJYbKKw+AGAvrA4dOvQMCoqLfbNR2Mp2D\/5U\\nId4pRiSsKjH5kWRodNrgsDCyrQAAdoAYAVpvwOscxKDBg5oLixSQZ4RAJCxDiILs69vV2tqabCsA\\nlbsAcBeQUnW\/kBAAviGl6iYyKjAdvCcUkuO72JzQ0FCyTSAZLy8vNptNrg0qNRAgiy+HqMWqM4BY\\ndZ28MYgGpj+qugMqgbYxFsxdBHPwGguGDR1y7sxZnApHSEa5qKdzyyhi74JIWHyyhUWj0VrdMiMe\\njSJHI7ml7Q6TMfjVHhgYSLqwXlZJMBPWS7LHWB09PU1MTMi1oRGqE8QM1nYDbItf5W6tedEQDMIB\\nd\/sQlo0NorUTAqCazwXmc8mqPSBQr5hVmFCAbKkc0eCd9GSW3fzIP2xOInm5uc+Skhpclkk\/O4kw\\nzF77iHFga4tj\/2KwSCSSixcuTI2aPPg\/A9PT0houurm5kWuVGllMhvbhLkej0cg2gVAqKipOHj8R\\nc\/RoUWHL+K5OLs7Z2WSmL0AY6rJtYUnwz3jeJtYGM8bCG4lE8v3WrTFHjkqlrQ9l7OxIPkRZK8Fo\\nHYv0tQYAAJfLJdsE3MnLy1uz6stH\/7R0Znd3d1+\/cUPTaguX2\/ZUH1cQ6qFtYRlCNEhDWWvAAZVK\\ndfP6jZijR+\/dvdv8OgzDYUOGRERF9g8JaX7dhOxgAgj10D7GWKRPhfCgurr67OnTMUdjXua8dTSX\\nx+ONHjt2yrSprY7T28tX0T6EhTq085d\/Z1\/F9JDtwj6Os4P1DW2anZW1e9euy5f+Fovf8pj19PKa\\nO2\/ekPChWlpoDPNL4kr7EJZIhNINN7daiu0OeqkAfQw0pVJ58fyFYzFH45\/EN9cHlUodNnz4lGlT\\nu3fvDrUV5FIkJNojGR1tC8sEVWxTbOHzsc+GTSTV1dVHDh0+deJEQUFB8+s8Hi8iKnJSZOS7Lu3v\\ng89HE18PQxDqoW1hmTLJb9XKyvQNRjKzpz266L8NJBTWxeajPAi+etWqvy9eqqt763Hfrl0jo6KG\\njxxhZqZbttzSUoLisrwPHhuRHsgXDRJk71nUQc6m4R72emQS2HwrD7WwYo4cbfoZgqCh4eFz58\/3\\n80eZXutVPsm51syRNTTtQ1jV1SScSMEWM3Pz2XPmTJ4y2YLH06ecFlNI4oEpiLztEQnLnAnzSc1Z\\nlfwsmcTa9aRnUM\/IqKiRo0frv1Igk8kUCpLPH7QWtKsVEAnL3ZJFbsrn4iKUuTc6mDO62HIaUuvo\\nY4AVh9ZQjhVH513Lo8eP61N1c1JTUrAqCjUOyEYUiITlYckmV1g5OTkymQxFyIbvRnl+N6plilQU\\nzAl2nKP38pX+vMjMJNsE4GGJ6CQ6ImEhnAjgh0wmi330KHTAAMxLlkqlL3NyGg6C0mg0Ty8vAtKl\\nFBYW1tY0HgB2cHTkIR51paam4mkXItwxFBabRv5SVkZ6OrbCevjgwYF9+588fiwQvFlBtbGxCRkQ\\nOmPWrKZc9hhSXV198MCBy39fzs7KaroIQZB\/QMC48eMmToqg0dvoZ69eJiFIZAu8bThIbkP0dvo7\\nkr8HfO\/uPayKqigvX7p4yYyp027dvCkQCKhUqourq6ubG5VKLS8vP3n8xISx47Z8++37HFfQcfHC\\nhWFhQ37asbNBVRYWFp5enjweT6PRPE1MXLt6zbgxY5KfPWv12ZI6WWM7V0vy6igEAJeNaJSJqMXy\\nsUUkUlx5lpRUV1en63Liu7zMyZkxdVpJSQmDwZgwaeL0mTPNzMzOnjmjUqpGjRktkUgOHvjr+LGY\\n3X\/sSkxI\/H3XH3quDjTw7Tff\/Ll7DwDAz98\/Iipy7LhxTXk0FArF6VOnYg4fSUlJmTBu\/A87to8c\\nNar5s2dSyiMPpu6N8FanthHwlwA6WiEN9YOoxTIjb\/F9dqTgt01VENBIpdLrV6\/pWVpeXl7kpIiS\\nkpKQ0NCbd++s37DBxcUlcuLELZu+3fbddyPCh1lZWa3f8PWtu3eDe\/dKiI+fEhkll+ubIOnrdev+\\n3L2HzeHs+PmnU2fPTIqIaJ6dhUajRURGnrlwftPmzVQqddnHS4\/HxDR9uvZKzvj9KUq1Rq7S3Lh2\\nXU9L9KeHE1KnHUTC8rThcFkkaGvx9LoVc4QhQfIfv6qBIM2li9rCvCCBTqezmMyZ0bP2HthvZ2fX\\nMNLKz2tcyxYKBNevXgUA2Nvb\/3Xo0JixYyEKRf8O0dLS0tzc\/PS5syNGajvIOiky4ujxY6ampqrX\\nmWvUAET8lbLhep4ZA742P2CkKzU+Pl5PS\/THgoV0tQWRXCiv8+WfTiE06dfi6XWLpjfu5Hu5KV0c\\nlU8eP66qrLTUI3Wqg4PDqbNnrJrFgHBxdaXT6U3NkrOLS8MPMAxv+\/EHkUikv4\/h4o8\/njJ1KtfC\\nos07\/fz9H8Q+4nA4ZQL58N1JiUUCPweTmGm+nW04e\/7YJZORn5y8rytS\/1WkU2snLqFhP0eHiRdO\\na1RVVS20aLVFXiFNIpFc1LvRsno7soirq+vylSs4HA6DwZg1Ozq4V6+mjyAIwspzFYmqGuBw6oez\\n4\/clJxYJLNnUMzO7dX49Czt96hQmluhJZ8SjbaTCCvVA+tXoj7uzYu0n\/IatA6kMTF9umVfY2AIf\\n2Lcf8z2NufPnP05MiE96+uWaNdiWjJqTM7t6WLKqxMqPDiQX82WJCYkvXhAdyvZdGFTIG3NheVoR\\nFOcEhjVbV9Wy\/j9l3+qt5nkFb\/r1V\/n5N65jP4ZlMBgGFdrU3oyRsCJopLfl0yJhjx8ff\/bjfrIt\\nqmeAhwUL8YomUmF525o4mhGRaXJ0mKRzx8ZzILcfMS7faSnovXv2NI+Y+KFizqQem951RahTiUB+\\n2zJc0AFNYmZs8bXTYdUJqbBgCvDRpVx0MBia6EmNm5JyOdj6eyuT26eJT69euYq3JYYAmw5\/P9or\\nnJaloVCVLOKGIu9jaCdL5DfrsC\/2UVfcT42G9hC6Ozf658RcYOcXtT65\/XnnTrwtMRD+efgwK2aL\\n85UvLLL0XcPTEwgCvvY6TGV0EFZvxFNN1Ezo9ibfyZkr7x30ZGZk7NlFTq5RIlEoFN9u\/AYAQCMj\\n8nYLBna00MkFVwdhdXMwdeW1zIONITCkGuka1\/BzYirtRa62Id3O7dtLiovxM8YQ2L93X3p6Gwlg\\nCKOfm26H0XVbT\/+Ph8W+6hIdTUJKkE0GldI4Kk\/JaGOFVywWf7pi5eGYozgZQwxqeYZaeF6jyNYo\\nXwF1HQTbAKoDhdkLZg8qKFbt+PFHsg18w0gf3daldRPW1O52+57gJaxQxzf+x0+etT0DjYuNPX3q\\n1PiPPsLJHlxRS5+oqjepxZebX2w4aqiu26uE6JkJ7mymSCol32Hp9fIH3d9Rt+1\/3ZzaermYmzPx\\n+lU7mLzJOlRWiaiWLz\/\/orlvUztBoaz8QlE4sIWq3kIj7x+YcWlfxZAQ8gOh17\/z7lyqju6Put3O\\npsNhXhi4kbQO9N7\/vA+FQrFk0eKKCkI3MfVCLZUXR6lqfwSg7f0DM1PN1i9royeRHwt9fm+d3bJ1\\ndsNFmEqFMLKzslYuW65Ukh9rCQmKypUasQ7bnVQYLJstDA8ls91yMKP3cdU5jJTOwprgZ2tnSsQS\\nPHL+efhw7ZerybaibVSCE+q6P3V9CobBuhV8SwvSjt+N6WJN17UjRCEsKgUa44tLuhil+o0xpia6\\nfY\/Hjx37et06HIzCDrVIWbUW3aOmHM2SGaR1iONR9VFoTqRMCbRD8VSbpFa9CQfVK0Bnv82\/9h\/Y\\ntuU7rI3CDJXwIlDmon58fLjEzpqE7r6LLWcwqlE1GmH1d7fwssbeF+Dqq+5NP3u6ofkSf\/\/tt7Wr\\n16hV5AdNfReV6Lw+j1OpYEAvEhz95gSjTBCJ8gzd8hBndA9qoUBoWyZuHCSGBMnsrNHo48ihQ4sX\\nLRIKyTxe2yptJEpBgI8X0YfrYQhM64Eyli5KYc3sac\/UfUDXJmdeNoZwhWEQPgDlVOj61WsfjR2n\\nf+QjjFHrm6LWxZHornB2sIOl7iEFGkApDiYNnhOMfVzo\/Sn\/afp5yliRqQlKv6uc7OzhQ4ZeuqCX\\nH7NYrhLK6v\/pU0gDapVQqGAKFUypEn3AeksLor3QPu6HNBzcu6BvdT4f5IZ59ti0XM6dR41b6A62\\n6qjR6MMi8vn85Z98surzL1B3iyG\/JJiuumO66o5K7z9oeoXGas8pqz2nIq+uQl2IWEJott5hnXk6\\n+cm0AL2wHM0Zk\/wxXixV0Tnb95oq\/7+NmDdZ6OyAvv1Xq9XHY2LGjBx55\/Ztfayqk+o7uGnK5kCB\\n0Is0v5DQE3irBuuVW0WvcdK3Izrq8\/i7qBimWbm0\/ScaXVVZTLBjXTUV1itOcH5e\/pxZ0XOjZyPY\\nVVQCIH99pA+83r9q\/HKuvdDXHepKRlXDD\/Yc9EUVlRG3Id3L2UxXP5kW6CUsNx4rKgDT9ElUhgpm\\n7NxnmpLR+HZ6uat+2VjdPGy9h4fH77t37T1woE\/fvsgLvn3rVnjYkGUfLy0pafDOkKtEV5UVKxUl\\nkbK8TrJs5ut\/JrJsM1k2u\/7nHPN+vEMNzy46lXEtswr173QsqWzzrcYzsSH26ANc3biPozNcCzbr\\n3WRAesYNzygT+X0fJ1dhFnzc6fIXdHGFvY1y95ZqN6fGHuTvW8zV27gyef0g4+DhQ71fS0omk4X2\\n7VdZWYm8cEsLVXiodOFsDwtWUps5Fh6Vdh589juVprGdgCBgb0qnNAtoZ8aEfe1MhnlbTgm0o8GU\\nrArxTw8KnhYJCmplqtfJ19QaTUmdvKkaDlXyfMocOzaawB5PntFmrnxrw6POLaQicDqKotpkYEeL\\nmwv1TYyob7fd2ZYzr5fjzw9bJqlCjYrNBeKKknLq0rUWf2yudrCt75gCfOUOtsrc1+fAmP9\/TovB\\nYAwcNOj4MW0Zmpvo6KqYPEY8Llz8OlLQUySP9LbLmO1zedfzxnPxGg0ornt7P4AP0srEx5+Vv6yS\\nTPSz7fNTvPYp5Ajz8+hUpdaAXw8Sl+nkqyEYZK7DYC1q1WBXDGPBK9iN7+XLAtr8z3kl5ZQaPrRg\\nFS\/3\/08X7ty+XSJpXOKi0dveDre3UW78tPbUH5URo8RtxZ9qyQ\/9\/ljc9RyH2saK2jc38uafSNei\\nKopMwEs9lXn4ak4+mjf5xEXW4ySCTqJH+NuEYHE4GYOJhr0Z438DXdZceal\/UfXAb\/74LwtokxZa\\nmXA0r4rf2Hn\/3v2Tx49PmzGjzTDdNJpmyljR4ulCNgtlT02lqL\/vt+t\/gcfSa5wzapyqpGYaTWNX\\nqAGgSmp2vaB7Nt9RrQGPXgfrhpQyVkUGvbYA0rwWmUYDywT0uiIGv4CilAkBZeGXFod3VFlb6jA3\\njHtK3\/IbQRm\/WDTKhnAPTIrCZga7ItR5\/5MShGmotaOivjVErebD1e9kpTDnWjR4+V1\/\/6loF0fl\\n1ytre3TDYBvEhs23YaeEOrYy7lao4YmXV195FQRey8jhzhYm\/5WWoopKqVM+sfxpfU0nD0TLKBdv\\nMr\/eYd4wuCSA\/\/7HxdMamyPv2AiLTYe3juo4fj8GMX0VHOv3fdS7T28nJ+d+If2HjxhRP3PZtIn\/\\nngh3vQJl276ssTDHPZ8RjaL6LPCEs2lFQgrtZZpQu6oaKCqlzlhpOWuicE6kSEt87vJKyva9pueu\\nsRA60+qPE5fx6QDMtoD1nRU2Z\/Kh1KNP9d2hY1a+cLzbuvfL2Qvnfbt2BQBUVlRu2rjx\/Llzrd42\\nOkz87f+Izr0jkYLpyy3TsnRwgTThqKMnCXsFyrt1VjTNNWVyEJvIuBvLOH6J3dTttgrms8KbCwIG\\nemLmd46lsAprpQE\/PK4U6dX70GvynW5taPWjhYsXWfB4D+7de3D\/wfvCN0SMEq39BGVuEj1RqcC0\\nZZbP0tG417JZag5bU1tHUSiQtk\/YCmtyoO3hKb5YlYaxsAAAB+NLph9N06cESCF1O78EXes\/anB9\\nW4UwdQIeCITQvC94yai0pSsYCsvRjJG4MsjGBEuzMXZ9mdbDXk\/HZQ2NqTBB46Havats\/QoyVfXa\\no1rz84ZqF0eSs5Loyh8TO2OrKuyFBQDYPdHbxgS9c8jrHUOdN9XtrFXbv6phGMAhD0uuZuuqWiaj\\n3QRaig6yH6HjKWckYC8saxO6nr211FK3jSoqrNn6ZQ2Payg5bbt0Uq6cS\/5hQCR4WDJ\/Ht8Jj5Jx\\nSe8x2Iu3JswV9eMS68463T9ppDjQ17B6n4jR4uAALPMP4AGLRjkzyw95kD6dwCtvzBeDXAd7otwZ\\nUHJ0aJmteaolM9E3D2K913SFoubDusY\/EkwB\/50vYNB1a0RRZKHShz2TvLvq4cqnHbyExaLB+6N8\\nEGaKaoHCxFbBQrqgMm+y0NwUZSf4LI02bIbNzYfo\/5z7jnPGz7fKL6rXE9X2L6rN701faeeOytFh\\nYuRF+XTpcvn6tb79dPAF0ofFfR0n43OMrwEcM105mjPPzOrGoeteBQQpTBC5eXHNVJGjdfjjNaeq\\nBprzP15lNbz0K4vLt3V2ddJowE\/7TbbtMisqpc5cYQm438Cmk2CzaVT7kwBqbAbmRgnbdM5pILhX\\nr5gTx52dnXfv3dvFF8v1pFbp78b9YYwXrlXgm0Ktq73J4Sm+KFYAxPbdkNw2f4oQdRI4SwvNgqkN\\n7vDQp99YfL7ZHHmA\/tIKSvSnvN8PNbqyzJwoBMI\/gaq6vhPkDKc5xQF6vf2OdupxQ9vua1d+9tnh\\nmKNsNhsAEPvoURbOkbfdecwzs7rSYXz\/9Ljn5hvja71lpM7uiFKee5v3UGHN+GF6DZFmR4g+ia6D\\nKfWNyoUb7CFTbY6cY\/PrtL0IxWWUnftMhs+wefyM8bpt1Xy+iD9jghgoX8oLBwNlaf13SvdgOMfC\\n1jsAzWPCiPc2qAwGY9z48ddu3li4eFHDlevXri9esFD\/7D1asDGh3VoUaMnBfWEG45X39\/H5xawt\\nt9veoH2DRuN8ZRVNrC0+0eC+kh3rMUizdvwia\/OvbzwITDjqIf2lQX4ydxcVk6GhUTVCMSSRQk+e\\n0VMy6HfjGE1beEyGZv3y2pGDm83+qB40+yMURlOGerVKeOnbHxLjnyQIBHXVVdVMJtPaxsbe3r5P\\nv77Dhg+3sXlzGuXk8RNfrVmDPK8JipV3Dh2+Pt+\/t+6hY1BAkLAAAFGHUmN02aK2jt9vlv9Ayw2b\\n\/lszZgg2U\/rMHOoXW8wzX+rwHnu5KTZ8Wuvb6R3vF8iEarkR5s5B7jnC5\/N\/3Pb9oYMHdbBYd2Gx\\naZQjU33H+L7XeQRbiBOWSg0iDyafTEYaJI1TmGAX99v7P9c8vVyqq0eoFtRqcOEm6\/eDJs2dClvF\\n3kY5f4pwzBCJltoheleq5UYKZ5B2eUml0nNnzn6\/bVt1lc6HNXQSFgRAzDTfSf6YnnzRCnFH1WAK\\nODzFV6ZMuZCG6PiDyMFPyeRSpa13dl07KzBUVf3AiALGhElGDpQkZ9Cu32dm5VKT0uhiSeMYlGeu\\n8vFSuDsrB\/aRBXaVtznw1chTFCVjANUVNp0EMfwlmkEmpm+8QIsKC+Pi4uJiY2\/duFlTo+\/R+zaB\\nADg+3XeCH3GqIlRYAAA6lXJiRtfIgylnUxFoi0IV2fuZ595t9cPOHrgstcMwCOiiCOiiaGjDauvq\\nR1cmHA3KpTJlnqrmu\/pZwnLLogpnCx5PrVaXFBfX1RHn2ANB4ECkD8GqImJW2AIGlXJierepyJbm\\nhI7d3\/cRujhHOkGhAB5X42inRr0A24S7s7K0tDQ9LS0zI4NIVVEp0MnpXVFHjNEHooX1epkAOjCl\\ny+ygtn9bqa2Pktn6OYIunoa1Oagdax4JIbtM6PDfc\/zQxePTHxKE1VDrngifTcPbXqyq9Rza6nU3\\np\/YRzbYB4q3lsakPlnQP0yWtEraQI6wGvhjktjfCm03TZkOdW381pZWBoLmZoTjJIMEWVRA51Pg7\\nmCStDPZzJO6M67uQKSwAwKwgh2vzA7Q4BmpoLIFTL2KNwh5TDnGvwfDOltfnBzhxiQv00CokCwsA\\n0NeNm\/xpr27v99+o6TJWQ9QRKJwgzGF69WDXC3P8rbD2M0YB+cKq7ylM6XGf9JzXq\/U4qioWV+Dc\\nvhstKf5hablM6rlZ3TYM86AYxjtoEMJ6HXuS8sdE74OTfXjsVkZUtZ2Ga6C3TMVzoxZ7qmqw99JU\\nN\/vbBTmbJa4IGk3Udg0SDEVYDUztbv9gSY8+72TcVJjZ891Cm18pLDWItFgIKSzBQVg0VkMnu3qw\\n662FgW6ofCrxw7CE9TqrOefhxz02hLvT4bfa9NrOw1W0N2EFMnIw3dDBmZcF2O9wqJjm7pbM+4u7\\nbxjmwcEu2g9WGJywGlgd5pa0Mrhvs6ZLxbKo7jK26b\/J6e1JWDV8zL9naNmIwLT\/9u6rX0BH\/DBQ\\nYTU0XQ8+7vHLeK+mUON1riEy08YBfjGBATn1J+4pltM0H7+A0xcurJ8\/kYFDqH2sMFzLGljU1+n5\\nZ8GzguxhCgRgakXgdA1UL6l\/4gk90KIPFVUUvgCb18CCx1v99cbz505369oFkwLxw9CF9XoxgrE3\\nwufhku4DO1rIrDrWufYDAEhklLQsQl0zUHMvDoN3gMnmTJkx89qt2zOnT8HCKNxpH38bAECwi\/nN\\nhYE3s6qXnYD5RzLpwtIzV9g+nuQEltGJK\/f0WgSnUKnhI0Z99tlKpw46pzklEeI8SLFCrQaXHzzZ\\ntW1TWUHC3RPlZJvTBlU1lJCJNuiCp8FU2pjx42fPndvJE5vwjUTS\/oTVgFqtvnX7jrPJZhfLe2Tb\\noo29xzjf79Yt\/3t9P0JnTIyMmhU9y90V+yxrxNBehdWEVJSqqNpKkZykwgaXplClAn3H2wpEOgxk\\nbRw6RERGzps\/l2UIoXP0oN0LqwGVUiSu+AkSHaSBHLJteUPMedaGncjWmSAoZOCgydOmDx7QH3ez\\nCOEDEVYTgpoEWH5Jzf+FRiE6DGkLpDIQPs2morqNhQbvbv6TJ0cNGxbONde5xzRkPjRhNVFXeRWS\\n3wOSK1TVc9TH8PVhy2+mf51q3RfIxNSsq5\/f4LCwoeHhdrYGtHOMIR+ssJoQ8p8D6TW1+AakTKND\\nJcRU+iKXGrnYqnl8dogCe3h6+gd2Dw7uGRYWZsLBJpy6wfLhC6s5UmGyVBgPSf6mQ3lKSTqdhst4\\nXyyBJi2yzC2gmfOsnJw6dPMPCAsb7Onp+aE2Tq3y7xJWc6RSPlWTIxZmaqSJGlkCiy4W8DNNWSij\\njKhUQKp2YHNsJQrH\/MretfJe9ra2HTu6w6R0wwbAv1dYrVInKGFSSqmwhi8oA6pKJk0sEpZSNLVQ\\ns8yoSjWDzrCm0y3FcjbLxJFJ54hldCrTncn4wHs3nTAKywgu\/EsbaiN4YxSWEVwwCssILhiFZQQX\\njMIyggtGYRnBBaOwjOCCUVhGcOH\/AgAA\/\/+S+QX6OV9s5wAAAABJRU5ErkJggpQu\\n\",\"__pickled_extra_fields__\":[\"thumbnail\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_functions": [ + "send_document(42, <>, caption=\"Here's your document with tumbnail! Run /start command again to restart.\", disable_notification=None, reply_markup=None, parse_mode=None, thumbnail=<>, filename='deeppavlov_article.pdf')" + ] + }, + { + "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 9, 39, tzinfo=UTC), delete_chat_photo=False, entities=(MessageEntity(length=6, offset=0, type=MessageEntityType.BOT_COMMAND),), from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=9, supergroup_chat_created=False, text='/start'), update_id=9)", + "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_functions": [ + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))))" + ] + }, + { + "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 9, 57, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=13, supergroup_chat_created=False, text='some text'), update_id=13)", + "received_message": "{\"text\":\"some text\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_message": "{\"text\":\"Bot has entered unrecoverable state :\/\\nRun \/start command again to restart.\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_functions": [ + "send_message(42, 'Bot has entered unrecoverable state :/\\nRun /start command again to restart.', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None)" + ] + }, + { + "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 9, 59, tzinfo=UTC), delete_chat_photo=False, entities=(MessageEntity(length=6, offset=0, type=MessageEntityType.BOT_COMMAND),), from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=14, supergroup_chat_created=False, text='/start'), update_id=14)", + "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_functions": [ + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))))" + ] + }, + { + "update": "Update(callback_query=CallbackQuery(chat_instance='-1', data='restart', from_user=User(first_name='AlexaTestnder', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), id='14', message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 9, 59, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Bot', id=16, is_bot=True, username='dff_bot'), group_chat_created=False, location=Location(latitude=58.43162, longitude=27.792879), message_id=15, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), supergroup_chat_created=False)), update_id=15)", + "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"restart\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_functions": [ + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))))" + ] + }, + { + "update": "Update(callback_query=CallbackQuery(chat_instance='-1', data='quit', from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), id='15', message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 10, 1, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Bot', id=16, is_bot=True, username='dff_bot'), group_chat_created=False, location=Location(latitude=58.43162, longitude=27.792879), message_id=16, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), supergroup_chat_created=False)), update_id=16)", + "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"quit\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_message": "{\"text\":\"Bot has entered unrecoverable state :\/\\nRun \/start command again to restart.\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_functions": [ + "send_message(42, 'Bot has entered unrecoverable state :/\\nRun /start command again to restart.', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None)" + ] + } + ] } diff --git a/tests/messengers/telegram/test_tutorials.py b/tests/messengers/telegram/test_tutorials.py index d497e25f0..7e55ee49d 100644 --- a/tests/messengers/telegram/test_tutorials.py +++ b/tests/messengers/telegram/test_tutorials.py @@ -1,21 +1,42 @@ from importlib import import_module +from json import loads from pathlib import Path +from typing import Dict, List import pytest -from tests.messengers.telegram.utils import MockApplication +from dff.script.core.message import Message +from tests.messengers.telegram.utils import MockApplication, PathStep from tests.test_utils import get_path_from_tests_to_current_dir dot_path_to_addon = get_path_from_tests_to_current_dir(__file__, separator=".") happy_paths_file = Path(__file__).parent / "test_happy_paths.json" +def _cast_dict_to_happy_step(dict: Dict) -> List[PathStep]: + imports = globals().copy() + imports.update(import_module("telegram").__dict__) + imports.update(import_module("telegram.ext").__dict__) + imports.update(import_module("telegram.constants").__dict__) + + path_steps = list() + for step in dict: + update = eval(step["update"], imports) + received = Message.model_validate_json(step["received_message"]) + received.original_message = update + response = Message.model_validate_json(step["response_message"]) + path_steps += [(update, received, response, step["response_functions"])] + return path_steps + + @pytest.mark.asyncio @pytest.mark.parametrize( - ["tutorial_module_name"], - [("1_basic",), ("2_attachments",), ("3_advanced",)], + "tutorial_module_name", + ["1_basic", "2_attachments", "3_advanced"], ) async def test_tutorials_memory(tutorial_module_name: str): + happy_path_data = loads(happy_paths_file.read_text())[tutorial_module_name] + happy_path_steps = _cast_dict_to_happy_step(happy_path_data) module = import_module(f"tutorials.{dot_path_to_addon}.{tutorial_module_name}") - module.interface.application = MockApplication.create(module.interface, list()) + module.interface.application = MockApplication.create(module.interface, happy_path_steps) module.pipeline.run() diff --git a/tests/messengers/telegram/utils.py b/tests/messengers/telegram/utils.py index a95f5414c..8294d8e30 100644 --- a/tests/messengers/telegram/utils.py +++ b/tests/messengers/telegram/utils.py @@ -1,27 +1,49 @@ from asyncio import get_event_loop -from typing import Any, Dict, Hashable, List, Optional, Tuple, TypeAlias +from typing import Any, Hashable, List, Optional, Tuple, TypeAlias from pydantic import BaseModel +from re import sub from telegram import File, Update from telegram.ext import ExtBot from dff.messengers.telegram.abstract import _AbstractTelegramInterface +from dff.pipeline.types import PipelineRunnerFunction from dff.script import Message from dff.script.core.context import Context -PathStep: TypeAlias = Tuple[Dict, Message, Message, Tuple[str, Tuple, Dict]] +PathStep: TypeAlias = Tuple[Update, Message, Message, List[str]] -class MockBot(BaseModel): - _original_bot: ExtBot - _latest_trace: Optional[Tuple[str, Tuple, Dict]] = None +def _compare_messages(mess1: Message, mess2: Message) -> bool: + if mess1.text == None or mess2.text == None: + return True + m1 = mess1.model_copy(deep=True) + m2 = mess2.model_copy(deep=True) + m1.text = sub(r"`\d+`", "<>", m1.text) + m2.text = sub(r"`\d+`", "<>", m2.text) + return m1 == m2 + + +class MockBot(BaseModel, arbitrary_types_allowed=True): + original_bot: ExtBot + latest_trace: List[str] = list() async def get_file(self, file_id: str) -> File: - return await self._original_bot.get_file(file_id) + return await self.original_bot.get_file(file_id) + + @staticmethod + def representation(any: Any) -> str: + if isinstance(any, bytes): + return "<>" + else: + return any.__repr__() def __getattribute__(self, name: str) -> Any: - def set_trace(*args, **kwargs): - self._latest_trace = (name, args, kwargs) + async def set_trace(*args, **kwargs): + joined_args = ", ".join([self.representation(a) for a in args]) + joined_kwargs = ", ".join([f"{k}={self.representation(v)}" for k, v in kwargs.items()]) + arguments = ", ".join([joined_args, joined_kwargs]) + self.latest_trace += [f"{name}({arguments})"] try: return object.__getattribute__(self, name) @@ -29,40 +51,46 @@ def set_trace(*args, **kwargs): return set_trace -class MockApplication(BaseModel): +class MockApplication(BaseModel, arbitrary_types_allowed=True): bot: MockBot happy_path: List[PathStep] - _interface: _AbstractTelegramInterface - _latest_ctx: Optional[Context] = None + interface: _AbstractTelegramInterface + latest_ctx: Optional[Context] = None @classmethod def create(cls, interface: _AbstractTelegramInterface, happy_path: List[PathStep]) -> "MockApplication": - mock_bot = MockBot(_original_bot=interface.application.bot) - instance = cls(bot=mock_bot, happy_path=happy_path, _interface=interface) - interface.pipeline_runner = instance._wrapped_pipeline_runner + mock_bot = MockBot(original_bot=interface.application.bot) + instance = cls(bot=mock_bot, happy_path=happy_path, interface=interface) return instance - async def _wrapped_pipeline_runner( - self, message: Message, ctx_id: Optional[Hashable] = None, update_ctx_misc: Optional[dict] = None - ) -> Context: - self._latest_ctx = await self._interface.pipeline_runner(message, ctx_id, update_ctx_misc) - return self._latest_ctx + def _wrap_pipeline_runner(self, runner: PipelineRunnerFunction): + async def wrapped_pipeline_runner( + message: Message, ctx_id: Optional[Hashable] = None, update_ctx_misc: Optional[dict] = None + ) -> Context: + self.latest_ctx = await runner(message, ctx_id, update_ctx_misc) + return self.latest_ctx + + wrapped_pipeline_runner.is_wrapped = True + return wrapped_pipeline_runner def _run_bot(self) -> None: + if not getattr(self.interface.pipeline_runner, "is_wrapped", False): + self.interface.pipeline_runner = self._wrap_pipeline_runner(self.interface.pipeline_runner) + loop = get_event_loop() for update, received, response, trace in self.happy_path: - if update["is_message"]: - update = Update() - loop.run_until_complete(self._interface.on_message(update, None)) # type: ignore + if update.message is not None: + loop.run_until_complete(self.interface.on_message(update, None)) # type: ignore + elif update.callback_query is not None: + loop.run_until_complete(self.interface.on_callback(update, None)) # type: ignore else: - update = Update() - loop.run_until_complete(self._interface.on_callback(update, None)) # type: ignore - assert self._latest_ctx is not None, "During pipeline runner execution, no context was produced!" - assert self._latest_ctx.last_request == received, "Expected request message is not equal to expected!" - assert self._latest_ctx.last_response == response, "Expected response message is not equal to expected!" - assert self.bot._latest_trace == trace, "Expected trace is not equal to expected!" - self.bot._latest_trace = None - self._latest_ctx = None + raise RuntimeError(f"Update {update} type unknown!") + assert self.latest_ctx is not None, "During pipeline runner execution, no context was produced!" + assert _compare_messages(self.latest_ctx.last_request, received), "Expected request message is not equal to expected!" + assert _compare_messages(self.latest_ctx.last_response, response), "Expected response message is not equal to expected!" + assert self.bot.latest_trace == trace, "Expected trace is not equal to expected!" + self.bot.latest_trace = list() + self.latest_ctx = None def run_polling(self, poll_interval: float, timeout: int, allowed_updates: List[str]) -> None: return self._run_bot() diff --git a/tutorials/messengers/telegram/2_attachments.py b/tutorials/messengers/telegram/2_attachments.py index b8604b3f9..5b821a393 100644 --- a/tutorials/messengers/telegram/2_attachments.py +++ b/tutorials/messengers/telegram/2_attachments.py @@ -80,10 +80,10 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) animation_data = { # For some reason, if we don't define filename explicitly, animation is sent as file. "source": HttpUrl( - f"{EXAMPLE_ATTACHMENT_SOURCE}/hong-kong-simplyart4794.mp4" + f"{EXAMPLE_ATTACHMENT_SOURCE}/hong-kong-simplyart4794.gif" ), "title": "Hong Kong skyscraper views by Simplyart4794", - "filename": "hk.mp4", + "filename": "hk.gif", } image_data = { diff --git a/tutorials/messengers/telegram/3_advanced.py b/tutorials/messengers/telegram/3_advanced.py index 9d7cdc362..f3a52d5e1 100644 --- a/tutorials/messengers/telegram/3_advanced.py +++ b/tutorials/messengers/telegram/3_advanced.py @@ -14,6 +14,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) # %pip install dff[telegram] # %% +from asyncio import get_event_loop import os from urllib.request import urlopen @@ -47,7 +48,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) 3. Multiple attachments of different kind handling. 4. Image with a spoiler. 5. Document with a thumbnail. -6. Raw representation of different data user can send to the bot. +6. Attachment bytes hash. Last option ("Raw attachments!") button might be especially interesting, because it shows how bot precepts different telegram attachments sent by user @@ -88,31 +89,15 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) } -# %% -formatted_request = r""" -Here's your previous request\! -```json -{} -``` -Run /start command again to restart\. -""" - - -def stringify_previous_request(ctx: Context, _: Pipeline) -> Message: - dump = ctx.last_request.model_dump_json(indent=4) - return Message( - formatted_request.format(dump), parse_mode=ParseMode.MARKDOWN_V2 - ) - - # %% def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: + loop = get_event_loop() atch = [ a for a in ctx.last_request.attachments if isinstance(a, DataAttachment) ] if len(atch) > 0: - atch_hash = hash(atch[0].get_bytes(pipe.messenger_interface)) - resp_format = r"Here's your previous request hash: `{}`\!\nRun /start command again to restart\." + atch_hash = hash(loop.run_until_complete(atch[0].get_bytes(pipe.messenger_interface))) + resp_format = "Here's your previous request hash: `{}`!\nRun /start command again to restart." return Message( resp_format.format(atch_hash, parse_mode=ParseMode.MARKDOWN_V2) ) @@ -162,11 +147,6 @@ def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: callback_data="thumbnail", ), ], - [ - InlineKeyboardButton( - "Raw attachments!", callback_data="raw" - ), - ], [ InlineKeyboardButton( "Attachment bytes hash!", @@ -191,7 +171,6 @@ def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: "attachments_node": cnd.has_callback_query("attachments"), "secret_node": cnd.has_callback_query("secret"), "thumbnail_node": cnd.has_callback_query("thumbnail"), - "raw_init_node": cnd.has_callback_query("raw"), "hash_init_node": cnd.has_callback_query("hash"), "hmmm_node": cnd.has_callback_query("restart"), "fallback_node": cnd.has_callback_query("quit"), @@ -221,15 +200,6 @@ def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: attachments=[Document(**document_data)], ), }, - "raw_init_node": { - RESPONSE: Message( - "Alright! Now send me any message and I'll send you it's raw data!" - ), - TRANSITIONS: {"raw_request_node": cnd.true()}, - }, - "raw_request_node": { - RESPONSE: stringify_previous_request, - }, "hash_init_node": { RESPONSE: Message( "Alright! Now send me a message with data attachment (audio, video, animation, image, sticker or document)!" From eb07fae9a7a3ae946f3adc92dfa95e689d1c694e Mon Sep 17 00:00:00 2001 From: pseusys Date: Fri, 24 May 2024 11:53:44 +0200 Subject: [PATCH 073/140] last test step added --- dff/script/core/message.py | 2 + .../messengers/telegram/test_happy_paths.json | 26 +++++++++++- tests/messengers/telegram/test_tutorials.py | 2 +- tests/messengers/telegram/utils.py | 40 +++++++++---------- tutorials/messengers/telegram/3_advanced.py | 5 +-- 5 files changed, 49 insertions(+), 26 deletions(-) diff --git a/dff/script/core/message.py b/dff/script/core/message.py index d982f9d0b..1abdd2964 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -162,6 +162,8 @@ def __eq__(self, other): if isinstance(other, DataAttachment): if self.id != other.id: return False + if self.source != other.source: + return False if self.title != other.title: return False return True diff --git a/tests/messengers/telegram/test_happy_paths.json b/tests/messengers/telegram/test_happy_paths.json index 0208daf7a..ba83ef773 100644 --- a/tests/messengers/telegram/test_happy_paths.json +++ b/tests/messengers/telegram/test_happy_paths.json @@ -179,7 +179,31 @@ ] }, { - "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 9, 39, tzinfo=UTC), delete_chat_photo=False, entities=(MessageEntity(length=6, offset=0, type=MessageEntityType.BOT_COMMAND),), from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=9, supergroup_chat_created=False, text='/start'), update_id=9)", + "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 22, 19, 53, 42, tzinfo=UTC), delete_chat_photo=False, entities=(MessageEntity(length=6, offset=0, type=MessageEntityType.BOT_COMMAND),), from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=9, supergroup_chat_created=False, text='/start'), update_id=9)", + "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_functions": [ + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))))" + ] + }, + { + "update": "Update(callback_query=CallbackQuery(chat_instance='-1', data='hash', from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), id='9', message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 22, 19, 53, 42, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Bot', id=6601664672, is_bot=True, username='dff_bot'), group_chat_created=False, location=Location(latitude=58.43162, longitude=27.792879), message_id=10, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), supergroup_chat_created=False)), update_id=10)", + "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"hash\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_message": "{\"text\":\"Alright! Now send me a message with data attachment (audio, video, animation, image, sticker or document)!\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_functions": [ + "send_message(42, 'Alright! Now send me a message with data attachment (audio, video, animation, image, sticker or document)!', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None)" + ] + }, + { + "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 22, 20, 12, 23, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=12, sticker=Sticker(api_kwargs={'thumb': {'file_id': 'AAMCAgADGQEAAgS0Zk5Rpz6PkZu6Ef3Ef7GwnxAZYocAAiwCAAJHBkMF0YsKl8FLgq8BAAdtAAM1BA', 'file_unique_id': 'AQADLAIAAkcGQwVy', 'file_size': 5416, 'width': 128, 'height': 128}}, emoji='👨', file_id='CAACAgIAAxkBAAIEtGZOUac-j5GbuhH9xH-xsJ8QGWKHAAIsAgACRwZDBdGLCpfBS4KvNQQ', file_size=29492, file_unique_id='AgADLAIAAkcGQwU', height=512, is_animated=False, is_video=False, set_name='citati_prosto', thumbnail=PhotoSize(file_id='AAMCAgADGQEAAgS0Zk5Rpz6PkZu6Ef3Ef7GwnxAZYocAAiwCAAJHBkMF0YsKl8FLgq8BAAdtAAM1BA', file_size=5416, file_unique_id='AQADLAIAAkcGQwVy', height=128, width=128), type=StickerType.REGULAR, width=512), supergroup_chat_created=False), update_id=11)", + "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"source\":null,\"cached_filename\":null,\"id\":\"CAACAgIAAxkBAAIEtGZOUac-j5GbuhH9xH-xsJ8QGWKHAAIsAgACRwZDBdGLCpfBS4KvNQQ\",\"title\":null,\"use_cache\":true,\"dff_attachment_type\":\"sticker\",\"is_animated\":false,\"is_video\":false,\"type\":\"regular\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_message": "{\"text\":\"Here's your previous request hash: `0`!\\nRun \/start command again to restart.\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_functions": [ + "send_message(42, \"Here's your previous request hash: `0`!\\nRun /start command again to restart.\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None)" + ] + }, + { + "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 9, 39, tzinfo=UTC), delete_chat_photo=False, entities=(MessageEntity(length=6, offset=0, type=MessageEntityType.BOT_COMMAND),), from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=12, supergroup_chat_created=False, text='/start'), update_id=12)", "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ diff --git a/tests/messengers/telegram/test_tutorials.py b/tests/messengers/telegram/test_tutorials.py index 7e55ee49d..dd55cd783 100644 --- a/tests/messengers/telegram/test_tutorials.py +++ b/tests/messengers/telegram/test_tutorials.py @@ -34,7 +34,7 @@ def _cast_dict_to_happy_step(dict: Dict) -> List[PathStep]: "tutorial_module_name", ["1_basic", "2_attachments", "3_advanced"], ) -async def test_tutorials_memory(tutorial_module_name: str): +def test_tutorials_memory(tutorial_module_name: str): happy_path_data = loads(happy_paths_file.read_text())[tutorial_module_name] happy_path_steps = _cast_dict_to_happy_step(happy_path_data) module = import_module(f"tutorials.{dot_path_to_addon}.{tutorial_module_name}") diff --git a/tests/messengers/telegram/utils.py b/tests/messengers/telegram/utils.py index 8294d8e30..06f603142 100644 --- a/tests/messengers/telegram/utils.py +++ b/tests/messengers/telegram/utils.py @@ -2,35 +2,20 @@ from typing import Any, Hashable, List, Optional, Tuple, TypeAlias from pydantic import BaseModel -from re import sub -from telegram import File, Update -from telegram.ext import ExtBot +from telegram import Update from dff.messengers.telegram.abstract import _AbstractTelegramInterface from dff.pipeline.types import PipelineRunnerFunction from dff.script import Message from dff.script.core.context import Context +from dff.script.core.message import DataAttachment PathStep: TypeAlias = Tuple[Update, Message, Message, List[str]] -def _compare_messages(mess1: Message, mess2: Message) -> bool: - if mess1.text == None or mess2.text == None: - return True - m1 = mess1.model_copy(deep=True) - m2 = mess2.model_copy(deep=True) - m1.text = sub(r"`\d+`", "<>", m1.text) - m2.text = sub(r"`\d+`", "<>", m2.text) - return m1 == m2 - - class MockBot(BaseModel, arbitrary_types_allowed=True): - original_bot: ExtBot latest_trace: List[str] = list() - async def get_file(self, file_id: str) -> File: - return await self.original_bot.get_file(file_id) - @staticmethod def representation(any: Any) -> str: if isinstance(any, bytes): @@ -59,8 +44,7 @@ class MockApplication(BaseModel, arbitrary_types_allowed=True): @classmethod def create(cls, interface: _AbstractTelegramInterface, happy_path: List[PathStep]) -> "MockApplication": - mock_bot = MockBot(original_bot=interface.application.bot) - instance = cls(bot=mock_bot, happy_path=happy_path, interface=interface) + instance = cls(bot=MockBot(), happy_path=happy_path, interface=interface) return instance def _wrap_pipeline_runner(self, runner: PipelineRunnerFunction): @@ -71,11 +55,22 @@ async def wrapped_pipeline_runner( return self.latest_ctx wrapped_pipeline_runner.is_wrapped = True + wrapped_pipeline_runner.original = runner return wrapped_pipeline_runner + def _wrap_populate_attachment(self, interface: _AbstractTelegramInterface): + async def wrapped_populate_attachment(_: DataAttachment) -> bytes: + return bytes() + + wrapped_populate_attachment.is_wrapped = True + wrapped_populate_attachment.original = interface.populate_attachment + return wrapped_populate_attachment + def _run_bot(self) -> None: if not getattr(self.interface.pipeline_runner, "is_wrapped", False): self.interface.pipeline_runner = self._wrap_pipeline_runner(self.interface.pipeline_runner) + if not getattr(self.interface.populate_attachment, "is_wrapped", False): + self.interface.populate_attachment = self._wrap_populate_attachment(self.interface) loop = get_event_loop() for update, received, response, trace in self.happy_path: @@ -86,12 +81,15 @@ def _run_bot(self) -> None: else: raise RuntimeError(f"Update {update} type unknown!") assert self.latest_ctx is not None, "During pipeline runner execution, no context was produced!" - assert _compare_messages(self.latest_ctx.last_request, received), "Expected request message is not equal to expected!" - assert _compare_messages(self.latest_ctx.last_response, response), "Expected response message is not equal to expected!" + assert self.latest_ctx.last_request == received, "Expected request message is not equal to expected!" + assert self.latest_ctx.last_response == response, "Expected response message is not equal to expected!" assert self.bot.latest_trace == trace, "Expected trace is not equal to expected!" self.bot.latest_trace = list() self.latest_ctx = None + self.interface.pipeline_runner = self.interface.pipeline_runner.original + self.interface.populate_attachment = self.interface.populate_attachment.original + def run_polling(self, poll_interval: float, timeout: int, allowed_updates: List[str]) -> None: return self._run_bot() diff --git a/tutorials/messengers/telegram/3_advanced.py b/tutorials/messengers/telegram/3_advanced.py index f3a52d5e1..a608c879c 100644 --- a/tutorials/messengers/telegram/3_advanced.py +++ b/tutorials/messengers/telegram/3_advanced.py @@ -90,13 +90,12 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) # %% -def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: - loop = get_event_loop() +async def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: atch = [ a for a in ctx.last_request.attachments if isinstance(a, DataAttachment) ] if len(atch) > 0: - atch_hash = hash(loop.run_until_complete(atch[0].get_bytes(pipe.messenger_interface))) + atch_hash = hash(await atch[0].get_bytes(pipe.messenger_interface)) resp_format = "Here's your previous request hash: `{}`!\nRun /start command again to restart." return Message( resp_format.format(atch_hash, parse_mode=ParseMode.MARKDOWN_V2) From 97be6b9bce3bebdc2013af09914dd10774a02289 Mon Sep 17 00:00:00 2001 From: pseusys Date: Fri, 24 May 2024 11:58:45 +0200 Subject: [PATCH 074/140] attachments migrated to wiki --- tests/messengers/telegram/test_happy_paths.json | 10 +++++----- tests/script/core/test_message.py | 2 +- tutorials/messengers/telegram/2_attachments.py | 2 +- tutorials/messengers/telegram/3_advanced.py | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/messengers/telegram/test_happy_paths.json b/tests/messengers/telegram/test_happy_paths.json index ba83ef773..9f88c12da 100644 --- a/tests/messengers/telegram/test_happy_paths.json +++ b/tests/messengers/telegram/test_happy_paths.json @@ -65,7 +65,7 @@ { "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 0, 46, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=6, supergroup_chat_created=False, text='audio'), update_id=6)", "received_message": "{\"text\":\"audio\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", - "response_message": "{\"text\":\"Here's your audio!\",\"commands\":null,\"attachments\":[{\"source\":\"https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/separation-william-king.mp3\",\"cached_filename\":null,\"id\":null,\"title\":\"\\\"Separation\\\" melody by William King\",\"use_cache\":true,\"dff_attachment_type\":\"audio\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_message": "{\"text\":\"Here's your audio!\",\"commands\":null,\"attachments\":[{\"source\":\"https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@wiki/example-attachments/separation-william-king.mp3\",\"cached_filename\":null,\"id\":null,\"title\":\"\\\"Separation\\\" melody by William King\",\"use_cache\":true,\"dff_attachment_type\":\"audio\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ "send_audio(42, audio=<>, performer=None, title=None, caption=\"Here's your audio!\", disable_notification=None, reply_markup=None, parse_mode=None, thumbnail=None)" ] @@ -73,7 +73,7 @@ { "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 1, 4, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=7, supergroup_chat_created=False, text='video'), update_id=7)", "received_message": "{\"text\":\"video\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", - "response_message": "{\"text\":\"Here's your video!\",\"commands\":null,\"attachments\":[{\"source\":\"https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/crownfall-lags-nkognit0.mp4\",\"cached_filename\":null,\"id\":null,\"title\":\"Epic Dota2 gameplay by Nkognit0\",\"use_cache\":true,\"dff_attachment_type\":\"video\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_message": "{\"text\":\"Here's your video!\",\"commands\":null,\"attachments\":[{\"source\":\"https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@wiki/example-attachments/crownfall-lags-nkognit0.mp4\",\"cached_filename\":null,\"id\":null,\"title\":\"Epic Dota2 gameplay by Nkognit0\",\"use_cache\":true,\"dff_attachment_type\":\"video\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ "send_video(42, <>, caption=\"Here's your video!\", disable_notification=None, reply_markup=None, parse_mode=None, supports_streaming=None, has_spoiler=None, thumbnail=None, filename=None)" ] @@ -81,7 +81,7 @@ { "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 1, 22, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=8, supergroup_chat_created=False, text='animation'), update_id=8)", "received_message": "{\"text\":\"animation\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", - "response_message": "{\"text\":\"Here's your animation!\",\"commands\":null,\"attachments\":[{\"source\":\"https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/hong-kong-simplyart4794.gif\",\"cached_filename\":null,\"id\":null,\"title\":\"Hong Kong skyscraper views by Simplyart4794\",\"use_cache\":true,\"dff_attachment_type\":\"animation\",\"filename\":\"hk.gif\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_message": "{\"text\":\"Here's your animation!\",\"commands\":null,\"attachments\":[{\"source\":\"https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@wiki/example-attachments/hong-kong-simplyart4794.gif\",\"cached_filename\":null,\"id\":null,\"title\":\"Hong Kong skyscraper views by Simplyart4794\",\"use_cache\":true,\"dff_attachment_type\":\"animation\",\"filename\":\"hk.gif\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ "send_animation(42, <>, caption=\"Here's your animation!\", parse_mode=None, disable_notification=None, reply_markup=None, has_spoiler=None, thumbnail=None, filename='hk.gif')" ] @@ -89,7 +89,7 @@ { "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 1, 35, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=9, supergroup_chat_created=False, text='image'), update_id=9)", "received_message": "{\"text\":\"image\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", - "response_message": "{\"text\":\"Here's your image!\",\"commands\":null,\"attachments\":[{\"source\":\"https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/deeppavlov.png\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov logo\",\"use_cache\":true,\"dff_attachment_type\":\"image\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_message": "{\"text\":\"Here's your image!\",\"commands\":null,\"attachments\":[{\"source\":\"https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@wiki/example-attachments/deeppavlov.png\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov logo\",\"use_cache\":true,\"dff_attachment_type\":\"image\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ "send_photo(42, <>, caption=\"Here's your image!\", disable_notification=None, reply_markup=None, parse_mode=None, has_spoiler=None, filename=None)" ] @@ -97,7 +97,7 @@ { "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 2, 19, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=10, supergroup_chat_created=False, text='document'), update_id=10)", "received_message": "{\"text\":\"document\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", - "response_message": "{\"text\":\"Here's your document!\",\"commands\":null,\"attachments\":[{\"source\":\"https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments/deeppavlov-article.pdf\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov article\",\"use_cache\":true,\"dff_attachment_type\":\"document\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_message": "{\"text\":\"Here's your document!\",\"commands\":null,\"attachments\":[{\"source\":\"https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@wiki/example-attachments/deeppavlov-article.pdf\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov article\",\"use_cache\":true,\"dff_attachment_type\":\"document\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ "send_document(42, <>, caption=\"Here's your document!\", disable_notification=None, reply_markup=None, parse_mode=None, thumbnail=None, filename=None)" ] diff --git a/tests/script/core/test_message.py b/tests/script/core/test_message.py index 1922b5bbf..ccd4216ab 100644 --- a/tests/script/core/test_message.py +++ b/tests/script/core/test_message.py @@ -27,7 +27,7 @@ Video, ) -EXAMPLE_SOURCE = "https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments" +EXAMPLE_SOURCE = "https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@wiki/example-attachments" class DFFCLIMessengerInterface(CLIMessengerInterface): diff --git a/tutorials/messengers/telegram/2_attachments.py b/tutorials/messengers/telegram/2_attachments.py index 5b821a393..d46ddfc87 100644 --- a/tutorials/messengers/telegram/2_attachments.py +++ b/tutorials/messengers/telegram/2_attachments.py @@ -48,7 +48,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) # %% -EXAMPLE_ATTACHMENT_SOURCE = "https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments" +EXAMPLE_ATTACHMENT_SOURCE = "https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@wiki/example-attachments" location_data = {"latitude": 50.65, "longitude": 3.916667} diff --git a/tutorials/messengers/telegram/3_advanced.py b/tutorials/messengers/telegram/3_advanced.py index a608c879c..3ac2a61e9 100644 --- a/tutorials/messengers/telegram/3_advanced.py +++ b/tutorials/messengers/telegram/3_advanced.py @@ -57,7 +57,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) # %% -EXAMPLE_ATTACHMENT_SOURCE = "https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@example-attachments" +EXAMPLE_ATTACHMENT_SOURCE = "https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@wiki/example-attachments" image_url = HttpUrl(f"{EXAMPLE_ATTACHMENT_SOURCE}/deeppavlov.png") From 33a5c73df0f6f76f9e0441023c1e6ee87b9eafcd Mon Sep 17 00:00:00 2001 From: pseusys Date: Mon, 27 May 2024 21:21:14 +0200 Subject: [PATCH 075/140] pragma removed, inheritance added --- dff/messengers/telegram/abstract.py | 10 ++++------ dff/messengers/telegram/interface.py | 24 +++++++++++++++++++----- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index 6c7e7be83..07d2738c5 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -45,7 +45,7 @@ telegram_available = False -class _AbstractTelegramInterface(MessengerInterface): # pragma: no cover +class _AbstractTelegramInterface(MessengerInterface): supported_request_attachment_types = {Location, Contact, Poll, Sticker, Audio, Video, Animation, Image, Document, Invoice} supported_response_attachment_types = {Location, Contact, Poll, Sticker, Audio, Video, Animation, Image, Document} @@ -58,7 +58,7 @@ def __init__(self, token: str, attachments_directory: Optional[Path] = None) -> self.application.add_handler(MessageHandler(ALL, self.on_message)) self.application.add_handler(CallbackQueryHandler(self.on_callback)) - async def populate_attachment(self, attachment: DataAttachment) -> bytes: # pragma: no cover + async def populate_attachment(self, attachment: DataAttachment) -> bytes: if attachment.id is not None: file = await self.application.bot.get_file(attachment.id) data = await file.download_as_bytearray() @@ -66,7 +66,7 @@ async def populate_attachment(self, attachment: DataAttachment) -> bytes: # pra else: raise ValueError(f"For attachment {attachment} id is not defined!") - def extract_message_from_telegram(self, update: TelegramMessage) -> Message: # pragma: no cover + def extract_message_from_telegram(self, update: TelegramMessage) -> Message: message = Message() message.attachments = list() @@ -196,9 +196,7 @@ def extract_message_from_telegram(self, update: TelegramMessage) -> Message: # return message - async def cast_message_to_telegram_and_send( - self, bot: ExtBot, chat_id: int, message: Message - ) -> None: # pragma: no cover + async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, message: Message) -> None: if message.attachments is not None: files = list() for attachment in message.attachments: diff --git a/dff/messengers/telegram/interface.py b/dff/messengers/telegram/interface.py index af294a62c..76106acb1 100644 --- a/dff/messengers/telegram/interface.py +++ b/dff/messengers/telegram/interface.py @@ -1,20 +1,28 @@ from pathlib import Path -from typing import Optional +from typing import Hashable, List, Optional, Tuple from telegram import Update +from dff.messengers.common.interface import PollingMessengerInterface, CallbackMessengerInterface from dff.pipeline.types import PipelineRunnerFunction +from dff.script import Context, Message from .abstract import _AbstractTelegramInterface -class PollingTelegramInterface(_AbstractTelegramInterface): # pragma: no cover +class PollingTelegramInterface(_AbstractTelegramInterface, PollingMessengerInterface): def __init__( self, token: str, attachments_directory: Optional[Path] = None, interval: int = 2, timeout: int = 20 ) -> None: - super().__init__(token, attachments_directory) + _AbstractTelegramInterface.__init__(self, token, attachments_directory) self.interval = interval self.timeout = timeout + def _request(self) -> List[Tuple[Message, Hashable]]: + raise RuntimeError("_request method for PollingTelegramInterface is not specified") + + def _respond(self, _: List[Context]): + raise RuntimeError("_respond method for PollingTelegramInterface is not specified") + async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs): await super().connect(pipeline_runner, *args, **kwargs) self.application.run_polling( @@ -22,14 +30,20 @@ async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs ) -class CallbackTelegramInterface(_AbstractTelegramInterface): # pragma: no cover +class CallbackTelegramInterface(_AbstractTelegramInterface, CallbackMessengerInterface): def __init__( self, token: str, attachments_directory: Optional[Path] = None, host: str = "localhost", port: int = 844 ): - super().__init__(token, attachments_directory) + _AbstractTelegramInterface.__init__(self, token, attachments_directory) self.listen = host self.port = port + def _request(self) -> List[Tuple[Message, Hashable]]: + raise RuntimeError("_request method for CallbackTelegramInterface is not specified") + + def _respond(self, _: List[Context]): + raise RuntimeError("_respond method for CallbackTelegramInterface is not specified") + async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs): await super().connect(pipeline_runner, *args, **kwargs) self.application.run_webhook(listen=self.listen, port=self.port, allowed_updates=Update.ALL_TYPES) From 140f014dabc5908908d0bb6659697417f4f29fa7 Mon Sep 17 00:00:00 2001 From: pseusys Date: Mon, 27 May 2024 22:11:52 +0200 Subject: [PATCH 076/140] some review parts fixed --- dff/messengers/common/interface.py | 14 ++++++++------ dff/messengers/telegram/abstract.py | 15 ++++++++------- tests/script/core/test_message.py | 4 ++-- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/dff/messengers/common/interface.py b/dff/messengers/common/interface.py index c1646ec39..9615d1b8e 100644 --- a/dff/messengers/common/interface.py +++ b/dff/messengers/common/interface.py @@ -11,13 +11,13 @@ import logging from pathlib import Path from tempfile import gettempdir -from typing import Optional, Any, List, Tuple, Hashable, TYPE_CHECKING +from typing import Optional, Any, List, Tuple, Hashable, TYPE_CHECKING, Type if TYPE_CHECKING: from dff.script import Context, Message from dff.pipeline.types import PipelineRunnerFunction from dff.messengers.common.types import PollingInterfaceLoopFunction - from dff.script.core.message import DataAttachment + from dff.script.core.message import Attachment, DataAttachment logger = logging.getLogger(__name__) @@ -28,8 +28,8 @@ class MessengerInterface(abc.ABC): It is responsible for connection between user and pipeline, as well as for request-response transactions. """ - supported_request_attachment_types = set() - supported_response_attachment_types = set() + supported_request_attachment_types: set[Type[Attachment]] = set() + supported_response_attachment_types: set[type[Attachment]] = set() def __init__(self, attachments_directory: Optional[Path] = None) -> None: tempdir = gettempdir() @@ -37,13 +37,14 @@ def __init__(self, attachments_directory: Optional[Path] = None) -> None: self.attachments_directory = attachments_directory else: warning_start = f"Attachments directory for {type(self).__name__} messenger interface" - warning_end = "attachment data won't be preserved locally!" + warning_end = "attachment data won't be cached locally!" if attachments_directory is None: self.attachments_directory = Path(tempdir) logger.warning(f"{warning_start} is None, so will be set to tempdir and {warning_end}") else: self.attachments_directory = attachments_directory logger.warning(f"{warning_start} is in tempdir, so {warning_end}") + self.attachments_directory.mkdir(parents=True, exist_ok=True) @abc.abstractmethod async def connect(self, pipeline_runner: PipelineRunnerFunction): @@ -140,7 +141,8 @@ class CallbackMessengerInterface(MessengerInterface): Callback message interface is waiting for user input and answers once it gets one. """ - def __init__(self): + def __init__(self, attachments_directory: Optional[Path] = None) -> None: + super().__init__(attachments_directory) self._pipeline_runner: Optional[PipelineRunnerFunction] = None async def connect(self, pipeline_runner: PipelineRunnerFunction): diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index 07d2738c5..5ed53d45e 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -199,6 +199,7 @@ def extract_message_from_telegram(self, update: TelegramMessage) -> Message: async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, message: Message) -> None: if message.attachments is not None: files = list() + media_group_attachments_num = len([att for att in message.attachments if isinstance(att, DataAttachment)]) for attachment in message.attachments: if isinstance(attachment, Location): await bot.send_location( @@ -250,7 +251,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes if isinstance(attachment, Audio): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: - if len(message.attachments) > 1: + if media_group_attachments_num == 1: files += [ InputMediaAudio( attachment_bytes, @@ -278,7 +279,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes if isinstance(attachment, Video): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: - if len(message.attachments) > 1: + if media_group_attachments_num == 1: files += [ InputMediaVideo( attachment_bytes, @@ -307,7 +308,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes if isinstance(attachment, Animation): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: - if len(message.attachments) > 1: + if media_group_attachments_num == 1: files += [ InputMediaAnimation( attachment_bytes, @@ -334,7 +335,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes if isinstance(attachment, Image): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: - if len(message.attachments) > 1: + if media_group_attachments_num == 1: files += [ InputMediaPhoto( attachment_bytes, @@ -359,7 +360,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes if isinstance(attachment, Document): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: - if len(message.attachments) > 1: + if media_group_attachments_num == 1: files += [ InputMediaDocument( attachment_bytes, @@ -410,7 +411,7 @@ async def _on_event( if update.effective_chat is not None and data_available: message = create_message(update) message.original_message = update - resp = await self.pipeline_runner(message, update.effective_chat.id) + resp = await self._pipeline_runner(message, update.effective_chat.id) if resp.last_response is not None: await self.cast_message_to_telegram_and_send( self.application.bot, update.effective_chat.id, resp.last_response @@ -423,4 +424,4 @@ async def on_callback(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> Non await self._on_event(update, _, lambda s: Message(attachments=[CallbackQuery(query_string=s.callback_query.data)])) async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs): - self.pipeline_runner = pipeline_runner + self._pipeline_runner = pipeline_runner diff --git a/tests/script/core/test_message.py b/tests/script/core/test_message.py index ccd4216ab..15eff7510 100644 --- a/tests/script/core/test_message.py +++ b/tests/script/core/test_message.py @@ -147,10 +147,10 @@ async def test_getting_attachment_bytes(self): for document in (remote_document_att, local_document_att, iface_document_att): read_bytes = await document.get_bytes(cli_iface) - assert document_bytes, read_bytes + assert document_bytes == read_bytes if not isinstance(document.source, Path): cached_bytes = document.cached_filename.read_bytes() - assert document_bytes, cached_bytes + assert document_bytes == cached_bytes def test_missing_error(self): with pytest.raises(ValidationError) as e: From 47610a338b3df1558aedb2f575196a1890433286 Mon Sep 17 00:00:00 2001 From: pseusys Date: Mon, 27 May 2024 22:12:50 +0200 Subject: [PATCH 077/140] venv ignore added back --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index bed9d05fb..bdba7af28 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *.DS_Store* *.egg-info/ dist/ +venv/ build/ docs/source/apiref docs/source/_misc From 0362fc8129501a5b23e25c17d0f302866746969f Mon Sep 17 00:00:00 2001 From: pseusys Date: Mon, 3 Jun 2024 21:18:36 +0200 Subject: [PATCH 078/140] some review comments updated --- dff/messengers/common/interface.py | 2 +- dff/script/core/message.py | 11 ++++++----- dff/utils/pydantic/__init__.py | 10 +++++----- tests/messengers/telegram/test_tutorials.py | 1 + tests/script/core/test_message.py | 1 + 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/dff/messengers/common/interface.py b/dff/messengers/common/interface.py index 9615d1b8e..e1d10c2c0 100644 --- a/dff/messengers/common/interface.py +++ b/dff/messengers/common/interface.py @@ -39,7 +39,7 @@ def __init__(self, attachments_directory: Optional[Path] = None) -> None: warning_start = f"Attachments directory for {type(self).__name__} messenger interface" warning_end = "attachment data won't be cached locally!" if attachments_directory is None: - self.attachments_directory = Path(tempdir) + self.attachments_directory = Path(tempdir) / f"dff-cache-{type(self).__name__}" logger.warning(f"{warning_start} is None, so will be set to tempdir and {warning_end}") else: self.attachments_directory = attachments_directory diff --git a/dff/script/core/message.py b/dff/script/core/message.py index 1abdd2964..691d5dd60 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -11,7 +11,7 @@ from urllib.request import urlopen from uuid import uuid4 -from pydantic import Field, field_validator, FilePath, HttpUrl, BaseModel, model_serializer, model_validator +from pydantic import Field, JsonValue, field_validator, FilePath, HttpUrl, BaseModel, model_serializer, model_validator from pydantic_core import Url from dff.messengers.common.interface import MessengerInterface @@ -23,11 +23,12 @@ class DataModel(BaseModel, extra="allow", arbitrary_types_allowed=True): This class is a Pydantic BaseModel that serves as a base class for all DFF models. """ - pass + def __init__(self, **kwargs): + super().__init__(**kwargs) # TODO: inline once annotated __pydantic_extra__ will be available in pydantic -def _json_extra_serializer(model: DataModel, original_serializer: Callable[[DataModel], Dict[str, Any]]) -> Dict[str, Any]: +def _json_extra_serializer(model: DataModel, original_serializer: Callable[[DataModel], JsonValue]) -> JsonValue: model_copy = model.model_copy(deep=True) for extra_name in model.model_extra.keys(): delattr(model_copy, extra_name) @@ -146,7 +147,7 @@ async def get_bytes(self, from_interface: MessengerInterface) -> Optional[bytes] if isinstance(self.source, Path): with open(self.source, "rb") as file: return file.read() - elif self.cached_filename is not None: + elif self.use_cache and self.cached_filename is not None: with open(self.cached_filename, "rb") as file: return file.read() elif isinstance(self.source, Url): @@ -243,7 +244,7 @@ def __init__( self, text: Optional[str] = None, commands: Optional[List[Command]] = None, - attachments: Optional[Attachment] = None, + attachments: Optional[List[Union[CallbackQuery, Location, Contact, Invoice, Poll, Audio, Video, Animation, Image, Sticker, Document]]] = None, annotations: Optional[dict] = None, misc: Optional[dict] = None, **kwargs, diff --git a/dff/utils/pydantic/__init__.py b/dff/utils/pydantic/__init__.py index a4d2b09c1..b96d0c2e7 100644 --- a/dff/utils/pydantic/__init__.py +++ b/dff/utils/pydantic/__init__.py @@ -1,13 +1,13 @@ from base64 import decodebytes, encodebytes from copy import deepcopy from pickle import dumps, loads -from typing import Annotated, Any, Callable, Dict -from pydantic import PydanticSchemaGenerationError, TypeAdapter, WrapSerializer, WrapValidator +from typing import Annotated, Callable +from pydantic import JsonValue, PydanticSchemaGenerationError, TypeAdapter, WrapSerializer, WrapValidator _JSON_EXTRA_FIELDS_KEYS = "__pickled_extra_fields__" -def json_pickle_serializer(model: Dict[str, Any], original_serializer: Callable[[Dict[str, Any]], Dict[str, Any]]) -> Dict[str, Any]: +def json_pickle_serializer(model: JsonValue, original_serializer: Callable[[JsonValue], JsonValue]) -> JsonValue: extra_fields = list() model_copy = deepcopy(model) @@ -26,7 +26,7 @@ def json_pickle_serializer(model: Dict[str, Any], original_serializer: Callable[ return original_dump -def json_pickle_validator(model: Dict[str, Any]) -> Dict[str, Any]: +def json_pickle_validator(model: JsonValue) -> JsonValue: model_copy = deepcopy(model) if _JSON_EXTRA_FIELDS_KEYS in model.keys(): @@ -40,4 +40,4 @@ def json_pickle_validator(model: Dict[str, Any]) -> Dict[str, Any]: JSONPickleSerializer = WrapSerializer(json_pickle_serializer, when_used="json") JSONPickleValidator = WrapValidator(json_pickle_validator) -JSONSerializableDict = Annotated[Dict[str, Any], JSONPickleSerializer, JSONPickleValidator] +JSONSerializableDict = Annotated[JsonValue, JSONPickleSerializer, JSONPickleValidator] diff --git a/tests/messengers/telegram/test_tutorials.py b/tests/messengers/telegram/test_tutorials.py index dd55cd783..bac4bc038 100644 --- a/tests/messengers/telegram/test_tutorials.py +++ b/tests/messengers/telegram/test_tutorials.py @@ -29,6 +29,7 @@ def _cast_dict_to_happy_step(dict: Dict) -> List[PathStep]: return path_steps +@pytest.mark.telegram @pytest.mark.asyncio @pytest.mark.parametrize( "tutorial_module_name", diff --git a/tests/script/core/test_message.py b/tests/script/core/test_message.py index 15eff7510..9cd2f0e4a 100644 --- a/tests/script/core/test_message.py +++ b/tests/script/core/test_message.py @@ -149,6 +149,7 @@ async def test_getting_attachment_bytes(self): read_bytes = await document.get_bytes(cli_iface) assert document_bytes == read_bytes if not isinstance(document.source, Path): + assert document.cached_filename is not None cached_bytes = document.cached_filename.read_bytes() assert document_bytes == cached_bytes From 07d887e8caa11e31842a9487da4dddffafd58a1a Mon Sep 17 00:00:00 2001 From: pseusys Date: Thu, 6 Jun 2024 00:20:52 +0200 Subject: [PATCH 079/140] erview notes taken into account --- dff/messengers/telegram/abstract.py | 10 +-- dff/script/core/message.py | 12 ++-- dff/utils/pydantic/__init__.py | 22 ++++-- .../messengers/telegram/test_happy_paths.json | 14 ++-- tests/messengers/telegram/utils.py | 69 ++++++++++--------- tests/script/core/test_message.py | 11 +-- 6 files changed, 77 insertions(+), 61 deletions(-) diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index 5ed53d45e..50c4742b6 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -251,7 +251,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes if isinstance(attachment, Audio): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: - if media_group_attachments_num == 1: + if media_group_attachments_num != 1: files += [ InputMediaAudio( attachment_bytes, @@ -279,7 +279,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes if isinstance(attachment, Video): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: - if media_group_attachments_num == 1: + if media_group_attachments_num != 1: files += [ InputMediaVideo( attachment_bytes, @@ -308,7 +308,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes if isinstance(attachment, Animation): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: - if media_group_attachments_num == 1: + if media_group_attachments_num != 1: files += [ InputMediaAnimation( attachment_bytes, @@ -335,7 +335,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes if isinstance(attachment, Image): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: - if media_group_attachments_num == 1: + if media_group_attachments_num != 1: files += [ InputMediaPhoto( attachment_bytes, @@ -360,7 +360,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes if isinstance(attachment, Document): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: - if media_group_attachments_num == 1: + if media_group_attachments_num != 1: files += [ InputMediaDocument( attachment_bytes, diff --git a/dff/script/core/message.py b/dff/script/core/message.py index 691d5dd60..ca8d1226a 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -15,7 +15,7 @@ from pydantic_core import Url from dff.messengers.common.interface import MessengerInterface -from dff.utils.pydantic import json_pickle_serializer, json_pickle_validator +from dff.utils.pydantic import JSONSerializableDict, SerializableVaue, json_pickle_serializer, json_pickle_validator class DataModel(BaseModel, extra="allow", arbitrary_types_allowed=True): @@ -232,9 +232,9 @@ class level variables to store message information. text: Optional[str] = None commands: Optional[List[Command]] = None attachments: Optional[List[Union[CallbackQuery, Location, Contact, Invoice, Poll, Audio, Video, Animation, Image, Sticker, Document]]] = None - annotations: Optional[dict] = None - misc: Optional[dict] = None - original_message: Optional[Any] = None + annotations: Optional[JSONSerializableDict] = None + misc: Optional[JSONSerializableDict] = None + original_message: Optional[SerializableVaue] = None # commands and state options are required for integration with services # that use an intermediate backend server, like Yandex's Alice # state: Optional[Session] = Session.ACTIVE @@ -245,8 +245,8 @@ def __init__( text: Optional[str] = None, commands: Optional[List[Command]] = None, attachments: Optional[List[Union[CallbackQuery, Location, Contact, Invoice, Poll, Audio, Video, Animation, Image, Sticker, Document]]] = None, - annotations: Optional[dict] = None, - misc: Optional[dict] = None, + annotations: Optional[JSONSerializableDict] = None, + misc: Optional[JSONSerializableDict] = None, **kwargs, ): super().__init__( diff --git a/dff/utils/pydantic/__init__.py b/dff/utils/pydantic/__init__.py index b96d0c2e7..31bb68879 100644 --- a/dff/utils/pydantic/__init__.py +++ b/dff/utils/pydantic/__init__.py @@ -1,12 +1,20 @@ from base64 import decodebytes, encodebytes from copy import deepcopy from pickle import dumps, loads -from typing import Annotated, Callable -from pydantic import JsonValue, PydanticSchemaGenerationError, TypeAdapter, WrapSerializer, WrapValidator +from typing import Annotated, Any, Callable +from pydantic import AfterValidator, JsonValue, PlainSerializer, PlainValidator, PydanticSchemaGenerationError, TypeAdapter, WrapSerializer, WrapValidator _JSON_EXTRA_FIELDS_KEYS = "__pickled_extra_fields__" +def pickle_serializer(value: Any) -> JsonValue: + return encodebytes(dumps(value)).decode() + + +def pickle_validator(value: JsonValue) -> Any: + return loads(decodebytes(value.encode())) + + def json_pickle_serializer(model: JsonValue, original_serializer: Callable[[JsonValue], JsonValue]) -> JsonValue: extra_fields = list() model_copy = deepcopy(model) @@ -18,7 +26,7 @@ def json_pickle_serializer(model: JsonValue, original_serializer: Callable[[Json else: TypeAdapter(type(field_value)) except PydanticSchemaGenerationError: - model_copy[field_name] = encodebytes(dumps(field_value)).decode() + model_copy[field_name] = pickle_serializer(field_value) extra_fields += [field_name] original_dump = original_serializer(model_copy) @@ -32,12 +40,16 @@ def json_pickle_validator(model: JsonValue) -> JsonValue: if _JSON_EXTRA_FIELDS_KEYS in model.keys(): for extra_key in model[_JSON_EXTRA_FIELDS_KEYS]: extra_value = model[extra_key] - model_copy[extra_key] = loads(decodebytes(extra_value.encode())) + model_copy[extra_key] = pickle_validator(extra_value) del model_copy[_JSON_EXTRA_FIELDS_KEYS] return model_copy +PickleSerializer = PlainSerializer(pickle_serializer, when_used="json") +PickleValidator = PlainValidator(pickle_validator) +SerializableVaue = Annotated[Any, PickleSerializer, PickleValidator] + JSONPickleSerializer = WrapSerializer(json_pickle_serializer, when_used="json") -JSONPickleValidator = WrapValidator(json_pickle_validator) +JSONPickleValidator = AfterValidator(json_pickle_validator) JSONSerializableDict = Annotated[JsonValue, JSONPickleSerializer, JSONPickleValidator] diff --git a/tests/messengers/telegram/test_happy_paths.json b/tests/messengers/telegram/test_happy_paths.json index 9f88c12da..fb71a225d 100644 --- a/tests/messengers/telegram/test_happy_paths.json +++ b/tests/messengers/telegram/test_happy_paths.json @@ -65,7 +65,7 @@ { "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 0, 46, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=6, supergroup_chat_created=False, text='audio'), update_id=6)", "received_message": "{\"text\":\"audio\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", - "response_message": "{\"text\":\"Here's your audio!\",\"commands\":null,\"attachments\":[{\"source\":\"https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@wiki/example-attachments/separation-william-king.mp3\",\"cached_filename\":null,\"id\":null,\"title\":\"\\\"Separation\\\" melody by William King\",\"use_cache\":true,\"dff_attachment_type\":\"audio\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_message": "{\"text\":\"Here's your audio!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/separation-william-king.mp3\",\"cached_filename\":null,\"id\":null,\"title\":\"\\\"Separation\\\" melody by William King\",\"use_cache\":true,\"dff_attachment_type\":\"audio\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ "send_audio(42, audio=<>, performer=None, title=None, caption=\"Here's your audio!\", disable_notification=None, reply_markup=None, parse_mode=None, thumbnail=None)" ] @@ -73,7 +73,7 @@ { "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 1, 4, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=7, supergroup_chat_created=False, text='video'), update_id=7)", "received_message": "{\"text\":\"video\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", - "response_message": "{\"text\":\"Here's your video!\",\"commands\":null,\"attachments\":[{\"source\":\"https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@wiki/example-attachments/crownfall-lags-nkognit0.mp4\",\"cached_filename\":null,\"id\":null,\"title\":\"Epic Dota2 gameplay by Nkognit0\",\"use_cache\":true,\"dff_attachment_type\":\"video\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_message": "{\"text\":\"Here's your video!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/crownfall-lags-nkognit0.mp4\",\"cached_filename\":null,\"id\":null,\"title\":\"Epic Dota2 gameplay by Nkognit0\",\"use_cache\":true,\"dff_attachment_type\":\"video\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ "send_video(42, <>, caption=\"Here's your video!\", disable_notification=None, reply_markup=None, parse_mode=None, supports_streaming=None, has_spoiler=None, thumbnail=None, filename=None)" ] @@ -81,7 +81,7 @@ { "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 1, 22, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=8, supergroup_chat_created=False, text='animation'), update_id=8)", "received_message": "{\"text\":\"animation\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", - "response_message": "{\"text\":\"Here's your animation!\",\"commands\":null,\"attachments\":[{\"source\":\"https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@wiki/example-attachments/hong-kong-simplyart4794.gif\",\"cached_filename\":null,\"id\":null,\"title\":\"Hong Kong skyscraper views by Simplyart4794\",\"use_cache\":true,\"dff_attachment_type\":\"animation\",\"filename\":\"hk.gif\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_message": "{\"text\":\"Here's your animation!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/hong-kong-simplyart4794.gif\",\"cached_filename\":null,\"id\":null,\"title\":\"Hong Kong skyscraper views by Simplyart4794\",\"use_cache\":true,\"dff_attachment_type\":\"animation\",\"filename\":\"hk.gif\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ "send_animation(42, <>, caption=\"Here's your animation!\", parse_mode=None, disable_notification=None, reply_markup=None, has_spoiler=None, thumbnail=None, filename='hk.gif')" ] @@ -89,7 +89,7 @@ { "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 1, 35, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=9, supergroup_chat_created=False, text='image'), update_id=9)", "received_message": "{\"text\":\"image\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", - "response_message": "{\"text\":\"Here's your image!\",\"commands\":null,\"attachments\":[{\"source\":\"https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@wiki/example-attachments/deeppavlov.png\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov logo\",\"use_cache\":true,\"dff_attachment_type\":\"image\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_message": "{\"text\":\"Here's your image!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/deeppavlov.png\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov logo\",\"use_cache\":true,\"dff_attachment_type\":\"image\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ "send_photo(42, <>, caption=\"Here's your image!\", disable_notification=None, reply_markup=None, parse_mode=None, has_spoiler=None, filename=None)" ] @@ -97,7 +97,7 @@ { "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 2, 19, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=10, supergroup_chat_created=False, text='document'), update_id=10)", "received_message": "{\"text\":\"document\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", - "response_message": "{\"text\":\"Here's your document!\",\"commands\":null,\"attachments\":[{\"source\":\"https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@wiki/example-attachments/deeppavlov-article.pdf\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov article\",\"use_cache\":true,\"dff_attachment_type\":\"document\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_message": "{\"text\":\"Here's your document!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/deeppavlov-article.pdf\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov article\",\"use_cache\":true,\"dff_attachment_type\":\"document\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ "send_document(42, <>, caption=\"Here's your document!\", disable_notification=None, reply_markup=None, parse_mode=None, thumbnail=None, filename=None)" ] @@ -157,7 +157,7 @@ { "update": "Update(callback_query=CallbackQuery(chat_instance='-1', data='secret', from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), id='5', message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 9, 28, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Bot', id=16, is_bot=True, username='dff_bot'), group_chat_created=False, location=Location(latitude=58.43162, longitude=27.792879), message_id=6, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), supergroup_chat_created=False)), update_id=6)", "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"secret\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", - "response_message": "{\"text\":\"Here's your secret image! Run \/start command again to restart.\",\"commands\":null,\"attachments\":[{\"source\":\"https:\/\/cdn.jsdelivr.net\/gh\/deeppavlov\/dialog_flow_framework@example-attachments\/deeppavlov.png\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov logo\",\"use_cache\":true,\"dff_attachment_type\":\"image\",\"has_spoiler\":true,\"filename\":\"deeppavlov_logo.png\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_message": "{\"text\":\"Here's your secret image! Run \/start command again to restart.\",\"commands\":null,\"attachments\":[{\"source\":\"https:\/\/github.com\/deeppavlov\/dialog_flow_framework\/wiki\/example_attachments\/deeppavlov.png\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov logo\",\"use_cache\":true,\"dff_attachment_type\":\"image\",\"has_spoiler\":true,\"filename\":\"deeppavlov_logo.png\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ "send_photo(42, <>, caption=\"Here's your secret image! Run /start command again to restart.\", disable_notification=None, reply_markup=None, parse_mode=None, has_spoiler=True, filename='deeppavlov_logo.png')" ] @@ -173,7 +173,7 @@ { "update": "Update(callback_query=CallbackQuery(chat_instance='-1', data='thumbnail', from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), id='7', message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 9, 34, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Bot', id=16, is_bot=True, username='dff_bot'), group_chat_created=False, location=Location(latitude=58.43162, longitude=27.792879), message_id=8, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), supergroup_chat_created=False)), update_id=8)", "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"thumbnail\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", - "response_message": "{\"text\":\"Here's your document with tumbnail! Run \/start command again to restart.\",\"commands\":null,\"attachments\":[{\"source\":\"https:\/\/cdn.jsdelivr.net\/gh\/deeppavlov\/dialog_flow_framework@example-attachments\/deeppavlov-article.pdf\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov article\",\"use_cache\":true,\"dff_attachment_type\":\"document\",\"filename\":\"deeppavlov_article.pdf\",\"thumbnail\":\"gASVySsAAAAAAABCwisAAIlQTkcNChoKAAAADUlIRFIAAADIAAAAyAgCAAAAIjo5yQAAK4lJREFU\\neJzsnXdcFFfXx+\/sbF\/KsnSQLiiIUlSwgo+KYi9RAbvYS4wleZ7EqNFojEaTqOlqLLFh77F3jaCA\\nCEgREJDeWbb394PwIiIus7NTFrPfj3\/g7My9h+U3t557DlWj0QAjRrCGQrYBRj5MjMIyggtGYRnB\\nBaOwjOCCUVhGcMEoLCO4YBSWEVwwCssILhiFZQQXqGQbgBIlf6+qYhHZVuAMbEOzP0NhdifbDjS0\\n1xZLVbmSbBPwR1WuEsSQbQRK2quwgEZCtgWEoBGTbQFK2q2wjBg2RmEZwQWjsIzgglFYRnDBKCw0\\n8GXsEhGPL2PrU4hcRX1a4Y6dUYZFe13H0s7DEu9tTycxYflgp8Ro76sQhOipx2Ve3yZEMWH5KNfY\\nyZ1ua7nzUn7Q7ucjulrm7gz5FbWRNwv9l91fkDl1DuoSDJkPU1ilYh6DIt8QvD\/65qdcunCoS8LG\\nJ5OFCtZnAcddzCrOvex9raB7oHXWbJ+rmxPq9ZfF77Cgy8VyiYVGAzb13jvk3OYulnnPq10flnSh\\nQqqvgw8czw515FQOd31yLCuExxBM9rrDl5mk1zg3VLcrddizKo\/edmmhDsm704Z9HXywWMj74\/mI\\n9cEHm1t1JPM\/D0u7MGH5V0EHM2s6XMzrNanjPZK+Idz5YLtCGqzqyC0Jc46\/Vej\/6YO5tqyavvap\\n824vyxdYr4md8XHXs\/vShqZVO5\/L7ZNe4+zFLVjxYD4AQAMgCqQGQEOnKDtxC77scVitATFZoQo1\\nvDF+MgDgh6cfmdFFzSs6md3vyIuBEzzurX88tUzC\/Tsv6G6R76X8IImS3sKkblYvV\/c4nFdnczwr\\n1MO8JNz5ybGsAcR+K8TxYbZYTYgVTAum8GZhQHKVuylNYsuqiSvtXCs3+eT+Ig5NSgFqAMB837\/N\\naKIfnk4AAMSWek++umplwClvXsHauGl3Cv3rFGwf3qto76urHkUnlHfkyznBdi+aV5FV6xhkm\/Gf\\nDsn97FOTq9wjve5cyO39qNRnR8gvDTeoNRAF0mg0YF\/6kCflncrEFiGOqTym0Ncyj6RvhQg+WGEV\\nCS1\/SxlxIifk0sjVpSIelyGY3OkOC5axqPX\/1vQ8DEEaJ9MKAEB6tVNiRcchzvEAgP4OqSeHbWgo\\n4VhW6Ibgv\/akhQMAmFTFoq7nh1345que9b3bS75dvsCmTMzN4dt15BZtSYi8VeB37VWPdUH1n\/Y5\\nucOGXdPdOhsAcDqnz+K7S4tnRQoVzN3Ph18Yufa\/D+sHVeVi82eV7jIV7XFZpyDbTLK\/LeyB161b\\nR7YNOpNUJLBWbtVyA5WigiDIhCZdH3zQw7w0xDGlQGjzoMS3g0mln3Wuv1XO+dzeVVKzYNuMvzLD\\nxEpmgHXOZ4EnmLDcgV3dyaKwoZBO3MLYMu+JHvd9eK\/sODU+vPwqqfmSbufYNHlcWSeJitnBpJIC\\naUa7xVox6+4U+23svd+HV8BliJmwfIRrnA+vAADAospMaeJ+DmkMWOlrmXe\/2HdOlyte3CKJkpFY\\n4dXLLqNAaNPbLv19v0iB2FvNGsamwzh8i\/gCta9zhQkFdRuu515IqxQvGIFJgb1Pbv8l9OfA162L\\nAbInLXz14+VL+nb4dICLOas9dS\/txtbcasmmG3kH4ksUKizfhEsjV5vSDXo\/u1ai3Hgjb09c8ReD\\nXJf2dyLbHKS0g1mhUqVefTnHc9M\/e+KKsVUVAIDHFNIoKmzLxINSgfyTsy\/cNj688aKabFsQYegt\\n1vnUioWnMorr5GQbYhDk1UjD\/ng6pBNvX4SPgzmDbHO0YbgtVoVQPn5f8ph9yUZVteBaZnWnzY9+\\neVBAtiHaMFBh7Xtc7L0l9kxqBdmGGChCuWrJmRcDfk14Xiok25bWMThhVYrkkw+mRh9LrxIrtNym\\n1iDb\/2vnKFTaFhru5tQG73iy61GRSk2gTcgwLGHdf1nTdWvc0aSyNu88n9uLEIvIRKGCrxcEar9H\\nJFfPP5kxdt+zaq3vIfEY0DrWqr+zt95+pVQjsodNlYY5JVix6vC3ixw0GpBY4ZlU2RHh\/Xam9PPR\\n3Xo6m+NsF1IMQlg1YsWMo2kX0irJNqR9w6RSNg33WB7qTLYhwCCElVkuGrsvOaO8vR5HMTSW9u+w\\nbZQnDSZ5kEOysO69rBn95zO+tB0sUbYj+rtzL0T7kbsFRKaw9sYVLziZoUA2qDKiE64WzDuLAl14\\nLLIMIEdYagDWXc7ZcONDdkgiHWcu88QM3yCShvMkCEupUn95Oee7268IrvdfiCWbejbar58bl\/iq\\nSRDWsrOZO+4XElzpvxZTBnw+2m9ARwuC6yVaWPNPpO+KLSayRiMsGuXvOf4Ea4vQSemS0xlGVRGP\\nRKEevifpQW4NkZUSJ6z\/Xcz65WERYdUZaY5EoR79Z3JCAXEbFQQJ69ubucbROrnUSJRDdydllImI\\nqY4IYR14Urzq75cEVGREO1UixcDfEssEMgLqwl1Yj1\/xF5z8AI83tVNKBPLwXUkiOe5bHfgKK7Nc\\nNHx3klRpeO5C\/2KSioXzTqTLcfbhwlFYApky8mBqlViJXxVG0HEksWzLLXy3PfASlkoN5p\/ISCo2\\nUMdZI19dyb2agaOfEl7C+vFe\/tGnbTuCGiELDQBRh57nVuF1phIXYT15Vbf6cg4eJRvBkBqJctJf\\nKTg5l2AvLIlCFXUoVaY0OsO0A+ILBeuv4tIEYC+sZWdf5ODWwBrBnK+v5z1+xce8WIyFdTWjyrgb\\n2O6IPJgqUWC8soWlsKpE8jnH3xuRx4jBklstXXEuC9sysXSbWXAi\/Y\/22FypVeySZJqwTOQYqDSx\\naf4JpJSZ5j9kVuXAMoGKaS4zcxS49FEzzcizFS9gCDxe1jOwA2a\/GmbCupdTM+j3pwhPBRoU1k\/2\\nmL2KrZ+BQ3DRgM9lPLfGD9Qqu39+4pSlNr9ZRWOV95gtdvAnx1Y88XcwebS0B5OGTZA3bLpChUq9\\n7NyL9qgqWFRl+lpVr18ylVnOraaPOMWJLVRVf79CYvfoF0algQZq04ekYuEvDzHz7MXmhNAfj4qe\\nFrXLRXYNBQYQBWgaN86cLU1H9LTvwGX06GBWEF+5Pe7Nnc4uLr6+vjeuX5fL5X1Lz2\/9JkahUqeW\\nCp8VCwv5sttZNbXSdr95tf5a7pRAOzszDAIkYdAVVork3ltiK0WGFTsAOc4pR2gv6hsqFpt1\/NQp\\nb2\/vhusymWxu9Ox\/Hj4EAMyZO3fFZ5\/S6fSE+PiICRMBAIdjjgb3ehM\/Qq5SpxQLb2fX3M+tvZxR\\nhXmAOMJY3Nfx5\/Gd9S8HA2GtbZ8HubhMamSA7YI+jn4OpokJiTk52UOHDjUzb3lYKutFFpfLtbax\\nbroya\/qM+\/furV67ZmZ0dKslK1TqI4mlx5LKr2RUtUd9PV0R5O9oqmch+naFxXzZd3fy9SyEYAId\\nTRf1dZzZ07HpGHpg98DA7q3HdfH08mxxxdHREQCQnfXeYRYNpszo6TCjp0OZQLb19qtdsUUCWXs6\\n6r3+2sszs\/z0LETfFuvj05k\/Yzfiw5t+buabhnv0d9frvMqd27dTklM6enYcNnw4kvsVKvWeuOLV\\nl3Oq248HUeKKoAD9Gi29hFUqkDl9\/bBdTAaDnM2+Dncf2smSLANEctX2e6++u51f1x4CVQzsaHFz\\nYRuhubSjl7AWn8r49R9DP3hjxoQ3hnss6ONIegAWAMCrGunnl7JjnpYZ\/rv4bGVQNwf0jRZ6YfGl\\nSpu19+SGPf0Z2om3P9IHk\/kzhlxKq1xwMqOQT8ShBtREBdgemeqL+nH0L\/E313MNWVVUCrRrYucr\\n8wIMTVUAgBE+Vokrg0Z3sSLbEG0cfVqWVYE+aBlKYSnVmt1xBrMtqFZRheX0mnxGTV7DP0+W9OHH\\n3ef2ciTbsvdizaGfi\/bbPMKDUfuKKqoAakMceP3+CP04B2VXeCSxdMrh56hr1R8av4hdlsoue04V\\nV9FElZDmzR+mi2+XP\/fvt7Iy6PagiatXrny2YqVIKpOb2Co51mIbH7G9n5JjEMYzqJSqDSEcVCmi\\nUAor8Ic4cvZw1ErT\/Fjuiyt0YWmrn\/fr33\/vgf0UCvnjdOQ8S3q2cN688vLypisSS8+aTsMk9t1I\\ntaue3z7qtKBPBxQPohHW00JB4I+PUVSmJ5xXsVYpJ6jS97o7jhk7dsu2rVSqoedxeZfCgoIJ48ZX\\nVr51bEbKdan0j5JZIg2cjAe9XMweLe2J4kE0b\/aBeKJHV1Rhuf397+2e7NGiqtFjxnz3\/bb2qCoA\\nQAcnp1Nnzzg4ODS\/yKzNd7yzhffsGKQkLelLbH7do7xaFA\/qLCylWnOW2EwkjKoch3tb2eXafFND\\nBwzYvPU7GG5\/CSObcOzQ4fc9u1tsVkJAY5F93fbRzxQ5ac4jp5LR\/Ll1Ftbl9Mr8GuIWYFhlaQ73\\nttIk2mI7+fr6bv9pJ53eMrl3u8PHx+fnX39lMpktrnPK0+zv\/0iRCUix6nRKOYrj+DoL62Ryuc6V\\noIVZnmH\/z06KWtsWm42NzW+7d5ma6rsbbyD06dtn9Vdr373OrM23f7gDaP0qcCK3WhpfoPMxHt2E\\nJVOqL6dX6VoHOmiCUtvHu6C2vsodP\/9kb29PjEnEEBkVNSli0rvXmTV5No93k2ER2BWr84KWbsJK\\nKKirIMahT6O2jt9HlbURge6T5ct7BgURYQ+xrFm3zt3D493rpkUJJv\/vSE0kt7J1DjOpm7CQ5OXC\\nBIv0i6zqNk7oevv4fPzJUmLsIRgWi7Xz558gqJXUedaJByGllGB78qqlz4p0G+HpJqzrmUQkJKbI\\nBOY5N9u4h0LZvnMHAcaQRWdv70VLlrx7naKScTOvEG\/PZR1D0+ggrIwyUaYeu5LI4WZegeVthMpc\\nsGihR0cyVw4JYM68ue7u7u9et3hxBaiIPmHwIFe38bsOwnpCSMxdSCExzX+o\/R4nZ+ely5YRYAy5\\nmJqafva\/\/757HVIrTfP\/IdiY6y+qq0Q6rNPqICxiFhpYFZnUthYD5y9c0E5X2HUlbOjQ3n36vHud\\nU5RIsCVyleZ5qQ4Rl3UQVjIh4fk4JUnab+js7T1u\/HgCLDEQlq9c8e5FZlU28b3h5QwdVpqQCiu9\\nVJRXg\/9kRKNhad26AQDMmh3NYBic7x5+BHbv7u\/f8kQ\/rJKxKl8QbEkaHi3Wy2oiQl5RheU0sbbX\\nwsbGZviIEQRYYlAsXd7KgJImIjrT8a2cGhniANhIhXU3h4hMLLCijVlnRFQki0Vackey6NO3r5OT\\nU4uLlLYmzpgjlKmQJ7ZAKqzCWiIW5WCtm80wDE+KiCDADEODSqV+NHFCi4uwhIg1xRZkI47ViHRu\\ndVP3RX0UwFrngz2Dguzf9lgiHpXghFp0UcsNsNl0CnsQ5vWOHT9+5\/YdavWbngjSkJCW4V5OzUfd\\nbJDciUhYLyvF5UIi5iCQVnfWEaNGEmCDdjTyFLXwmJYbKKw+AGAvrA4dOvQMCoqLfbNR2Mp2D\/5U\\nId4pRiSsKjH5kWRodNrgsDCyrQAAdoAYAVpvwOscxKDBg5oLixSQZ4RAJCxDiILs69vV2tqabCsA\\nlbsAcBeQUnW\/kBAAviGl6iYyKjAdvCcUkuO72JzQ0FCyTSAZLy8vNptNrg0qNRAgiy+HqMWqM4BY\\ndZ28MYgGpj+qugMqgbYxFsxdBHPwGguGDR1y7sxZnApHSEa5qKdzyyhi74JIWHyyhUWj0VrdMiMe\\njSJHI7ml7Q6TMfjVHhgYSLqwXlZJMBPWS7LHWB09PU1MTMi1oRGqE8QM1nYDbItf5W6tedEQDMIB\\nd\/sQlo0NorUTAqCazwXmc8mqPSBQr5hVmFCAbKkc0eCd9GSW3fzIP2xOInm5uc+Skhpclkk\/O4kw\\nzF77iHFga4tj\/2KwSCSSixcuTI2aPPg\/A9PT0houurm5kWuVGllMhvbhLkej0cg2gVAqKipOHj8R\\nc\/RoUWHL+K5OLs7Z2WSmL0AY6rJtYUnwz3jeJtYGM8bCG4lE8v3WrTFHjkqlrQ9l7OxIPkRZK8Fo\\nHYv0tQYAAJfLJdsE3MnLy1uz6stH\/7R0Znd3d1+\/cUPTaguX2\/ZUH1cQ6qFtYRlCNEhDWWvAAZVK\\ndfP6jZijR+\/dvdv8OgzDYUOGRERF9g8JaX7dhOxgAgj10D7GWKRPhfCgurr67OnTMUdjXua8dTSX\\nx+ONHjt2yrSprY7T28tX0T6EhTq085d\/Z1\/F9JDtwj6Os4P1DW2anZW1e9euy5f+Fovf8pj19PKa\\nO2\/ekPChWlpoDPNL4kr7EJZIhNINN7daiu0OeqkAfQw0pVJ58fyFYzFH45\/EN9cHlUodNnz4lGlT\\nu3fvDrUV5FIkJNojGR1tC8sEVWxTbOHzsc+GTSTV1dVHDh0+deJEQUFB8+s8Hi8iKnJSZOS7Lu3v\\ng89HE18PQxDqoW1hmTLJb9XKyvQNRjKzpz266L8NJBTWxeajPAi+etWqvy9eqqt763Hfrl0jo6KG\\njxxhZqZbttzSUoLisrwPHhuRHsgXDRJk71nUQc6m4R72emQS2HwrD7WwYo4cbfoZgqCh4eFz58\/3\\n80eZXutVPsm51syRNTTtQ1jV1SScSMEWM3Pz2XPmTJ4y2YLH06ecFlNI4oEpiLztEQnLnAnzSc1Z\\nlfwsmcTa9aRnUM\/IqKiRo0frv1Igk8kUCpLPH7QWtKsVEAnL3ZJFbsrn4iKUuTc6mDO62HIaUuvo\\nY4AVh9ZQjhVH513Lo8eP61N1c1JTUrAqCjUOyEYUiITlYckmV1g5OTkymQxFyIbvRnl+N6plilQU\\nzAl2nKP38pX+vMjMJNsE4GGJ6CQ6ImEhnAjgh0wmi330KHTAAMxLlkqlL3NyGg6C0mg0Ty8vAtKl\\nFBYW1tY0HgB2cHTkIR51paam4mkXItwxFBabRv5SVkZ6OrbCevjgwYF9+588fiwQvFlBtbGxCRkQ\\nOmPWrKZc9hhSXV198MCBy39fzs7KaroIQZB\/QMC48eMmToqg0dvoZ69eJiFIZAu8bThIbkP0dvo7\\nkr8HfO\/uPayKqigvX7p4yYyp027dvCkQCKhUqourq6ubG5VKLS8vP3n8xISx47Z8++37HFfQcfHC\\nhWFhQ37asbNBVRYWFp5enjweT6PRPE1MXLt6zbgxY5KfPWv12ZI6WWM7V0vy6igEAJeNaJSJqMXy\\nsUUkUlx5lpRUV1en63Liu7zMyZkxdVpJSQmDwZgwaeL0mTPNzMzOnjmjUqpGjRktkUgOHvjr+LGY\\n3X\/sSkxI\/H3XH3quDjTw7Tff\/Ll7DwDAz98\/Iipy7LhxTXk0FArF6VOnYg4fSUlJmTBu\/A87to8c\\nNar5s2dSyiMPpu6N8FanthHwlwA6WiEN9YOoxTIjb\/F9dqTgt01VENBIpdLrV6\/pWVpeXl7kpIiS\\nkpKQ0NCbd++s37DBxcUlcuLELZu+3fbddyPCh1lZWa3f8PWtu3eDe\/dKiI+fEhkll+ubIOnrdev+\\n3L2HzeHs+PmnU2fPTIqIaJ6dhUajRURGnrlwftPmzVQqddnHS4\/HxDR9uvZKzvj9KUq1Rq7S3Lh2\\nXU9L9KeHE1KnHUTC8rThcFkkaGvx9LoVc4QhQfIfv6qBIM2li9rCvCCBTqezmMyZ0bP2HthvZ2fX\\nMNLKz2tcyxYKBNevXgUA2Nvb\/3Xo0JixYyEKRf8O0dLS0tzc\/PS5syNGajvIOiky4ujxY6ampqrX\\nmWvUAET8lbLhep4ZA742P2CkKzU+Pl5PS\/THgoV0tQWRXCiv8+WfTiE06dfi6XWLpjfu5Hu5KV0c\\nlU8eP66qrLTUI3Wqg4PDqbNnrJrFgHBxdaXT6U3NkrOLS8MPMAxv+\/EHkUikv4\/h4o8\/njJ1KtfC\\nos07\/fz9H8Q+4nA4ZQL58N1JiUUCPweTmGm+nW04e\/7YJZORn5y8rytS\/1WkU2snLqFhP0eHiRdO\\na1RVVS20aLVFXiFNIpFc1LvRsno7soirq+vylSs4HA6DwZg1Ozq4V6+mjyAIwspzFYmqGuBw6oez\\n4\/clJxYJLNnUMzO7dX49Czt96hQmluhJZ8SjbaTCCvVA+tXoj7uzYu0n\/IatA6kMTF9umVfY2AIf\\n2Lcf8z2NufPnP05MiE96+uWaNdiWjJqTM7t6WLKqxMqPDiQX82WJCYkvXhAdyvZdGFTIG3NheVoR\\nFOcEhjVbV9Wy\/j9l3+qt5nkFb\/r1V\/n5N65jP4ZlMBgGFdrU3oyRsCJopLfl0yJhjx8ff\/bjfrIt\\nqmeAhwUL8YomUmF525o4mhGRaXJ0mKRzx8ZzILcfMS7faSnovXv2NI+Y+KFizqQem951RahTiUB+\\n2zJc0AFNYmZs8bXTYdUJqbBgCvDRpVx0MBia6EmNm5JyOdj6eyuT26eJT69euYq3JYYAmw5\/P9or\\nnJaloVCVLOKGIu9jaCdL5DfrsC\/2UVfcT42G9hC6Ozf658RcYOcXtT65\/XnnTrwtMRD+efgwK2aL\\n85UvLLL0XcPTEwgCvvY6TGV0EFZvxFNN1Ezo9ibfyZkr7x30ZGZk7NlFTq5RIlEoFN9u\/AYAQCMj\\n8nYLBna00MkFVwdhdXMwdeW1zIONITCkGuka1\/BzYirtRa62Id3O7dtLiovxM8YQ2L93X3p6Gwlg\\nCKOfm26H0XVbT\/+Ph8W+6hIdTUJKkE0GldI4Kk\/JaGOFVywWf7pi5eGYozgZQwxqeYZaeF6jyNYo\\nXwF1HQTbAKoDhdkLZg8qKFbt+PFHsg18w0gf3daldRPW1O52+57gJaxQxzf+x0+etT0DjYuNPX3q\\n1PiPPsLJHlxRS5+oqjepxZebX2w4aqiu26uE6JkJ7mymSCol32Hp9fIH3d9Rt+1\/3ZzaermYmzPx\\n+lU7mLzJOlRWiaiWLz\/\/orlvUztBoaz8QlE4sIWq3kIj7x+YcWlfxZAQ8gOh17\/z7lyqju6Put3O\\npsNhXhi4kbQO9N7\/vA+FQrFk0eKKCkI3MfVCLZUXR6lqfwSg7f0DM1PN1i9royeRHwt9fm+d3bJ1\\ndsNFmEqFMLKzslYuW65Ukh9rCQmKypUasQ7bnVQYLJstDA8ls91yMKP3cdU5jJTOwprgZ2tnSsQS\\nPHL+efhw7ZerybaibVSCE+q6P3V9CobBuhV8SwvSjt+N6WJN17UjRCEsKgUa44tLuhil+o0xpia6\\nfY\/Hjx37et06HIzCDrVIWbUW3aOmHM2SGaR1iONR9VFoTqRMCbRD8VSbpFa9CQfVK0Bnv82\/9h\/Y\\ntuU7rI3CDJXwIlDmon58fLjEzpqE7r6LLWcwqlE1GmH1d7fwssbeF+Dqq+5NP3u6ofkSf\/\/tt7Wr\\n16hV5AdNfReV6Lw+j1OpYEAvEhz95gSjTBCJ8gzd8hBndA9qoUBoWyZuHCSGBMnsrNHo48ihQ4sX\\nLRIKyTxe2yptJEpBgI8X0YfrYQhM64Eyli5KYc3sac\/UfUDXJmdeNoZwhWEQPgDlVOj61WsfjR2n\\nf+QjjFHrm6LWxZHornB2sIOl7iEFGkApDiYNnhOMfVzo\/Sn\/afp5yliRqQlKv6uc7OzhQ4ZeuqCX\\nH7NYrhLK6v\/pU0gDapVQqGAKFUypEn3AeksLor3QPu6HNBzcu6BvdT4f5IZ59ti0XM6dR41b6A62\\n6qjR6MMi8vn85Z98surzL1B3iyG\/JJiuumO66o5K7z9oeoXGas8pqz2nIq+uQl2IWEJott5hnXk6\\n+cm0AL2wHM0Zk\/wxXixV0Tnb95oq\/7+NmDdZ6OyAvv1Xq9XHY2LGjBx55\/Ztfayqk+o7uGnK5kCB\\n0Is0v5DQE3irBuuVW0WvcdK3Izrq8\/i7qBimWbm0\/ScaXVVZTLBjXTUV1itOcH5e\/pxZ0XOjZyPY\\nVVQCIH99pA+83r9q\/HKuvdDXHepKRlXDD\/Yc9EUVlRG3Id3L2UxXP5kW6CUsNx4rKgDT9ElUhgpm\\n7NxnmpLR+HZ6uat+2VjdPGy9h4fH77t37T1woE\/fvsgLvn3rVnjYkGUfLy0pafDOkKtEV5UVKxUl\\nkbK8TrJs5ut\/JrJsM1k2u\/7nHPN+vEMNzy46lXEtswr173QsqWzzrcYzsSH26ANc3biPozNcCzbr\\n3WRAesYNzygT+X0fJ1dhFnzc6fIXdHGFvY1y95ZqN6fGHuTvW8zV27gyef0g4+DhQ71fS0omk4X2\\n7VdZWYm8cEsLVXiodOFsDwtWUps5Fh6Vdh589juVprGdgCBgb0qnNAtoZ8aEfe1MhnlbTgm0o8GU\\nrArxTw8KnhYJCmplqtfJ19QaTUmdvKkaDlXyfMocOzaawB5PntFmrnxrw6POLaQicDqKotpkYEeL\\nmwv1TYyob7fd2ZYzr5fjzw9bJqlCjYrNBeKKknLq0rUWf2yudrCt75gCfOUOtsrc1+fAmP9\/TovB\\nYAwcNOj4MW0Zmpvo6KqYPEY8Llz8OlLQUySP9LbLmO1zedfzxnPxGg0ornt7P4AP0srEx5+Vv6yS\\nTPSz7fNTvPYp5Ajz8+hUpdaAXw8Sl+nkqyEYZK7DYC1q1WBXDGPBK9iN7+XLAtr8z3kl5ZQaPrRg\\nFS\/3\/08X7ty+XSJpXOKi0dveDre3UW78tPbUH5URo8RtxZ9qyQ\/9\/ljc9RyH2saK2jc38uafSNei\\nKopMwEs9lXn4ak4+mjf5xEXW4ySCTqJH+NuEYHE4GYOJhr0Z438DXdZceal\/UfXAb\/74LwtokxZa\\nmXA0r4rf2Hn\/3v2Tx49PmzGjzTDdNJpmyljR4ulCNgtlT02lqL\/vt+t\/gcfSa5wzapyqpGYaTWNX\\nqAGgSmp2vaB7Nt9RrQGPXgfrhpQyVkUGvbYA0rwWmUYDywT0uiIGv4CilAkBZeGXFod3VFlb6jA3\\njHtK3\/IbQRm\/WDTKhnAPTIrCZga7ItR5\/5MShGmotaOivjVErebD1e9kpTDnWjR4+V1\/\/6loF0fl\\n1ytre3TDYBvEhs23YaeEOrYy7lao4YmXV195FQRey8jhzhYm\/5WWoopKqVM+sfxpfU0nD0TLKBdv\\nMr\/eYd4wuCSA\/\/7HxdMamyPv2AiLTYe3juo4fj8GMX0VHOv3fdS7T28nJ+d+If2HjxhRP3PZtIn\/\\nngh3vQJl276ssTDHPZ8RjaL6LPCEs2lFQgrtZZpQu6oaKCqlzlhpOWuicE6kSEt87vJKyva9pueu\\nsRA60+qPE5fx6QDMtoD1nRU2Z\/Kh1KNP9d2hY1a+cLzbuvfL2Qvnfbt2BQBUVlRu2rjx\/Llzrd42\\nOkz87f+Izr0jkYLpyy3TsnRwgTThqKMnCXsFyrt1VjTNNWVyEJvIuBvLOH6J3dTttgrms8KbCwIG\\nemLmd46lsAprpQE\/PK4U6dX70GvynW5taPWjhYsXWfB4D+7de3D\/wfvCN0SMEq39BGVuEj1RqcC0\\nZZbP0tG417JZag5bU1tHUSiQtk\/YCmtyoO3hKb5YlYaxsAAAB+NLph9N06cESCF1O78EXes\/anB9\\nW4UwdQIeCITQvC94yai0pSsYCsvRjJG4MsjGBEuzMXZ9mdbDXk\/HZQ2NqTBB46Havats\/QoyVfXa\\no1rz84ZqF0eSs5Loyh8TO2OrKuyFBQDYPdHbxgS9c8jrHUOdN9XtrFXbv6phGMAhD0uuZuuqWiaj\\n3QRaig6yH6HjKWckYC8saxO6nr211FK3jSoqrNn6ZQ2Payg5bbt0Uq6cS\/5hQCR4WDJ\/Ht8Jj5Jx\\nSe8x2Iu3JswV9eMS68463T9ppDjQ17B6n4jR4uAALPMP4AGLRjkzyw95kD6dwCtvzBeDXAd7otwZ\\nUHJ0aJmteaolM9E3D2K913SFoubDusY\/EkwB\/50vYNB1a0RRZKHShz2TvLvq4cqnHbyExaLB+6N8\\nEGaKaoHCxFbBQrqgMm+y0NwUZSf4LI02bIbNzYfo\/5z7jnPGz7fKL6rXE9X2L6rN701faeeOytFh\\nYuRF+XTpcvn6tb79dPAF0ofFfR0n43OMrwEcM105mjPPzOrGoeteBQQpTBC5eXHNVJGjdfjjNaeq\\nBprzP15lNbz0K4vLt3V2ddJowE\/7TbbtMisqpc5cYQm438Cmk2CzaVT7kwBqbAbmRgnbdM5pILhX\\nr5gTx52dnXfv3dvFF8v1pFbp78b9YYwXrlXgm0Ktq73J4Sm+KFYAxPbdkNw2f4oQdRI4SwvNgqkN\\n7vDQp99YfL7ZHHmA\/tIKSvSnvN8PNbqyzJwoBMI\/gaq6vhPkDKc5xQF6vf2OdupxQ9vua1d+9tnh\\nmKNsNhsAEPvoURbOkbfdecwzs7rSYXz\/9Ljn5hvja71lpM7uiFKee5v3UGHN+GF6DZFmR4g+ia6D\\nKfWNyoUb7CFTbY6cY\/PrtL0IxWWUnftMhs+wefyM8bpt1Xy+iD9jghgoX8oLBwNlaf13SvdgOMfC\\n1jsAzWPCiPc2qAwGY9z48ddu3li4eFHDlevXri9esFD\/7D1asDGh3VoUaMnBfWEG45X39\/H5xawt\\nt9veoH2DRuN8ZRVNrC0+0eC+kh3rMUizdvwia\/OvbzwITDjqIf2lQX4ydxcVk6GhUTVCMSSRQk+e\\n0VMy6HfjGE1beEyGZv3y2pGDm83+qB40+yMURlOGerVKeOnbHxLjnyQIBHXVVdVMJtPaxsbe3r5P\\nv77Dhg+3sXlzGuXk8RNfrVmDPK8JipV3Dh2+Pt+\/t+6hY1BAkLAAAFGHUmN02aK2jt9vlv9Ayw2b\\n\/lszZgg2U\/rMHOoXW8wzX+rwHnu5KTZ8Wuvb6R3vF8iEarkR5s5B7jnC5\/N\/3Pb9oYMHdbBYd2Gx\\naZQjU33H+L7XeQRbiBOWSg0iDyafTEYaJI1TmGAX99v7P9c8vVyqq0eoFtRqcOEm6\/eDJs2dClvF\\n3kY5f4pwzBCJltoheleq5UYKZ5B2eUml0nNnzn6\/bVt1lc6HNXQSFgRAzDTfSf6YnnzRCnFH1WAK\\nODzFV6ZMuZCG6PiDyMFPyeRSpa13dl07KzBUVf3AiALGhElGDpQkZ9Cu32dm5VKT0uhiSeMYlGeu\\n8vFSuDsrB\/aRBXaVtznw1chTFCVjANUVNp0EMfwlmkEmpm+8QIsKC+Pi4uJiY2\/duFlTo+\/R+zaB\\nADg+3XeCH3GqIlRYAAA6lXJiRtfIgylnUxFoi0IV2fuZ595t9cPOHrgstcMwCOiiCOiiaGjDauvq\\nR1cmHA3KpTJlnqrmu\/pZwnLLogpnCx5PrVaXFBfX1RHn2ANB4ECkD8GqImJW2AIGlXJierepyJbm\\nhI7d3\/cRujhHOkGhAB5X42inRr0A24S7s7K0tDQ9LS0zI4NIVVEp0MnpXVFHjNEHooX1epkAOjCl\\ny+ygtn9bqa2Pktn6OYIunoa1Oagdax4JIbtM6PDfc\/zQxePTHxKE1VDrngifTcPbXqyq9Rza6nU3\\np\/YRzbYB4q3lsakPlnQP0yWtEraQI6wGvhjktjfCm03TZkOdW381pZWBoLmZoTjJIMEWVRA51Pg7\\nmCStDPZzJO6M67uQKSwAwKwgh2vzA7Q4BmpoLIFTL2KNwh5TDnGvwfDOltfnBzhxiQv00CokCwsA\\n0NeNm\/xpr27v99+o6TJWQ9QRKJwgzGF69WDXC3P8rbD2M0YB+cKq7ylM6XGf9JzXq\/U4qioWV+Dc\\nvhstKf5hablM6rlZ3TYM86AYxjtoEMJ6HXuS8sdE74OTfXjsVkZUtZ2Ga6C3TMVzoxZ7qmqw99JU\\nN\/vbBTmbJa4IGk3Udg0SDEVYDUztbv9gSY8+72TcVJjZ891Cm18pLDWItFgIKSzBQVg0VkMnu3qw\\n662FgW6ofCrxw7CE9TqrOefhxz02hLvT4bfa9NrOw1W0N2EFMnIw3dDBmZcF2O9wqJjm7pbM+4u7\\nbxjmwcEu2g9WGJywGlgd5pa0Mrhvs6ZLxbKo7jK26b\/J6e1JWDV8zL9naNmIwLT\/9u6rX0BH\/DBQ\\nYTU0XQ8+7vHLeK+mUON1riEy08YBfjGBATn1J+4pltM0H7+A0xcurJ8\/kYFDqH2sMFzLGljU1+n5\\nZ8GzguxhCgRgakXgdA1UL6l\/4gk90KIPFVUUvgCb18CCx1v99cbz505369oFkwLxw9CF9XoxgrE3\\nwufhku4DO1rIrDrWufYDAEhklLQsQl0zUHMvDoN3gMnmTJkx89qt2zOnT8HCKNxpH38bAECwi\/nN\\nhYE3s6qXnYD5RzLpwtIzV9g+nuQEltGJK\/f0WgSnUKnhI0Z99tlKpw46pzklEeI8SLFCrQaXHzzZ\\ntW1TWUHC3RPlZJvTBlU1lJCJNuiCp8FU2pjx42fPndvJE5vwjUTS\/oTVgFqtvnX7jrPJZhfLe2Tb\\noo29xzjf79Yt\/3t9P0JnTIyMmhU9y90V+yxrxNBehdWEVJSqqNpKkZykwgaXplClAn3H2wpEOgxk\\nbRw6RERGzps\/l2UIoXP0oN0LqwGVUiSu+AkSHaSBHLJteUPMedaGncjWmSAoZOCgydOmDx7QH3ez\\nCOEDEVYTgpoEWH5Jzf+FRiE6DGkLpDIQPs2morqNhQbvbv6TJ0cNGxbONde5xzRkPjRhNVFXeRWS\\n3wOSK1TVc9TH8PVhy2+mf51q3RfIxNSsq5\/f4LCwoeHhdrYGtHOMIR+ssJoQ8p8D6TW1+AakTKND\\nJcRU+iKXGrnYqnl8dogCe3h6+gd2Dw7uGRYWZsLBJpy6wfLhC6s5UmGyVBgPSf6mQ3lKSTqdhst4\\nXyyBJi2yzC2gmfOsnJw6dPMPCAsb7Onp+aE2Tq3y7xJWc6RSPlWTIxZmaqSJGlkCiy4W8DNNWSij\\njKhUQKp2YHNsJQrH\/MretfJe9ra2HTu6w6R0wwbAv1dYrVInKGFSSqmwhi8oA6pKJk0sEpZSNLVQ\\ns8yoSjWDzrCm0y3FcjbLxJFJ54hldCrTncn4wHs3nTAKywgu\/EsbaiN4YxSWEVwwCssILhiFZQQX\\njMIyggtGYRnBBaOwjOCCUVhGcOH\/AgAA\/\/+S+QX6OV9s5wAAAABJRU5ErkJggpQu\\n\",\"__pickled_extra_fields__\":[\"thumbnail\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_message": "{\"text\":\"Here's your document with tumbnail! Run \/start command again to restart.\",\"commands\":null,\"attachments\":[{\"source\":\"https:\/\/github.com\/deeppavlov\/dialog_flow_framework\/wiki\/example_attachments\/deeppavlov-article.pdf\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov article\",\"use_cache\":true,\"dff_attachment_type\":\"document\",\"filename\":\"deeppavlov_article.pdf\",\"thumbnail\":\"gASVySsAAAAAAABCwisAAIlQTkcNChoKAAAADUlIRFIAAADIAAAAyAgCAAAAIjo5yQAAK4lJREFU\\neJzsnXdcFFfXx+\/sbF\/KsnSQLiiIUlSwgo+KYi9RAbvYS4wleZ7EqNFojEaTqOlqLLFh77F3jaCA\\nCEgREJDeWbb394PwIiIus7NTFrPfj3\/g7My9h+U3t557DlWj0QAjRrCGQrYBRj5MjMIyggtGYRnB\\nBaOwjOCCUVhGcMEoLCO4YBSWEVwwCssILhiFZQQXqGQbgBIlf6+qYhHZVuAMbEOzP0NhdifbDjS0\\n1xZLVbmSbBPwR1WuEsSQbQRK2quwgEZCtgWEoBGTbQFK2q2wjBg2RmEZwQWjsIzgglFYRnDBKCw0\\n8GXsEhGPL2PrU4hcRX1a4Y6dUYZFe13H0s7DEu9tTycxYflgp8Ro76sQhOipx2Ve3yZEMWH5KNfY\\nyZ1ua7nzUn7Q7ucjulrm7gz5FbWRNwv9l91fkDl1DuoSDJkPU1ilYh6DIt8QvD\/65qdcunCoS8LG\\nJ5OFCtZnAcddzCrOvex9raB7oHXWbJ+rmxPq9ZfF77Cgy8VyiYVGAzb13jvk3OYulnnPq10flnSh\\nQqqvgw8czw515FQOd31yLCuExxBM9rrDl5mk1zg3VLcrddizKo\/edmmhDsm704Z9HXywWMj74\/mI\\n9cEHm1t1JPM\/D0u7MGH5V0EHM2s6XMzrNanjPZK+Idz5YLtCGqzqyC0Jc46\/Vej\/6YO5tqyavvap\\n824vyxdYr4md8XHXs\/vShqZVO5\/L7ZNe4+zFLVjxYD4AQAMgCqQGQEOnKDtxC77scVitATFZoQo1\\nvDF+MgDgh6cfmdFFzSs6md3vyIuBEzzurX88tUzC\/Tsv6G6R76X8IImS3sKkblYvV\/c4nFdnczwr\\n1MO8JNz5ybGsAcR+K8TxYbZYTYgVTAum8GZhQHKVuylNYsuqiSvtXCs3+eT+Ig5NSgFqAMB837\/N\\naKIfnk4AAMSWek++umplwClvXsHauGl3Cv3rFGwf3qto76urHkUnlHfkyznBdi+aV5FV6xhkm\/Gf\\nDsn97FOTq9wjve5cyO39qNRnR8gvDTeoNRAF0mg0YF\/6kCflncrEFiGOqTym0Ncyj6RvhQg+WGEV\\nCS1\/SxlxIifk0sjVpSIelyGY3OkOC5axqPX\/1vQ8DEEaJ9MKAEB6tVNiRcchzvEAgP4OqSeHbWgo\\n4VhW6Ibgv\/akhQMAmFTFoq7nh1345que9b3bS75dvsCmTMzN4dt15BZtSYi8VeB37VWPdUH1n\/Y5\\nucOGXdPdOhsAcDqnz+K7S4tnRQoVzN3Ph18Yufa\/D+sHVeVi82eV7jIV7XFZpyDbTLK\/LeyB161b\\nR7YNOpNUJLBWbtVyA5WigiDIhCZdH3zQw7w0xDGlQGjzoMS3g0mln3Wuv1XO+dzeVVKzYNuMvzLD\\nxEpmgHXOZ4EnmLDcgV3dyaKwoZBO3MLYMu+JHvd9eK\/sODU+vPwqqfmSbufYNHlcWSeJitnBpJIC\\naUa7xVox6+4U+23svd+HV8BliJmwfIRrnA+vAADAospMaeJ+DmkMWOlrmXe\/2HdOlyte3CKJkpFY\\n4dXLLqNAaNPbLv19v0iB2FvNGsamwzh8i\/gCta9zhQkFdRuu515IqxQvGIFJgb1Pbv8l9OfA162L\\nAbInLXz14+VL+nb4dICLOas9dS\/txtbcasmmG3kH4ksUKizfhEsjV5vSDXo\/u1ai3Hgjb09c8ReD\\nXJf2dyLbHKS0g1mhUqVefTnHc9M\/e+KKsVUVAIDHFNIoKmzLxINSgfyTsy\/cNj688aKabFsQYegt\\n1vnUioWnMorr5GQbYhDk1UjD\/ng6pBNvX4SPgzmDbHO0YbgtVoVQPn5f8ph9yUZVteBaZnWnzY9+\\neVBAtiHaMFBh7Xtc7L0l9kxqBdmGGChCuWrJmRcDfk14Xiok25bWMThhVYrkkw+mRh9LrxIrtNym\\n1iDb\/2vnKFTaFhru5tQG73iy61GRSk2gTcgwLGHdf1nTdWvc0aSyNu88n9uLEIvIRKGCrxcEar9H\\nJFfPP5kxdt+zaq3vIfEY0DrWqr+zt95+pVQjsodNlYY5JVix6vC3ixw0GpBY4ZlU2RHh\/Xam9PPR\\n3Xo6m+NsF1IMQlg1YsWMo2kX0irJNqR9w6RSNg33WB7qTLYhwCCElVkuGrsvOaO8vR5HMTSW9u+w\\nbZQnDSZ5kEOysO69rBn95zO+tB0sUbYj+rtzL0T7kbsFRKaw9sYVLziZoUA2qDKiE64WzDuLAl14\\nLLIMIEdYagDWXc7ZcONDdkgiHWcu88QM3yCShvMkCEupUn95Oee7268IrvdfiCWbejbar58bl\/iq\\nSRDWsrOZO+4XElzpvxZTBnw+2m9ARwuC6yVaWPNPpO+KLSayRiMsGuXvOf4Ea4vQSemS0xlGVRGP\\nRKEevifpQW4NkZUSJ6z\/Xcz65WERYdUZaY5EoR79Z3JCAXEbFQQJ69ubucbROrnUSJRDdydllImI\\nqY4IYR14Urzq75cEVGREO1UixcDfEssEMgLqwl1Yj1\/xF5z8AI83tVNKBPLwXUkiOe5bHfgKK7Nc\\nNHx3klRpeO5C\/2KSioXzTqTLcfbhwlFYApky8mBqlViJXxVG0HEksWzLLXy3PfASlkoN5p\/ISCo2\\nUMdZI19dyb2agaOfEl7C+vFe\/tGnbTuCGiELDQBRh57nVuF1phIXYT15Vbf6cg4eJRvBkBqJctJf\\nKTg5l2AvLIlCFXUoVaY0OsO0A+ILBeuv4tIEYC+sZWdf5ODWwBrBnK+v5z1+xce8WIyFdTWjyrgb\\n2O6IPJgqUWC8soWlsKpE8jnH3xuRx4jBklstXXEuC9sysXSbWXAi\/Y\/22FypVeySZJqwTOQYqDSx\\naf4JpJSZ5j9kVuXAMoGKaS4zcxS49FEzzcizFS9gCDxe1jOwA2a\/GmbCupdTM+j3pwhPBRoU1k\/2\\nmL2KrZ+BQ3DRgM9lPLfGD9Qqu39+4pSlNr9ZRWOV95gtdvAnx1Y88XcwebS0B5OGTZA3bLpChUq9\\n7NyL9qgqWFRl+lpVr18ylVnOraaPOMWJLVRVf79CYvfoF0algQZq04ekYuEvDzHz7MXmhNAfj4qe\\nFrXLRXYNBQYQBWgaN86cLU1H9LTvwGX06GBWEF+5Pe7Nnc4uLr6+vjeuX5fL5X1Lz2\/9JkahUqeW\\nCp8VCwv5sttZNbXSdr95tf5a7pRAOzszDAIkYdAVVork3ltiK0WGFTsAOc4pR2gv6hsqFpt1\/NQp\\nb2\/vhusymWxu9Ox\/Hj4EAMyZO3fFZ5\/S6fSE+PiICRMBAIdjjgb3ehM\/Qq5SpxQLb2fX3M+tvZxR\\nhXmAOMJY3Nfx5\/Gd9S8HA2GtbZ8HubhMamSA7YI+jn4OpokJiTk52UOHDjUzb3lYKutFFpfLtbax\\nbroya\/qM+\/furV67ZmZ0dKslK1TqI4mlx5LKr2RUtUd9PV0R5O9oqmch+naFxXzZd3fy9SyEYAId\\nTRf1dZzZ07HpGHpg98DA7q3HdfH08mxxxdHREQCQnfXeYRYNpszo6TCjp0OZQLb19qtdsUUCWXs6\\n6r3+2sszs\/z0LETfFuvj05k\/Yzfiw5t+buabhnv0d9frvMqd27dTklM6enYcNnw4kvsVKvWeuOLV\\nl3Oq248HUeKKoAD9Gi29hFUqkDl9\/bBdTAaDnM2+Dncf2smSLANEctX2e6++u51f1x4CVQzsaHFz\\nYRuhubSjl7AWn8r49R9DP3hjxoQ3hnss6ONIegAWAMCrGunnl7JjnpYZ\/rv4bGVQNwf0jRZ6YfGl\\nSpu19+SGPf0Z2om3P9IHk\/kzhlxKq1xwMqOQT8ShBtREBdgemeqL+nH0L\/E313MNWVVUCrRrYucr\\n8wIMTVUAgBE+Vokrg0Z3sSLbEG0cfVqWVYE+aBlKYSnVmt1xBrMtqFZRheX0mnxGTV7DP0+W9OHH\\n3ef2ciTbsvdizaGfi\/bbPMKDUfuKKqoAakMceP3+CP04B2VXeCSxdMrh56hr1R8av4hdlsoue04V\\nV9FElZDmzR+mi2+XP\/fvt7Iy6PagiatXrny2YqVIKpOb2Co51mIbH7G9n5JjEMYzqJSqDSEcVCmi\\nUAor8Ic4cvZw1ErT\/Fjuiyt0YWmrn\/fr33\/vgf0UCvnjdOQ8S3q2cN688vLypisSS8+aTsMk9t1I\\ntaue3z7qtKBPBxQPohHW00JB4I+PUVSmJ5xXsVYpJ6jS97o7jhk7dsu2rVSqoedxeZfCgoIJ48ZX\\nVr51bEbKdan0j5JZIg2cjAe9XMweLe2J4kE0b\/aBeKJHV1Rhuf397+2e7NGiqtFjxnz3\/bb2qCoA\\nQAcnp1Nnzzg4ODS\/yKzNd7yzhffsGKQkLelLbH7do7xaFA\/qLCylWnOW2EwkjKoch3tb2eXafFND\\nBwzYvPU7GG5\/CSObcOzQ4fc9u1tsVkJAY5F93fbRzxQ5ac4jp5LR\/Ll1Ftbl9Mr8GuIWYFhlaQ73\\nttIk2mI7+fr6bv9pJ53eMrl3u8PHx+fnX39lMpktrnPK0+zv\/0iRCUix6nRKOYrj+DoL62Ryuc6V\\noIVZnmH\/z06KWtsWm42NzW+7d5ma6rsbbyD06dtn9Vdr373OrM23f7gDaP0qcCK3WhpfoPMxHt2E\\nJVOqL6dX6VoHOmiCUtvHu6C2vsodP\/9kb29PjEnEEBkVNSli0rvXmTV5No93k2ER2BWr84KWbsJK\\nKKirIMahT6O2jt9HlbURge6T5ct7BgURYQ+xrFm3zt3D493rpkUJJv\/vSE0kt7J1DjOpm7CQ5OXC\\nBIv0i6zqNk7oevv4fPzJUmLsIRgWi7Xz558gqJXUedaJByGllGB78qqlz4p0G+HpJqzrmUQkJKbI\\nBOY5N9u4h0LZvnMHAcaQRWdv70VLlrx7naKScTOvEG\/PZR1D0+ggrIwyUaYeu5LI4WZegeVthMpc\\nsGihR0cyVw4JYM68ue7u7u9et3hxBaiIPmHwIFe38bsOwnpCSMxdSCExzX+o\/R4nZ+ely5YRYAy5\\nmJqafva\/\/757HVIrTfP\/IdiY6y+qq0Q6rNPqICxiFhpYFZnUthYD5y9c0E5X2HUlbOjQ3n36vHud\\nU5RIsCVyleZ5qQ4Rl3UQVjIh4fk4JUnab+js7T1u\/HgCLDEQlq9c8e5FZlU28b3h5QwdVpqQCiu9\\nVJRXg\/9kRKNhad26AQDMmh3NYBic7x5+BHbv7u\/f8kQ\/rJKxKl8QbEkaHi3Wy2oiQl5RheU0sbbX\\nwsbGZviIEQRYYlAsXd7KgJImIjrT8a2cGhniANhIhXU3h4hMLLCijVlnRFQki0Vackey6NO3r5OT\\nU4uLlLYmzpgjlKmQJ7ZAKqzCWiIW5WCtm80wDE+KiCDADEODSqV+NHFCi4uwhIg1xRZkI47ViHRu\\ndVP3RX0UwFrngz2Dguzf9lgiHpXghFp0UcsNsNl0CnsQ5vWOHT9+5\/YdavWbngjSkJCW4V5OzUfd\\nbJDciUhYLyvF5UIi5iCQVnfWEaNGEmCDdjTyFLXwmJYbKKw+AGAvrA4dOvQMCoqLfbNR2Mp2D\/5U\\nId4pRiSsKjH5kWRodNrgsDCyrQAAdoAYAVpvwOscxKDBg5oLixSQZ4RAJCxDiILs69vV2tqabCsA\\nlbsAcBeQUnW\/kBAAviGl6iYyKjAdvCcUkuO72JzQ0FCyTSAZLy8vNptNrg0qNRAgiy+HqMWqM4BY\\ndZ28MYgGpj+qugMqgbYxFsxdBHPwGguGDR1y7sxZnApHSEa5qKdzyyhi74JIWHyyhUWj0VrdMiMe\\njSJHI7ml7Q6TMfjVHhgYSLqwXlZJMBPWS7LHWB09PU1MTMi1oRGqE8QM1nYDbItf5W6tedEQDMIB\\nd\/sQlo0NorUTAqCazwXmc8mqPSBQr5hVmFCAbKkc0eCd9GSW3fzIP2xOInm5uc+Skhpclkk\/O4kw\\nzF77iHFga4tj\/2KwSCSSixcuTI2aPPg\/A9PT0houurm5kWuVGllMhvbhLkej0cg2gVAqKipOHj8R\\nc\/RoUWHL+K5OLs7Z2WSmL0AY6rJtYUnwz3jeJtYGM8bCG4lE8v3WrTFHjkqlrQ9l7OxIPkRZK8Fo\\nHYv0tQYAAJfLJdsE3MnLy1uz6stH\/7R0Znd3d1+\/cUPTaguX2\/ZUH1cQ6qFtYRlCNEhDWWvAAZVK\\ndfP6jZijR+\/dvdv8OgzDYUOGRERF9g8JaX7dhOxgAgj10D7GWKRPhfCgurr67OnTMUdjXua8dTSX\\nx+ONHjt2yrSprY7T28tX0T6EhTq085d\/Z1\/F9JDtwj6Os4P1DW2anZW1e9euy5f+Fovf8pj19PKa\\nO2\/ekPChWlpoDPNL4kr7EJZIhNINN7daiu0OeqkAfQw0pVJ58fyFYzFH45\/EN9cHlUodNnz4lGlT\\nu3fvDrUV5FIkJNojGR1tC8sEVWxTbOHzsc+GTSTV1dVHDh0+deJEQUFB8+s8Hi8iKnJSZOS7Lu3v\\ng89HE18PQxDqoW1hmTLJb9XKyvQNRjKzpz266L8NJBTWxeajPAi+etWqvy9eqqt763Hfrl0jo6KG\\njxxhZqZbttzSUoLisrwPHhuRHsgXDRJk71nUQc6m4R72emQS2HwrD7WwYo4cbfoZgqCh4eFz58\/3\\n80eZXutVPsm51syRNTTtQ1jV1SScSMEWM3Pz2XPmTJ4y2YLH06ecFlNI4oEpiLztEQnLnAnzSc1Z\\nlfwsmcTa9aRnUM\/IqKiRo0frv1Igk8kUCpLPH7QWtKsVEAnL3ZJFbsrn4iKUuTc6mDO62HIaUuvo\\nY4AVh9ZQjhVH513Lo8eP61N1c1JTUrAqCjUOyEYUiITlYckmV1g5OTkymQxFyIbvRnl+N6plilQU\\nzAl2nKP38pX+vMjMJNsE4GGJ6CQ6ImEhnAjgh0wmi330KHTAAMxLlkqlL3NyGg6C0mg0Ty8vAtKl\\nFBYW1tY0HgB2cHTkIR51paam4mkXItwxFBabRv5SVkZ6OrbCevjgwYF9+588fiwQvFlBtbGxCRkQ\\nOmPWrKZc9hhSXV198MCBy39fzs7KaroIQZB\/QMC48eMmToqg0dvoZ69eJiFIZAu8bThIbkP0dvo7\\nkr8HfO\/uPayKqigvX7p4yYyp027dvCkQCKhUqourq6ubG5VKLS8vP3n8xISx47Z8++37HFfQcfHC\\nhWFhQ37asbNBVRYWFp5enjweT6PRPE1MXLt6zbgxY5KfPWv12ZI6WWM7V0vy6igEAJeNaJSJqMXy\\nsUUkUlx5lpRUV1en63Liu7zMyZkxdVpJSQmDwZgwaeL0mTPNzMzOnjmjUqpGjRktkUgOHvjr+LGY\\n3X\/sSkxI\/H3XH3quDjTw7Tff\/Ll7DwDAz98\/Iipy7LhxTXk0FArF6VOnYg4fSUlJmTBu\/A87to8c\\nNar5s2dSyiMPpu6N8FanthHwlwA6WiEN9YOoxTIjb\/F9dqTgt01VENBIpdLrV6\/pWVpeXl7kpIiS\\nkpKQ0NCbd++s37DBxcUlcuLELZu+3fbddyPCh1lZWa3f8PWtu3eDe\/dKiI+fEhkll+ubIOnrdev+\\n3L2HzeHs+PmnU2fPTIqIaJ6dhUajRURGnrlwftPmzVQqddnHS4\/HxDR9uvZKzvj9KUq1Rq7S3Lh2\\nXU9L9KeHE1KnHUTC8rThcFkkaGvx9LoVc4QhQfIfv6qBIM2li9rCvCCBTqezmMyZ0bP2HthvZ2fX\\nMNLKz2tcyxYKBNevXgUA2Nvb\/3Xo0JixYyEKRf8O0dLS0tzc\/PS5syNGajvIOiky4ujxY6ampqrX\\nmWvUAET8lbLhep4ZA742P2CkKzU+Pl5PS\/THgoV0tQWRXCiv8+WfTiE06dfi6XWLpjfu5Hu5KV0c\\nlU8eP66qrLTUI3Wqg4PDqbNnrJrFgHBxdaXT6U3NkrOLS8MPMAxv+\/EHkUikv4\/h4o8\/njJ1KtfC\\nos07\/fz9H8Q+4nA4ZQL58N1JiUUCPweTmGm+nW04e\/7YJZORn5y8rytS\/1WkU2snLqFhP0eHiRdO\\na1RVVS20aLVFXiFNIpFc1LvRsno7soirq+vylSs4HA6DwZg1Ozq4V6+mjyAIwspzFYmqGuBw6oez\\n4\/clJxYJLNnUMzO7dX49Czt96hQmluhJZ8SjbaTCCvVA+tXoj7uzYu0n\/IatA6kMTF9umVfY2AIf\\n2Lcf8z2NufPnP05MiE96+uWaNdiWjJqTM7t6WLKqxMqPDiQX82WJCYkvXhAdyvZdGFTIG3NheVoR\\nFOcEhjVbV9Wy\/j9l3+qt5nkFb\/r1V\/n5N65jP4ZlMBgGFdrU3oyRsCJopLfl0yJhjx8ff\/bjfrIt\\nqmeAhwUL8YomUmF525o4mhGRaXJ0mKRzx8ZzILcfMS7faSnovXv2NI+Y+KFizqQem951RahTiUB+\\n2zJc0AFNYmZs8bXTYdUJqbBgCvDRpVx0MBia6EmNm5JyOdj6eyuT26eJT69euYq3JYYAmw5\/P9or\\nnJaloVCVLOKGIu9jaCdL5DfrsC\/2UVfcT42G9hC6Ozf658RcYOcXtT65\/XnnTrwtMRD+efgwK2aL\\n85UvLLL0XcPTEwgCvvY6TGV0EFZvxFNN1Ezo9ibfyZkr7x30ZGZk7NlFTq5RIlEoFN9u\/AYAQCMj\\n8nYLBna00MkFVwdhdXMwdeW1zIONITCkGuka1\/BzYirtRa62Id3O7dtLiovxM8YQ2L93X3p6Gwlg\\nCKOfm26H0XVbT\/+Ph8W+6hIdTUJKkE0GldI4Kk\/JaGOFVywWf7pi5eGYozgZQwxqeYZaeF6jyNYo\\nXwF1HQTbAKoDhdkLZg8qKFbt+PFHsg18w0gf3daldRPW1O52+57gJaxQxzf+x0+etT0DjYuNPX3q\\n1PiPPsLJHlxRS5+oqjepxZebX2w4aqiu26uE6JkJ7mymSCol32Hp9fIH3d9Rt+1\/3ZzaermYmzPx\\n+lU7mLzJOlRWiaiWLz\/\/orlvUztBoaz8QlE4sIWq3kIj7x+YcWlfxZAQ8gOh17\/z7lyqju6Put3O\\npsNhXhi4kbQO9N7\/vA+FQrFk0eKKCkI3MfVCLZUXR6lqfwSg7f0DM1PN1i9royeRHwt9fm+d3bJ1\\ndsNFmEqFMLKzslYuW65Ukh9rCQmKypUasQ7bnVQYLJstDA8ls91yMKP3cdU5jJTOwprgZ2tnSsQS\\nPHL+efhw7ZerybaibVSCE+q6P3V9CobBuhV8SwvSjt+N6WJN17UjRCEsKgUa44tLuhil+o0xpia6\\nfY\/Hjx37et06HIzCDrVIWbUW3aOmHM2SGaR1iONR9VFoTqRMCbRD8VSbpFa9CQfVK0Bnv82\/9h\/Y\\ntuU7rI3CDJXwIlDmon58fLjEzpqE7r6LLWcwqlE1GmH1d7fwssbeF+Dqq+5NP3u6ofkSf\/\/tt7Wr\\n16hV5AdNfReV6Lw+j1OpYEAvEhz95gSjTBCJ8gzd8hBndA9qoUBoWyZuHCSGBMnsrNHo48ihQ4sX\\nLRIKyTxe2yptJEpBgI8X0YfrYQhM64Eyli5KYc3sac\/UfUDXJmdeNoZwhWEQPgDlVOj61WsfjR2n\\nf+QjjFHrm6LWxZHornB2sIOl7iEFGkApDiYNnhOMfVzo\/Sn\/afp5yliRqQlKv6uc7OzhQ4ZeuqCX\\nH7NYrhLK6v\/pU0gDapVQqGAKFUypEn3AeksLor3QPu6HNBzcu6BvdT4f5IZ59ti0XM6dR41b6A62\\n6qjR6MMi8vn85Z98surzL1B3iyG\/JJiuumO66o5K7z9oeoXGas8pqz2nIq+uQl2IWEJott5hnXk6\\n+cm0AL2wHM0Zk\/wxXixV0Tnb95oq\/7+NmDdZ6OyAvv1Xq9XHY2LGjBx55\/Ztfayqk+o7uGnK5kCB\\n0Is0v5DQE3irBuuVW0WvcdK3Izrq8\/i7qBimWbm0\/ScaXVVZTLBjXTUV1itOcH5e\/pxZ0XOjZyPY\\nVVQCIH99pA+83r9q\/HKuvdDXHepKRlXDD\/Yc9EUVlRG3Id3L2UxXP5kW6CUsNx4rKgDT9ElUhgpm\\n7NxnmpLR+HZ6uat+2VjdPGy9h4fH77t37T1woE\/fvsgLvn3rVnjYkGUfLy0pafDOkKtEV5UVKxUl\\nkbK8TrJs5ut\/JrJsM1k2u\/7nHPN+vEMNzy46lXEtswr173QsqWzzrcYzsSH26ANc3biPozNcCzbr\\n3WRAesYNzygT+X0fJ1dhFnzc6fIXdHGFvY1y95ZqN6fGHuTvW8zV27gyef0g4+DhQ71fS0omk4X2\\n7VdZWYm8cEsLVXiodOFsDwtWUps5Fh6Vdh589juVprGdgCBgb0qnNAtoZ8aEfe1MhnlbTgm0o8GU\\nrArxTw8KnhYJCmplqtfJ19QaTUmdvKkaDlXyfMocOzaawB5PntFmrnxrw6POLaQicDqKotpkYEeL\\nmwv1TYyob7fd2ZYzr5fjzw9bJqlCjYrNBeKKknLq0rUWf2yudrCt75gCfOUOtsrc1+fAmP9\/TovB\\nYAwcNOj4MW0Zmpvo6KqYPEY8Llz8OlLQUySP9LbLmO1zedfzxnPxGg0ornt7P4AP0srEx5+Vv6yS\\nTPSz7fNTvPYp5Ajz8+hUpdaAXw8Sl+nkqyEYZK7DYC1q1WBXDGPBK9iN7+XLAtr8z3kl5ZQaPrRg\\nFS\/3\/08X7ty+XSJpXOKi0dveDre3UW78tPbUH5URo8RtxZ9qyQ\/9\/ljc9RyH2saK2jc38uafSNei\\nKopMwEs9lXn4ak4+mjf5xEXW4ySCTqJH+NuEYHE4GYOJhr0Z438DXdZceal\/UfXAb\/74LwtokxZa\\nmXA0r4rf2Hn\/3v2Tx49PmzGjzTDdNJpmyljR4ulCNgtlT02lqL\/vt+t\/gcfSa5wzapyqpGYaTWNX\\nqAGgSmp2vaB7Nt9RrQGPXgfrhpQyVkUGvbYA0rwWmUYDywT0uiIGv4CilAkBZeGXFod3VFlb6jA3\\njHtK3\/IbQRm\/WDTKhnAPTIrCZga7ItR5\/5MShGmotaOivjVErebD1e9kpTDnWjR4+V1\/\/6loF0fl\\n1ytre3TDYBvEhs23YaeEOrYy7lao4YmXV195FQRey8jhzhYm\/5WWoopKqVM+sfxpfU0nD0TLKBdv\\nMr\/eYd4wuCSA\/\/7HxdMamyPv2AiLTYe3juo4fj8GMX0VHOv3fdS7T28nJ+d+If2HjxhRP3PZtIn\/\\nngh3vQJl276ssTDHPZ8RjaL6LPCEs2lFQgrtZZpQu6oaKCqlzlhpOWuicE6kSEt87vJKyva9pueu\\nsRA60+qPE5fx6QDMtoD1nRU2Z\/Kh1KNP9d2hY1a+cLzbuvfL2Qvnfbt2BQBUVlRu2rjx\/Llzrd42\\nOkz87f+Izr0jkYLpyy3TsnRwgTThqKMnCXsFyrt1VjTNNWVyEJvIuBvLOH6J3dTttgrms8KbCwIG\\nemLmd46lsAprpQE\/PK4U6dX70GvynW5taPWjhYsXWfB4D+7de3D\/wfvCN0SMEq39BGVuEj1RqcC0\\nZZbP0tG417JZag5bU1tHUSiQtk\/YCmtyoO3hKb5YlYaxsAAAB+NLph9N06cESCF1O78EXes\/anB9\\nW4UwdQIeCITQvC94yai0pSsYCsvRjJG4MsjGBEuzMXZ9mdbDXk\/HZQ2NqTBB46Havats\/QoyVfXa\\no1rz84ZqF0eSs5Loyh8TO2OrKuyFBQDYPdHbxgS9c8jrHUOdN9XtrFXbv6phGMAhD0uuZuuqWiaj\\n3QRaig6yH6HjKWckYC8saxO6nr211FK3jSoqrNn6ZQ2Payg5bbt0Uq6cS\/5hQCR4WDJ\/Ht8Jj5Jx\\nSe8x2Iu3JswV9eMS68463T9ppDjQ17B6n4jR4uAALPMP4AGLRjkzyw95kD6dwCtvzBeDXAd7otwZ\\nUHJ0aJmteaolM9E3D2K913SFoubDusY\/EkwB\/50vYNB1a0RRZKHShz2TvLvq4cqnHbyExaLB+6N8\\nEGaKaoHCxFbBQrqgMm+y0NwUZSf4LI02bIbNzYfo\/5z7jnPGz7fKL6rXE9X2L6rN701faeeOytFh\\nYuRF+XTpcvn6tb79dPAF0ofFfR0n43OMrwEcM105mjPPzOrGoeteBQQpTBC5eXHNVJGjdfjjNaeq\\nBprzP15lNbz0K4vLt3V2ddJowE\/7TbbtMisqpc5cYQm438Cmk2CzaVT7kwBqbAbmRgnbdM5pILhX\\nr5gTx52dnXfv3dvFF8v1pFbp78b9YYwXrlXgm0Ktq73J4Sm+KFYAxPbdkNw2f4oQdRI4SwvNgqkN\\n7vDQp99YfL7ZHHmA\/tIKSvSnvN8PNbqyzJwoBMI\/gaq6vhPkDKc5xQF6vf2OdupxQ9vua1d+9tnh\\nmKNsNhsAEPvoURbOkbfdecwzs7rSYXz\/9Ljn5hvja71lpM7uiFKee5v3UGHN+GF6DZFmR4g+ia6D\\nKfWNyoUb7CFTbY6cY\/PrtL0IxWWUnftMhs+wefyM8bpt1Xy+iD9jghgoX8oLBwNlaf13SvdgOMfC\\n1jsAzWPCiPc2qAwGY9z48ddu3li4eFHDlevXri9esFD\/7D1asDGh3VoUaMnBfWEG45X39\/H5xawt\\nt9veoH2DRuN8ZRVNrC0+0eC+kh3rMUizdvwia\/OvbzwITDjqIf2lQX4ydxcVk6GhUTVCMSSRQk+e\\n0VMy6HfjGE1beEyGZv3y2pGDm83+qB40+yMURlOGerVKeOnbHxLjnyQIBHXVVdVMJtPaxsbe3r5P\\nv77Dhg+3sXlzGuXk8RNfrVmDPK8JipV3Dh2+Pt+\/t+6hY1BAkLAAAFGHUmN02aK2jt9vlv9Ayw2b\\n\/lszZgg2U\/rMHOoXW8wzX+rwHnu5KTZ8Wuvb6R3vF8iEarkR5s5B7jnC5\/N\/3Pb9oYMHdbBYd2Gx\\naZQjU33H+L7XeQRbiBOWSg0iDyafTEYaJI1TmGAX99v7P9c8vVyqq0eoFtRqcOEm6\/eDJs2dClvF\\n3kY5f4pwzBCJltoheleq5UYKZ5B2eUml0nNnzn6\/bVt1lc6HNXQSFgRAzDTfSf6YnnzRCnFH1WAK\\nODzFV6ZMuZCG6PiDyMFPyeRSpa13dl07KzBUVf3AiALGhElGDpQkZ9Cu32dm5VKT0uhiSeMYlGeu\\n8vFSuDsrB\/aRBXaVtznw1chTFCVjANUVNp0EMfwlmkEmpm+8QIsKC+Pi4uJiY2\/duFlTo+\/R+zaB\\nADg+3XeCH3GqIlRYAAA6lXJiRtfIgylnUxFoi0IV2fuZ595t9cPOHrgstcMwCOiiCOiiaGjDauvq\\nR1cmHA3KpTJlnqrmu\/pZwnLLogpnCx5PrVaXFBfX1RHn2ANB4ECkD8GqImJW2AIGlXJierepyJbm\\nhI7d3\/cRujhHOkGhAB5X42inRr0A24S7s7K0tDQ9LS0zI4NIVVEp0MnpXVFHjNEHooX1epkAOjCl\\ny+ygtn9bqa2Pktn6OYIunoa1Oagdax4JIbtM6PDfc\/zQxePTHxKE1VDrngifTcPbXqyq9Rza6nU3\\np\/YRzbYB4q3lsakPlnQP0yWtEraQI6wGvhjktjfCm03TZkOdW381pZWBoLmZoTjJIMEWVRA51Pg7\\nmCStDPZzJO6M67uQKSwAwKwgh2vzA7Q4BmpoLIFTL2KNwh5TDnGvwfDOltfnBzhxiQv00CokCwsA\\n0NeNm\/xpr27v99+o6TJWQ9QRKJwgzGF69WDXC3P8rbD2M0YB+cKq7ylM6XGf9JzXq\/U4qioWV+Dc\\nvhstKf5hablM6rlZ3TYM86AYxjtoEMJ6HXuS8sdE74OTfXjsVkZUtZ2Ga6C3TMVzoxZ7qmqw99JU\\nN\/vbBTmbJa4IGk3Udg0SDEVYDUztbv9gSY8+72TcVJjZ891Cm18pLDWItFgIKSzBQVg0VkMnu3qw\\n662FgW6ofCrxw7CE9TqrOefhxz02hLvT4bfa9NrOw1W0N2EFMnIw3dDBmZcF2O9wqJjm7pbM+4u7\\nbxjmwcEu2g9WGJywGlgd5pa0Mrhvs6ZLxbKo7jK26b\/J6e1JWDV8zL9naNmIwLT\/9u6rX0BH\/DBQ\\nYTU0XQ8+7vHLeK+mUON1riEy08YBfjGBATn1J+4pltM0H7+A0xcurJ8\/kYFDqH2sMFzLGljU1+n5\\nZ8GzguxhCgRgakXgdA1UL6l\/4gk90KIPFVUUvgCb18CCx1v99cbz505369oFkwLxw9CF9XoxgrE3\\nwufhku4DO1rIrDrWufYDAEhklLQsQl0zUHMvDoN3gMnmTJkx89qt2zOnT8HCKNxpH38bAECwi\/nN\\nhYE3s6qXnYD5RzLpwtIzV9g+nuQEltGJK\/f0WgSnUKnhI0Z99tlKpw46pzklEeI8SLFCrQaXHzzZ\\ntW1TWUHC3RPlZJvTBlU1lJCJNuiCp8FU2pjx42fPndvJE5vwjUTS\/oTVgFqtvnX7jrPJZhfLe2Tb\\noo29xzjf79Yt\/3t9P0JnTIyMmhU9y90V+yxrxNBehdWEVJSqqNpKkZykwgaXplClAn3H2wpEOgxk\\nbRw6RERGzps\/l2UIoXP0oN0LqwGVUiSu+AkSHaSBHLJteUPMedaGncjWmSAoZOCgydOmDx7QH3ez\\nCOEDEVYTgpoEWH5Jzf+FRiE6DGkLpDIQPs2morqNhQbvbv6TJ0cNGxbONde5xzRkPjRhNVFXeRWS\\n3wOSK1TVc9TH8PVhy2+mf51q3RfIxNSsq5\/f4LCwoeHhdrYGtHOMIR+ssJoQ8p8D6TW1+AakTKND\\nJcRU+iKXGrnYqnl8dogCe3h6+gd2Dw7uGRYWZsLBJpy6wfLhC6s5UmGyVBgPSf6mQ3lKSTqdhst4\\nXyyBJi2yzC2gmfOsnJw6dPMPCAsb7Onp+aE2Tq3y7xJWc6RSPlWTIxZmaqSJGlkCiy4W8DNNWSij\\njKhUQKp2YHNsJQrH\/MretfJe9ra2HTu6w6R0wwbAv1dYrVInKGFSSqmwhi8oA6pKJk0sEpZSNLVQ\\ns8yoSjWDzrCm0y3FcjbLxJFJ54hldCrTncn4wHs3nTAKywgu\/EsbaiN4YxSWEVwwCssILhiFZQQX\\njMIyggtGYRnBBaOwjOCCUVhGcOH\/AgAA\/\/+S+QX6OV9s5wAAAABJRU5ErkJggpQu\\n\",\"__pickled_extra_fields__\":[\"thumbnail\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ "send_document(42, <>, caption=\"Here's your document with tumbnail! Run /start command again to restart.\", disable_notification=None, reply_markup=None, parse_mode=None, thumbnail=<>, filename='deeppavlov_article.pdf')" ] diff --git a/tests/messengers/telegram/utils.py b/tests/messengers/telegram/utils.py index 06f603142..a3860c051 100644 --- a/tests/messengers/telegram/utils.py +++ b/tests/messengers/telegram/utils.py @@ -1,11 +1,11 @@ from asyncio import get_event_loop -from typing import Any, Hashable, List, Optional, Tuple, TypeAlias +from contextlib import contextmanager +from typing import Any, Hashable, Iterator, List, Optional, Tuple, TypeAlias from pydantic import BaseModel from telegram import Update from dff.messengers.telegram.abstract import _AbstractTelegramInterface -from dff.pipeline.types import PipelineRunnerFunction from dff.script import Message from dff.script.core.context import Context from dff.script.core.message import DataAttachment @@ -47,48 +47,51 @@ def create(cls, interface: _AbstractTelegramInterface, happy_path: List[PathStep instance = cls(bot=MockBot(), happy_path=happy_path, interface=interface) return instance - def _wrap_pipeline_runner(self, runner: PipelineRunnerFunction): + @contextmanager + def _check_context_and_trace(self, last_request: Message, last_response: Message, last_trace: List[str]) -> Iterator[None]: + self.bot.latest_trace = list() + self.latest_ctx = None + yield + assert self.latest_ctx is not None, "During pipeline runner execution, no context was produced!" + assert self.latest_ctx.last_request == last_request, "Expected request message is not equal to expected!" + assert self.latest_ctx.last_response == last_response, "Expected response message is not equal to expected!" + assert self.bot.latest_trace == last_trace, "Expected trace is not equal to expected!" + + @contextmanager + def _wrap_pipeline_runner(self) -> Iterator[None]: + original_pipeline_runner = self.interface._pipeline_runner + async def wrapped_pipeline_runner( message: Message, ctx_id: Optional[Hashable] = None, update_ctx_misc: Optional[dict] = None ) -> Context: - self.latest_ctx = await runner(message, ctx_id, update_ctx_misc) + self.latest_ctx = await original_pipeline_runner(message, ctx_id, update_ctx_misc) return self.latest_ctx - wrapped_pipeline_runner.is_wrapped = True - wrapped_pipeline_runner.original = runner - return wrapped_pipeline_runner + self.interface._pipeline_runner = wrapped_pipeline_runner + yield + self.interface._pipeline_runner = original_pipeline_runner - def _wrap_populate_attachment(self, interface: _AbstractTelegramInterface): + @contextmanager + def _wrap_populate_attachment(self) -> Iterator[None]: async def wrapped_populate_attachment(_: DataAttachment) -> bytes: return bytes() - - wrapped_populate_attachment.is_wrapped = True - wrapped_populate_attachment.original = interface.populate_attachment - return wrapped_populate_attachment - def _run_bot(self) -> None: - if not getattr(self.interface.pipeline_runner, "is_wrapped", False): - self.interface.pipeline_runner = self._wrap_pipeline_runner(self.interface.pipeline_runner) - if not getattr(self.interface.populate_attachment, "is_wrapped", False): - self.interface.populate_attachment = self._wrap_populate_attachment(self.interface) + original_populate_attachment = self.interface.populate_attachment + self.interface.populate_attachment = wrapped_populate_attachment + yield + self.interface.populate_attachment = original_populate_attachment + def _run_bot(self) -> None: loop = get_event_loop() - for update, received, response, trace in self.happy_path: - if update.message is not None: - loop.run_until_complete(self.interface.on_message(update, None)) # type: ignore - elif update.callback_query is not None: - loop.run_until_complete(self.interface.on_callback(update, None)) # type: ignore - else: - raise RuntimeError(f"Update {update} type unknown!") - assert self.latest_ctx is not None, "During pipeline runner execution, no context was produced!" - assert self.latest_ctx.last_request == received, "Expected request message is not equal to expected!" - assert self.latest_ctx.last_response == response, "Expected response message is not equal to expected!" - assert self.bot.latest_trace == trace, "Expected trace is not equal to expected!" - self.bot.latest_trace = list() - self.latest_ctx = None - - self.interface.pipeline_runner = self.interface.pipeline_runner.original - self.interface.populate_attachment = self.interface.populate_attachment.original + with self._wrap_pipeline_runner(), self._wrap_populate_attachment(): + for update, received, response, trace in self.happy_path: + with self._check_context_and_trace(received, response, trace): + if update.message is not None: + loop.run_until_complete(self.interface.on_message(update, None)) # type: ignore + elif update.callback_query is not None: + loop.run_until_complete(self.interface.on_callback(update, None)) # type: ignore + else: + raise RuntimeError(f"Update {update} type unknown!") def run_polling(self, poll_interval: float, timeout: int, allowed_updates: List[str]) -> None: return self._run_bot() diff --git a/tests/script/core/test_message.py b/tests/script/core/test_message.py index 9cd2f0e4a..3d0b74293 100644 --- a/tests/script/core/test_message.py +++ b/tests/script/core/test_message.py @@ -1,5 +1,6 @@ from pathlib import Path from shutil import rmtree +from tempfile import gettempdir from typing import Hashable, Optional, TextIO from urllib.request import urlopen @@ -27,7 +28,8 @@ Video, ) -EXAMPLE_SOURCE = "https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@wiki/example-attachments" +TEMP_PATH = Path(gettempdir()) / "dff_script_test" +EXAMPLE_SOURCE = "https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments" class DFFCLIMessengerInterface(CLIMessengerInterface): @@ -50,7 +52,7 @@ async def populate_attachment(self, attachment: DataAttachment) -> bytes: class TestMessage: @pytest.fixture def json_context_storage(self) -> DBContextStorage: - return JSONContextStorage(f"file://{Path(__file__).parent / 'serialization_database.json'}") + return JSONContextStorage(f"file://{TEMP_PATH / 'serialization_database.json'}") def clear_and_create_dir(self, dir: Path) -> Path: rmtree(dir, ignore_errors=True) @@ -129,11 +131,10 @@ def test_attachment_serialize(self, json_context_storage: DBContextStorage, atta @pytest.mark.asyncio async def test_getting_attachment_bytes(self): - root_dir = Path(__file__).parent - local_path = self.clear_and_create_dir(root_dir / "local") + local_path = self.clear_and_create_dir(TEMP_PATH / "local") local_document = local_path / "pre-saved-document.pdf" - cli_iface = DFFCLIMessengerInterface(self.clear_and_create_dir(root_dir / "cache")) + cli_iface = DFFCLIMessengerInterface(self.clear_and_create_dir(TEMP_PATH / "cache")) document_name = "deeppavlov-article.pdf" remote_document_url = f"{EXAMPLE_SOURCE}/{document_name}" From 121b0925b1886b028368867a70a721cddb1d24d6 Mon Sep 17 00:00:00 2001 From: pseusys Date: Thu, 6 Jun 2024 00:52:11 +0200 Subject: [PATCH 080/140] additional `Message` serialization test introduced --- .gitignore | 3 --- tests/script/core/test_message.py | 39 +++++++++++++++++++++++++------ 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index bdba7af28..a6828357b 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,3 @@ dbs benchmarks benchmark_results_files.json uploaded_benchmarks -tests/script/core/serialization_database.json -tests/script/core/cache/ -tests/script/core/local/ diff --git a/tests/script/core/test_message.py b/tests/script/core/test_message.py index 3d0b74293..5a8179120 100644 --- a/tests/script/core/test_message.py +++ b/tests/script/core/test_message.py @@ -1,6 +1,6 @@ from pathlib import Path +from random import randint, randbytes from shutil import rmtree -from tempfile import gettempdir from typing import Hashable, Optional, TextIO from urllib.request import urlopen @@ -28,10 +28,21 @@ Video, ) -TEMP_PATH = Path(gettempdir()) / "dff_script_test" EXAMPLE_SOURCE = "https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments" +class UnserializableObject: + def __init__(self, number: int, string: bytes) -> None: + self.number = number + self.bytes = string + + def __eq__(self, value: object) -> bool: + if isinstance(value, UnserializableObject): + return self.number == value.number and self.bytes == value.bytes + else: + return False + + class DFFCLIMessengerInterface(CLIMessengerInterface): def __init__(self, attachments_directory: Optional[Path] = None): MessengerInterface.__init__(self, attachments_directory) @@ -51,8 +62,12 @@ async def populate_attachment(self, attachment: DataAttachment) -> bytes: class TestMessage: @pytest.fixture - def json_context_storage(self) -> DBContextStorage: - return JSONContextStorage(f"file://{TEMP_PATH / 'serialization_database.json'}") + def json_context_storage(self, tmp_path) -> DBContextStorage: + return JSONContextStorage(f"file://{tmp_path / 'serialization_database.json'}") + + @pytest.fixture + def random_original_message(self) -> UnserializableObject: + return UnserializableObject(randint(0, 256), randbytes(32)) def clear_and_create_dir(self, dir: Path) -> Path: rmtree(dir, ignore_errors=True) @@ -129,12 +144,22 @@ def test_attachment_serialize(self, json_context_storage: DBContextStorage, atta retrieved = json_context_storage[name].requests[0].attachments[0] assert attachment == retrieved + def test_field_serializable(self, json_context_storage: DBContextStorage, random_original_message: UnserializableObject): + name = "serializable_test" + message = Message(text="sample message") + message.misc = {"answer": 42} + message.original_message = random_original_message + message.some_extra_field = {"unserializable": random_original_message} + json_context_storage[name] = Context(requests={0: message}) + retrieved = json_context_storage[name].requests[0] + assert message == retrieved + @pytest.mark.asyncio - async def test_getting_attachment_bytes(self): - local_path = self.clear_and_create_dir(TEMP_PATH / "local") + async def test_getting_attachment_bytes(self, tmp_path): + local_path = self.clear_and_create_dir(tmp_path / "local") local_document = local_path / "pre-saved-document.pdf" - cli_iface = DFFCLIMessengerInterface(self.clear_and_create_dir(TEMP_PATH / "cache")) + cli_iface = DFFCLIMessengerInterface(self.clear_and_create_dir(tmp_path / "cache")) document_name = "deeppavlov-article.pdf" remote_document_url = f"{EXAMPLE_SOURCE}/{document_name}" From 5172f0c4b8f6ae7fb977a95b52981df917b42f67 Mon Sep 17 00:00:00 2001 From: pseusys Date: Fri, 7 Jun 2024 19:10:10 +0200 Subject: [PATCH 081/140] extra field verification added --- dff/messengers/telegram/abstract.py | 103 +++++--------------------- dff/utils/messengers/__init__.py | 1 + dff/utils/messengers/verify_params.py | 7 ++ dff/utils/pydantic/__init__.py | 56 +------------- dff/utils/pydantic/serializing.py | 55 ++++++++++++++ 5 files changed, 82 insertions(+), 140 deletions(-) create mode 100644 dff/utils/messengers/__init__.py create mode 100644 dff/utils/messengers/verify_params.py create mode 100644 dff/utils/pydantic/serializing.py diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index 50c4742b6..a8ec3a898 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -8,6 +8,8 @@ from pathlib import Path from typing import Callable, Optional +from dff.utils.messengers.verify_params import generate_extra_fields + try: from telegram import ( InputMediaAnimation, @@ -206,10 +208,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes chat_id, attachment.latitude, attachment.longitude, - horizontal_accuracy=attachment.__pydantic_extra__.get("horizontal_accuracy", None), - disable_notification=attachment.__pydantic_extra__.get("disable_notification", None), - protect_content=attachment.__pydantic_extra__.get("protect_content", None), - reply_markup=attachment.__pydantic_extra__.get("reply_markup", None), + **generate_extra_fields(attachment, ["horizontal_accuracy", "disable_notification", "protect_content", "reply_markup"]), ) if isinstance(attachment, Contact): await bot.send_contact( @@ -217,36 +216,21 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes attachment.phone_number, attachment.first_name, attachment.last_name, - vcard=attachment.__pydantic_extra__.get("vcard", None), - disable_notification=attachment.__pydantic_extra__.get("disable_notification", None), - protect_content=attachment.__pydantic_extra__.get("protect_content", None), - reply_markup=attachment.__pydantic_extra__.get("reply_markup", None), + **generate_extra_fields(attachment, ["vcard", "disable_notification", "protect_content", "reply_markup"]), ) if isinstance(attachment, Poll): await bot.send_poll( chat_id, attachment.question, [option.text for option in attachment.options], - is_anonymous=attachment.__pydantic_extra__.get("is_anonymous", None), - type=attachment.__pydantic_extra__.get("type", None), - allows_multiple_answers=attachment.__pydantic_extra__.get("allows_multiple_answers", None), - correct_option_id=attachment.__pydantic_extra__.get("correct_option_id", None), - explanation=attachment.__pydantic_extra__.get("explanation", None), - explanation_parse_mode=attachment.__pydantic_extra__.get("explanation_parse_mode", None), - open_period=attachment.__pydantic_extra__.get("open_period", None), - is_closed=attachment.__pydantic_extra__.get("is_closed", None), - disable_notification=attachment.__pydantic_extra__.get("disable_notification", None), - protect_content=attachment.__pydantic_extra__.get("protect_content", None), - reply_markup=attachment.__pydantic_extra__.get("reply_markup", None), + **generate_extra_fields(attachment, ["is_anonymous", "type", "allows_multiple_answers", "correct_option_id", "explanation", "explanation_parse_mode", "open_period", "is_closed", "disable_notification", "protect_content", "reply_markup"]), ) if isinstance(attachment, Sticker): sticker = await attachment.get_bytes(self) if attachment.id is None else attachment.id await bot.send_sticker( chat_id, sticker, - disable_notification=attachment.__pydantic_extra__.get("disable_notification", None), - reply_markup=attachment.__pydantic_extra__.get("reply_markup", None), - emoji=attachment.__pydantic_extra__.get("emoji", None), + **generate_extra_fields(attachment, ["disable_notification", "protect_content", "reply_markup"]), ) if isinstance(attachment, Audio): attachment_bytes = await attachment.get_bytes(self) @@ -255,25 +239,15 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes files += [ InputMediaAudio( attachment_bytes, - filename=attachment.__pydantic_extra__.get("filename", None), - caption=attachment.__pydantic_extra__.get("caption", None), - parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), - performer=attachment.__pydantic_extra__.get("performer", None), - title=attachment.__pydantic_extra__.get("title", None), - thumbnail=attachment.__pydantic_extra__.get("thumbnail", None), + **generate_extra_fields(attachment, ["filename", "caption", "parse_mode", "performer", "title", "thumbnail"]), ), ] else: await bot.send_audio( chat_id, audio=attachment_bytes, - performer=attachment.__pydantic_extra__.get("performer", None), - title=attachment.__pydantic_extra__.get("title", None), caption=message.text, - disable_notification=attachment.__pydantic_extra__.get("disable_notification", None), - reply_markup=attachment.__pydantic_extra__.get("reply_markup", None), - parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), - thumbnail=attachment.__pydantic_extra__.get("thumbnail", None), + **generate_extra_fields(attachment, ["performer", "title", "disable_notification", "reply_markup", "parse_mode", "thumbnail"]), ) return if isinstance(attachment, Video): @@ -283,12 +257,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes files += [ InputMediaVideo( attachment_bytes, - filename=attachment.__pydantic_extra__.get("filename", None), - caption=attachment.__pydantic_extra__.get("caption", None), - parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), - supports_streaming=attachment.__pydantic_extra__.get("supports_streaming", None), - has_spoiler=attachment.__pydantic_extra__.get("has_spoiler", None), - thumbnail=attachment.__pydantic_extra__.get("thumbnail", None), + **generate_extra_fields(attachment, ["filename", "caption", "parse_mode", "supports_streaming", "has_spoiler", "thumbnail"]), ), ] else: @@ -296,13 +265,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes chat_id, attachment_bytes, caption=message.text, - disable_notification=attachment.__pydantic_extra__.get("disable_notification", None), - reply_markup=attachment.__pydantic_extra__.get("reply_markup", None), - parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), - supports_streaming=attachment.__pydantic_extra__.get("supports_streaming", None), - has_spoiler=attachment.__pydantic_extra__.get("has_spoiler", None), - thumbnail=attachment.__pydantic_extra__.get("thumbnail", None), - filename=attachment.__pydantic_extra__.get("filename", None), + **generate_extra_fields(attachment, ["disable_notification", "reply_markup", "parse_mode", "supports_streaming", "has_spoiler", "thumbnail", "filename"]), ) return if isinstance(attachment, Animation): @@ -312,11 +275,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes files += [ InputMediaAnimation( attachment_bytes, - filename=attachment.__pydantic_extra__.get("filename", None), - caption=attachment.__pydantic_extra__.get("caption", None), - parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), - has_spoiler=attachment.__pydantic_extra__.get("has_spoiler", None), - thumbnail=attachment.__pydantic_extra__.get("thumbnail", None), + **generate_extra_fields(attachment, ["filename", "caption", "parse_mode", "has_spoiler", "thumbnail"]), ), ] else: @@ -324,12 +283,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes chat_id, attachment_bytes, caption=message.text, - parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), - disable_notification=attachment.__pydantic_extra__.get("disable_notification", None), - reply_markup=attachment.__pydantic_extra__.get("reply_markup", None), - has_spoiler=attachment.__pydantic_extra__.get("has_spoiler", None), - thumbnail=attachment.__pydantic_extra__.get("thumbnail", None), - filename=attachment.__pydantic_extra__.get("filename", None), + **generate_extra_fields(attachment, ["parse_mode", "disable_notification", "reply_markup", "has_spoiler", "thumbnail", "filename"]), ) return if isinstance(attachment, Image): @@ -339,10 +293,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes files += [ InputMediaPhoto( attachment_bytes, - filename=attachment.__pydantic_extra__.get("filename", None), - caption=attachment.__pydantic_extra__.get("caption", None), - parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), - has_spoiler=attachment.__pydantic_extra__.get("has_spoiler", None), + **generate_extra_fields(attachment, ["filename", "caption", "parse_mode", "has_spoiler"]), ), ] else: @@ -350,11 +301,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes chat_id, attachment_bytes, caption=message.text, - disable_notification=attachment.__pydantic_extra__.get("disable_notification", None), - reply_markup=attachment.__pydantic_extra__.get("reply_markup", None), - parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), - has_spoiler=attachment.__pydantic_extra__.get("has_spoiler", None), - filename=attachment.__pydantic_extra__.get("filename", None), + **generate_extra_fields(attachment, ["disable_notification", "reply_markup", "parse_mode", "has_spoiler", "filename"]), ) return if isinstance(attachment, Document): @@ -364,13 +311,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes files += [ InputMediaDocument( attachment_bytes, - filename=attachment.__pydantic_extra__.get("filename", None), - caption=attachment.__pydantic_extra__.get("caption", None), - parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), - disable_content_type_detection=attachment.__pydantic_extra__.get( - "disable_content_type_detection", None - ), - thumbnail=attachment.__pydantic_extra__.get("thumbnail", None), + **generate_extra_fields(attachment, ["filename", "caption", "parse_mode", "disable_content_type_detection", "thumbnail"]), ), ] else: @@ -378,30 +319,22 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes chat_id, attachment_bytes, caption=message.text, - disable_notification=attachment.__pydantic_extra__.get("disable_notification", None), - reply_markup=attachment.__pydantic_extra__.get("reply_markup", None), - parse_mode=attachment.__pydantic_extra__.get("parse_mode", None), - thumbnail=attachment.__pydantic_extra__.get("thumbnail", None), - filename=attachment.__pydantic_extra__.get("filename", None), + **generate_extra_fields(attachment, ["disable_notification", "reply_markup", "parse_mode", "thumbnail", "filename"]), ) return if len(files) > 0: await bot.send_media_group( chat_id, files, - disable_notification=message.__pydantic_extra__.get("disable_notification", None), - protect_content=message.__pydantic_extra__.get("protect_content", None), caption=message.text, + **generate_extra_fields(attachment, ["disable_notification", "protect_content"]), ) return if message.text is not None: await bot.send_message( chat_id, message.text, - parse_mode=message.__pydantic_extra__.get("parse_mode", None), - disable_notification=message.__pydantic_extra__.get("disable_notification", None), - protect_content=message.__pydantic_extra__.get("protect_content", None), - reply_markup=message.__pydantic_extra__.get("reply_markup", None), + **generate_extra_fields(attachment, ["parse_mode", "disable_notification", "protect_content", "reply_markup"]), ) async def _on_event( diff --git a/dff/utils/messengers/__init__.py b/dff/utils/messengers/__init__.py new file mode 100644 index 000000000..09e683272 --- /dev/null +++ b/dff/utils/messengers/__init__.py @@ -0,0 +1 @@ +from .verify_params import generate_extra_fields diff --git a/dff/utils/messengers/verify_params.py b/dff/utils/messengers/verify_params.py new file mode 100644 index 000000000..34e8ad12c --- /dev/null +++ b/dff/utils/messengers/verify_params.py @@ -0,0 +1,7 @@ +from typing import List + +from pydantic import BaseModel + + +def generate_extra_fields(attachment: BaseModel, extra_fields: List[str]): + return {extra_field: attachment.__pydantic_extra__.get(extra_field, None) for extra_field in extra_fields} diff --git a/dff/utils/pydantic/__init__.py b/dff/utils/pydantic/__init__.py index 31bb68879..656538e90 100644 --- a/dff/utils/pydantic/__init__.py +++ b/dff/utils/pydantic/__init__.py @@ -1,55 +1 @@ -from base64 import decodebytes, encodebytes -from copy import deepcopy -from pickle import dumps, loads -from typing import Annotated, Any, Callable -from pydantic import AfterValidator, JsonValue, PlainSerializer, PlainValidator, PydanticSchemaGenerationError, TypeAdapter, WrapSerializer, WrapValidator - -_JSON_EXTRA_FIELDS_KEYS = "__pickled_extra_fields__" - - -def pickle_serializer(value: Any) -> JsonValue: - return encodebytes(dumps(value)).decode() - - -def pickle_validator(value: JsonValue) -> Any: - return loads(decodebytes(value.encode())) - - -def json_pickle_serializer(model: JsonValue, original_serializer: Callable[[JsonValue], JsonValue]) -> JsonValue: - extra_fields = list() - model_copy = deepcopy(model) - - for field_name, field_value in model_copy.items(): - try: - if isinstance(field_value, bytes): - raise PydanticSchemaGenerationError("") - else: - TypeAdapter(type(field_value)) - except PydanticSchemaGenerationError: - model_copy[field_name] = pickle_serializer(field_value) - extra_fields += [field_name] - - original_dump = original_serializer(model_copy) - original_dump[_JSON_EXTRA_FIELDS_KEYS] = extra_fields - return original_dump - - -def json_pickle_validator(model: JsonValue) -> JsonValue: - model_copy = deepcopy(model) - - if _JSON_EXTRA_FIELDS_KEYS in model.keys(): - for extra_key in model[_JSON_EXTRA_FIELDS_KEYS]: - extra_value = model[extra_key] - model_copy[extra_key] = pickle_validator(extra_value) - del model_copy[_JSON_EXTRA_FIELDS_KEYS] - - return model_copy - - -PickleSerializer = PlainSerializer(pickle_serializer, when_used="json") -PickleValidator = PlainValidator(pickle_validator) -SerializableVaue = Annotated[Any, PickleSerializer, PickleValidator] - -JSONPickleSerializer = WrapSerializer(json_pickle_serializer, when_used="json") -JSONPickleValidator = AfterValidator(json_pickle_validator) -JSONSerializableDict = Annotated[JsonValue, JSONPickleSerializer, JSONPickleValidator] +from .serializing import JSONSerializableDict, SerializableVaue, json_pickle_serializer, json_pickle_validator, pickle_serializer, pickle_validator diff --git a/dff/utils/pydantic/serializing.py b/dff/utils/pydantic/serializing.py new file mode 100644 index 000000000..3d59a6718 --- /dev/null +++ b/dff/utils/pydantic/serializing.py @@ -0,0 +1,55 @@ +from base64 import decodebytes, encodebytes +from copy import deepcopy +from pickle import dumps, loads +from typing import Annotated, Any, Callable +from pydantic import AfterValidator, JsonValue, PlainSerializer, PlainValidator, PydanticSchemaGenerationError, TypeAdapter, WrapSerializer + +_JSON_EXTRA_FIELDS_KEYS = "__pickled_extra_fields__" + + +def pickle_serializer(value: Any) -> JsonValue: + return encodebytes(dumps(value)).decode() + + +def pickle_validator(value: JsonValue) -> Any: + return loads(decodebytes(value.encode())) + + +def json_pickle_serializer(model: JsonValue, original_serializer: Callable[[JsonValue], JsonValue]) -> JsonValue: + extra_fields = list() + model_copy = deepcopy(model) + + for field_name, field_value in model_copy.items(): + try: + if isinstance(field_value, bytes): + raise PydanticSchemaGenerationError("") + else: + TypeAdapter(type(field_value)) + except PydanticSchemaGenerationError: + model_copy[field_name] = pickle_serializer(field_value) + extra_fields += [field_name] + + original_dump = original_serializer(model_copy) + original_dump[_JSON_EXTRA_FIELDS_KEYS] = extra_fields + return original_dump + + +def json_pickle_validator(model: JsonValue) -> JsonValue: + model_copy = deepcopy(model) + + if _JSON_EXTRA_FIELDS_KEYS in model.keys(): + for extra_key in model[_JSON_EXTRA_FIELDS_KEYS]: + extra_value = model[extra_key] + model_copy[extra_key] = pickle_validator(extra_value) + del model_copy[_JSON_EXTRA_FIELDS_KEYS] + + return model_copy + + +PickleSerializer = PlainSerializer(pickle_serializer, when_used="json") +PickleValidator = PlainValidator(pickle_validator) +SerializableVaue = Annotated[Any, PickleSerializer, PickleValidator] + +JSONPickleSerializer = WrapSerializer(json_pickle_serializer, when_used="json") +JSONPickleValidator = AfterValidator(json_pickle_validator) +JSONSerializableDict = Annotated[JsonValue, JSONPickleSerializer, JSONPickleValidator] From 27205bb50fa08cb738638f6d4b5c45b0ea6610ad Mon Sep 17 00:00:00 2001 From: pseusys Date: Fri, 7 Jun 2024 19:21:36 +0200 Subject: [PATCH 082/140] github action fixes #1 --- dff/messengers/telegram/abstract.py | 4 ++-- tests/messengers/telegram/utils.py | 3 ++- tutorials/messengers/telegram/2_attachments.py | 2 +- tutorials/messengers/telegram/3_advanced.py | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index a8ec3a898..127c20816 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -327,14 +327,14 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes chat_id, files, caption=message.text, - **generate_extra_fields(attachment, ["disable_notification", "protect_content"]), + **generate_extra_fields(message, ["disable_notification", "protect_content"]), ) return if message.text is not None: await bot.send_message( chat_id, message.text, - **generate_extra_fields(attachment, ["parse_mode", "disable_notification", "protect_content", "reply_markup"]), + **generate_extra_fields(message, ["parse_mode", "disable_notification", "protect_content", "reply_markup"]), ) async def _on_event( diff --git a/tests/messengers/telegram/utils.py b/tests/messengers/telegram/utils.py index a3860c051..e7fb7ce71 100644 --- a/tests/messengers/telegram/utils.py +++ b/tests/messengers/telegram/utils.py @@ -1,9 +1,10 @@ from asyncio import get_event_loop from contextlib import contextmanager -from typing import Any, Hashable, Iterator, List, Optional, Tuple, TypeAlias +from typing import Any, Hashable, Iterator, List, Optional, Tuple from pydantic import BaseModel from telegram import Update +from typing_extensions import TypeAlias from dff.messengers.telegram.abstract import _AbstractTelegramInterface from dff.script import Message diff --git a/tutorials/messengers/telegram/2_attachments.py b/tutorials/messengers/telegram/2_attachments.py index d46ddfc87..f6a563ce0 100644 --- a/tutorials/messengers/telegram/2_attachments.py +++ b/tutorials/messengers/telegram/2_attachments.py @@ -48,7 +48,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) # %% -EXAMPLE_ATTACHMENT_SOURCE = "https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@wiki/example-attachments" +EXAMPLE_ATTACHMENT_SOURCE = "https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments" location_data = {"latitude": 50.65, "longitude": 3.916667} diff --git a/tutorials/messengers/telegram/3_advanced.py b/tutorials/messengers/telegram/3_advanced.py index 3ac2a61e9..9e121bba5 100644 --- a/tutorials/messengers/telegram/3_advanced.py +++ b/tutorials/messengers/telegram/3_advanced.py @@ -57,7 +57,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) # %% -EXAMPLE_ATTACHMENT_SOURCE = "https://cdn.jsdelivr.net/gh/deeppavlov/dialog_flow_framework@wiki/example-attachments" +EXAMPLE_ATTACHMENT_SOURCE = "https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments" image_url = HttpUrl(f"{EXAMPLE_ATTACHMENT_SOURCE}/deeppavlov.png") From 0de3e2b4cd6587ee337a8ab052a97b688d501771 Mon Sep 17 00:00:00 2001 From: pseusys Date: Fri, 7 Jun 2024 20:37:14 +0200 Subject: [PATCH 083/140] github action fixes N2 --- dff/messengers/telegram/abstract.py | 2 +- tests/messengers/telegram/test_happy_paths.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index 127c20816..67714544a 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -230,7 +230,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes await bot.send_sticker( chat_id, sticker, - **generate_extra_fields(attachment, ["disable_notification", "protect_content", "reply_markup"]), + **generate_extra_fields(attachment, ["disable_notification", "protect_content", "reply_markup", "emoji"]), ) if isinstance(attachment, Audio): attachment_bytes = await attachment.get_bytes(self) diff --git a/tests/messengers/telegram/test_happy_paths.json b/tests/messengers/telegram/test_happy_paths.json index fb71a225d..30398bc9d 100644 --- a/tests/messengers/telegram/test_happy_paths.json +++ b/tests/messengers/telegram/test_happy_paths.json @@ -58,7 +58,7 @@ "received_message": "{\"text\":\"sticker\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your sticker!\",\"commands\":null,\"attachments\":[{\"source\":null,\"cached_filename\":null,\"id\":\"CAACAgIAAxkBAAErAAFXZibO5ksphCKSXSe1CYiw5588yqsAAkEAAzyKVxogmx2BPCogYDQE\",\"title\":\"A sticker I've just found\",\"use_cache\":true,\"dff_attachment_type\":\"sticker\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_sticker(42, 'CAACAgIAAxkBAAErAAFXZibO5ksphCKSXSe1CYiw5588yqsAAkEAAzyKVxogmx2BPCogYDQE', disable_notification=None, reply_markup=None, emoji=None)", + "send_sticker(42, 'CAACAgIAAxkBAAErAAFXZibO5ksphCKSXSe1CYiw5588yqsAAkEAAzyKVxogmx2BPCogYDQE', disable_notification=None, protect_content=None, reply_markup=None, emoji=None)", "send_message(42, \"Here's your sticker!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None)" ] }, @@ -67,7 +67,7 @@ "received_message": "{\"text\":\"audio\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your audio!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/separation-william-king.mp3\",\"cached_filename\":null,\"id\":null,\"title\":\"\\\"Separation\\\" melody by William King\",\"use_cache\":true,\"dff_attachment_type\":\"audio\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_audio(42, audio=<>, performer=None, title=None, caption=\"Here's your audio!\", disable_notification=None, reply_markup=None, parse_mode=None, thumbnail=None)" + "send_audio(42, audio=<>, caption=\"Here's your audio!\", performer=None, title=None, disable_notification=None, reply_markup=None, parse_mode=None, thumbnail=None)" ] }, { @@ -142,7 +142,7 @@ "response_message": "{\"text\":\"Here's your message with multiple attachments (a location and a sticker)!\\nRun \/start command again to restart.\",\"commands\":null,\"attachments\":[{\"longitude\":30.3141,\"latitude\":59.9386,\"dff_attachment_type\":\"location\",\"__pickled_extra_fields__\":[]},{\"source\":null,\"cached_filename\":null,\"id\":\"CAACAgIAAxkBAAErBZ1mKAbZvEOmhscojaIL5q0u8vgp1wACRygAAiSjCUtLa7RHZy76ezQE\",\"title\":null,\"use_cache\":true,\"dff_attachment_type\":\"sticker\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ "send_location(42, 59.9386, 30.3141, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=None)", - "send_sticker(42, 'CAACAgIAAxkBAAErBZ1mKAbZvEOmhscojaIL5q0u8vgp1wACRygAAiSjCUtLa7RHZy76ezQE', disable_notification=None, reply_markup=None, emoji=None)", + "send_sticker(42, 'CAACAgIAAxkBAAErBZ1mKAbZvEOmhscojaIL5q0u8vgp1wACRygAAiSjCUtLa7RHZy76ezQE', disable_notification=None, protect_content=None, reply_markup=None, emoji=None)", "send_message(42, \"Here's your message with multiple attachments (a location and a sticker)!\\nRun /start command again to restart.\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None)" ] }, From 83cb8a0b3fc8b93ddfb8af33394a8aef88b0a4cb Mon Sep 17 00:00:00 2001 From: pseusys Date: Fri, 7 Jun 2024 21:34:18 +0200 Subject: [PATCH 084/140] github action fixes N3 --- dff/utils/pydantic/serializing.py | 4 +++- tests/script/core/test_message.py | 16 ++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/dff/utils/pydantic/serializing.py b/dff/utils/pydantic/serializing.py index 3d59a6718..c8701acbb 100644 --- a/dff/utils/pydantic/serializing.py +++ b/dff/utils/pydantic/serializing.py @@ -3,6 +3,7 @@ from pickle import dumps, loads from typing import Annotated, Any, Callable from pydantic import AfterValidator, JsonValue, PlainSerializer, PlainValidator, PydanticSchemaGenerationError, TypeAdapter, WrapSerializer +from pydantic_core import PydanticSerializationError _JSON_EXTRA_FIELDS_KEYS = "__pickled_extra_fields__" @@ -25,7 +26,8 @@ def json_pickle_serializer(model: JsonValue, original_serializer: Callable[[Json raise PydanticSchemaGenerationError("") else: TypeAdapter(type(field_value)) - except PydanticSchemaGenerationError: + model_copy[field_name] = original_serializer(field_value) + except (PydanticSchemaGenerationError, PydanticSerializationError): model_copy[field_name] = pickle_serializer(field_value) extra_fields += [field_name] diff --git a/tests/script/core/test_message.py b/tests/script/core/test_message.py index 5a8179120..6f8192f04 100644 --- a/tests/script/core/test_message.py +++ b/tests/script/core/test_message.py @@ -88,18 +88,21 @@ def test_location(self): @pytest.mark.parametrize( "attachment1,attachment2,equal", [ + ( + DataAttachment(source="https://github.com/mathiasbynens/small/raw/master/pdf.pdf", title="Title"), + DataAttachment(source="https://github.com/mathiasbynens/small/raw/master/pdf.pdf", title="Title"), + True, + ), ( DataAttachment(source="https://github.com/mathiasbynens/small/raw/master/pdf.pdf", title="File"), DataAttachment( source="https://raw.githubusercontent.com/mathiasbynens/small/master/pdf.pdf", title="File" ), - True, + False, ), ( DataAttachment(source="https://github.com/mathiasbynens/small/raw/master/pdf.pdf", title="1"), - DataAttachment( - source="https://raw.githubusercontent.com/mathiasbynens/small/master/pdf.pdf", title="2" - ), + DataAttachment(source="https://github.com/mathiasbynens/small/raw/master/pdf.pdf", title="2"), False, ), ( @@ -147,9 +150,10 @@ def test_attachment_serialize(self, json_context_storage: DBContextStorage, atta def test_field_serializable(self, json_context_storage: DBContextStorage, random_original_message: UnserializableObject): name = "serializable_test" message = Message(text="sample message") - message.misc = {"answer": 42} + message.misc = {"answer": 42, "unserializable": random_original_message} message.original_message = random_original_message - message.some_extra_field = {"unserializable": random_original_message} + message.some_extra_field = random_original_message + message.other_extra_field = {"unserializable": random_original_message} json_context_storage[name] = Context(requests={0: message}) retrieved = json_context_storage[name].requests[0] assert message == retrieved From 27678f56817b02dc99bb857adc6ea9c720c73afc Mon Sep 17 00:00:00 2001 From: pseusys Date: Fri, 7 Jun 2024 22:09:44 +0200 Subject: [PATCH 085/140] misc typing fixed --- tests/script/conditions/test_conditions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/script/conditions/test_conditions.py b/tests/script/conditions/test_conditions.py index ab85df16a..de69edb6e 100644 --- a/tests/script/conditions/test_conditions.py +++ b/tests/script/conditions/test_conditions.py @@ -16,7 +16,7 @@ def test_conditions(): assert cnd.exact_match(Message("text"))(ctx, pipeline) assert cnd.exact_match(Message("text", misc={}))(ctx, pipeline) - assert not cnd.exact_match(Message("text", misc={1: 1}))(ctx, pipeline) + assert not cnd.exact_match(Message("text", misc={"one": 1}))(ctx, pipeline) assert not cnd.exact_match(Message("text1"))(ctx, pipeline) assert cnd.exact_match(Message())(ctx, pipeline) assert not cnd.exact_match(Message(), skip_none=False)(ctx, pipeline) From f88e4541d395ce82ee3739ecc31fb125969bf51e Mon Sep 17 00:00:00 2001 From: pseusys Date: Fri, 7 Jun 2024 22:48:49 +0200 Subject: [PATCH 086/140] Attachment name changed (for windows) --- tutorials/messengers/telegram/2_attachments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorials/messengers/telegram/2_attachments.py b/tutorials/messengers/telegram/2_attachments.py index f6a563ce0..05383b9b3 100644 --- a/tutorials/messengers/telegram/2_attachments.py +++ b/tutorials/messengers/telegram/2_attachments.py @@ -67,7 +67,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) "source": HttpUrl( f"{EXAMPLE_ATTACHMENT_SOURCE}/separation-william-king.mp3" ), - "title": '"Separation" melody by William King', + "title": "Separation melody by William King", } video_data = { From 85487c3671255ef956150ae572aec0da27743f3e Mon Sep 17 00:00:00 2001 From: pseusys Date: Fri, 7 Jun 2024 22:49:56 +0200 Subject: [PATCH 087/140] typing.Annotated fixed --- dff/utils/pydantic/serializing.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dff/utils/pydantic/serializing.py b/dff/utils/pydantic/serializing.py index c8701acbb..31ec1a434 100644 --- a/dff/utils/pydantic/serializing.py +++ b/dff/utils/pydantic/serializing.py @@ -1,7 +1,8 @@ from base64 import decodebytes, encodebytes from copy import deepcopy from pickle import dumps, loads -from typing import Annotated, Any, Callable +from typing import Any, Callable +from typing_extensions import Annotated from pydantic import AfterValidator, JsonValue, PlainSerializer, PlainValidator, PydanticSchemaGenerationError, TypeAdapter, WrapSerializer from pydantic_core import PydanticSerializationError From e634436d25f660a6a057eb1d72cc0dc24822fa09 Mon Sep 17 00:00:00 2001 From: pseusys Date: Fri, 7 Jun 2024 22:56:40 +0200 Subject: [PATCH 088/140] Naming fixed once again --- tests/messengers/telegram/test_happy_paths.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/messengers/telegram/test_happy_paths.json b/tests/messengers/telegram/test_happy_paths.json index 30398bc9d..bf493d163 100644 --- a/tests/messengers/telegram/test_happy_paths.json +++ b/tests/messengers/telegram/test_happy_paths.json @@ -65,7 +65,7 @@ { "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 0, 46, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=6, supergroup_chat_created=False, text='audio'), update_id=6)", "received_message": "{\"text\":\"audio\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", - "response_message": "{\"text\":\"Here's your audio!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/separation-william-king.mp3\",\"cached_filename\":null,\"id\":null,\"title\":\"\\\"Separation\\\" melody by William King\",\"use_cache\":true,\"dff_attachment_type\":\"audio\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_message": "{\"text\":\"Here's your audio!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/separation-william-king.mp3\",\"cached_filename\":null,\"id\":null,\"title\":\"Separation melody by William King\",\"use_cache\":true,\"dff_attachment_type\":\"audio\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ "send_audio(42, audio=<>, caption=\"Here's your audio!\", performer=None, title=None, disable_notification=None, reply_markup=None, parse_mode=None, thumbnail=None)" ] From 5ca6e20385726aecc2b0fe17980ee498a6f08e2c Mon Sep 17 00:00:00 2001 From: pseusys Date: Fri, 7 Jun 2024 23:09:49 +0200 Subject: [PATCH 089/140] telegram import in tests fixed --- tests/messengers/telegram/utils.py | 8 +++++--- tests/script/core/test_message.py | 5 +++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/tests/messengers/telegram/utils.py b/tests/messengers/telegram/utils.py index e7fb7ce71..1ae22404d 100644 --- a/tests/messengers/telegram/utils.py +++ b/tests/messengers/telegram/utils.py @@ -1,9 +1,8 @@ from asyncio import get_event_loop from contextlib import contextmanager -from typing import Any, Hashable, Iterator, List, Optional, Tuple +from typing import TYPE_CHECKING, Any, Hashable, Iterator, List, Optional, Tuple from pydantic import BaseModel -from telegram import Update from typing_extensions import TypeAlias from dff.messengers.telegram.abstract import _AbstractTelegramInterface @@ -11,7 +10,10 @@ from dff.script.core.context import Context from dff.script.core.message import DataAttachment -PathStep: TypeAlias = Tuple[Update, Message, Message, List[str]] +if TYPE_CHECKING: + from telegram import Update + + PathStep: TypeAlias = Tuple[Update, Message, Message, List[str]] class MockBot(BaseModel, arbitrary_types_allowed=True): diff --git a/tests/script/core/test_message.py b/tests/script/core/test_message.py index 6f8192f04..379b1b35b 100644 --- a/tests/script/core/test_message.py +++ b/tests/script/core/test_message.py @@ -1,5 +1,6 @@ +from os import urandom from pathlib import Path -from random import randint, randbytes +from random import randint from shutil import rmtree from typing import Hashable, Optional, TextIO from urllib.request import urlopen @@ -67,7 +68,7 @@ def json_context_storage(self, tmp_path) -> DBContextStorage: @pytest.fixture def random_original_message(self) -> UnserializableObject: - return UnserializableObject(randint(0, 256), randbytes(32)) + return UnserializableObject(randint(0, 256), urandom(32)) def clear_and_create_dir(self, dir: Path) -> Path: rmtree(dir, ignore_errors=True) From 209d9751227d62cf892b59a459b5651ade13cca8 Mon Sep 17 00:00:00 2001 From: pseusys Date: Fri, 7 Jun 2024 23:24:22 +0200 Subject: [PATCH 090/140] telegram import fix (again) --- tests/messengers/telegram/test_tutorials.py | 8 ++++++-- tests/messengers/telegram/utils.py | 8 +++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/messengers/telegram/test_tutorials.py b/tests/messengers/telegram/test_tutorials.py index bac4bc038..6820f6d42 100644 --- a/tests/messengers/telegram/test_tutorials.py +++ b/tests/messengers/telegram/test_tutorials.py @@ -1,14 +1,16 @@ from importlib import import_module from json import loads from pathlib import Path -from typing import Dict, List +from typing import TYPE_CHECKING, Dict, List import pytest from dff.script.core.message import Message -from tests.messengers.telegram.utils import MockApplication, PathStep from tests.test_utils import get_path_from_tests_to_current_dir +if TYPE_CHECKING: + from tests.messengers.telegram.utils import PathStep + dot_path_to_addon = get_path_from_tests_to_current_dir(__file__, separator=".") happy_paths_file = Path(__file__).parent / "test_happy_paths.json" @@ -36,6 +38,8 @@ def _cast_dict_to_happy_step(dict: Dict) -> List[PathStep]: ["1_basic", "2_attachments", "3_advanced"], ) def test_tutorials_memory(tutorial_module_name: str): + from tests.messengers.telegram.utils import MockApplication + happy_path_data = loads(happy_paths_file.read_text())[tutorial_module_name] happy_path_steps = _cast_dict_to_happy_step(happy_path_data) module = import_module(f"tutorials.{dot_path_to_addon}.{tutorial_module_name}") diff --git a/tests/messengers/telegram/utils.py b/tests/messengers/telegram/utils.py index 1ae22404d..e7fb7ce71 100644 --- a/tests/messengers/telegram/utils.py +++ b/tests/messengers/telegram/utils.py @@ -1,8 +1,9 @@ from asyncio import get_event_loop from contextlib import contextmanager -from typing import TYPE_CHECKING, Any, Hashable, Iterator, List, Optional, Tuple +from typing import Any, Hashable, Iterator, List, Optional, Tuple from pydantic import BaseModel +from telegram import Update from typing_extensions import TypeAlias from dff.messengers.telegram.abstract import _AbstractTelegramInterface @@ -10,10 +11,7 @@ from dff.script.core.context import Context from dff.script.core.message import DataAttachment -if TYPE_CHECKING: - from telegram import Update - - PathStep: TypeAlias = Tuple[Update, Message, Message, List[str]] +PathStep: TypeAlias = Tuple[Update, Message, Message, List[str]] class MockBot(BaseModel, arbitrary_types_allowed=True): From 2f6781638d6f99337223c5decc1e6d8d4e291bd5 Mon Sep 17 00:00:00 2001 From: pseusys Date: Fri, 7 Jun 2024 23:27:23 +0200 Subject: [PATCH 091/140] PathStep quotes added --- tests/messengers/telegram/test_tutorials.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/messengers/telegram/test_tutorials.py b/tests/messengers/telegram/test_tutorials.py index 6820f6d42..f815907f9 100644 --- a/tests/messengers/telegram/test_tutorials.py +++ b/tests/messengers/telegram/test_tutorials.py @@ -15,7 +15,7 @@ happy_paths_file = Path(__file__).parent / "test_happy_paths.json" -def _cast_dict_to_happy_step(dict: Dict) -> List[PathStep]: +def _cast_dict_to_happy_step(dict: Dict) -> List["PathStep"]: imports = globals().copy() imports.update(import_module("telegram").__dict__) imports.update(import_module("telegram.ext").__dict__) From b61c42d00fad1bbb003feeb51fa6102cb5882447 Mon Sep 17 00:00:00 2001 From: pseusys Date: Sat, 8 Jun 2024 03:05:38 +0200 Subject: [PATCH 092/140] aiofiles dependency fixed --- tests/script/core/test_message.py | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/tests/script/core/test_message.py b/tests/script/core/test_message.py index 379b1b35b..2b4c66601 100644 --- a/tests/script/core/test_message.py +++ b/tests/script/core/test_message.py @@ -8,10 +8,8 @@ import pytest from pydantic import ValidationError, HttpUrl, FilePath -from dff.context_storages import DBContextStorage, JSONContextStorage from dff.messengers.common.interface import MessengerInterface from dff.messengers.console import CLIMessengerInterface -from dff.script.core.context import Context from dff.script.core.message import ( Animation, Audio, @@ -62,10 +60,6 @@ async def populate_attachment(self, attachment: DataAttachment) -> bytes: class TestMessage: - @pytest.fixture - def json_context_storage(self, tmp_path) -> DBContextStorage: - return JSONContextStorage(f"file://{tmp_path / 'serialization_database.json'}") - @pytest.fixture def random_original_message(self) -> UnserializableObject: return UnserializableObject(randint(0, 256), urandom(32)) @@ -142,22 +136,21 @@ def test_attachment_equal(self, attachment1: DataAttachment, attachment2: DataAt Document(source="https://example.com/some_document.pdf"), ], ) - def test_attachment_serialize(self, json_context_storage: DBContextStorage, attachment: DataAttachment): - name = type(attachment).__name__ - json_context_storage[name] = Context(requests={0: Message(attachments=[attachment])}) - retrieved = json_context_storage[name].requests[0].attachments[0] - assert attachment == retrieved - - def test_field_serializable(self, json_context_storage: DBContextStorage, random_original_message: UnserializableObject): - name = "serializable_test" + def test_attachment_serialize(self, attachment: DataAttachment): + message = Message(attachments=[attachment]) + serialized = message.model_dump_json() + validated = Message.model_validate_json(serialized) + assert message == validated + + def test_field_serializable(self, random_original_message: UnserializableObject): message = Message(text="sample message") message.misc = {"answer": 42, "unserializable": random_original_message} message.original_message = random_original_message message.some_extra_field = random_original_message message.other_extra_field = {"unserializable": random_original_message} - json_context_storage[name] = Context(requests={0: message}) - retrieved = json_context_storage[name].requests[0] - assert message == retrieved + serialized = message.model_dump_json() + validated = Message.model_validate_json(serialized) + assert message == validated @pytest.mark.asyncio async def test_getting_attachment_bytes(self, tmp_path): From cc5772ee4220378ed5f44bc101452b1feef478b8 Mon Sep 17 00:00:00 2001 From: pseusys Date: Mon, 10 Jun 2024 00:24:23 +0200 Subject: [PATCH 093/140] typing fixed --- dff/utils/pydantic/serializing.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/dff/utils/pydantic/serializing.py b/dff/utils/pydantic/serializing.py index 31ec1a434..17306e1bb 100644 --- a/dff/utils/pydantic/serializing.py +++ b/dff/utils/pydantic/serializing.py @@ -1,13 +1,15 @@ from base64 import decodebytes, encodebytes from copy import deepcopy from pickle import dumps, loads -from typing import Any, Callable -from typing_extensions import Annotated -from pydantic import AfterValidator, JsonValue, PlainSerializer, PlainValidator, PydanticSchemaGenerationError, TypeAdapter, WrapSerializer +from typing import Any, Callable, Dict, List, Union +from typing_extensions import Annotated, TypeAlias +from pydantic import AfterValidator, BaseModel, JsonValue, PlainSerializer, PlainValidator, PydanticSchemaGenerationError, TypeAdapter, WrapSerializer from pydantic_core import PydanticSerializationError _JSON_EXTRA_FIELDS_KEYS = "__pickled_extra_fields__" +Serializable: TypeAlias = Dict[str, Union[JsonValue, BaseModel, List[BaseModel], Dict[str, BaseModel]]] + def pickle_serializer(value: Any) -> JsonValue: return encodebytes(dumps(value)).decode() @@ -17,7 +19,7 @@ def pickle_validator(value: JsonValue) -> Any: return loads(decodebytes(value.encode())) -def json_pickle_serializer(model: JsonValue, original_serializer: Callable[[JsonValue], JsonValue]) -> JsonValue: +def json_pickle_serializer(model: Serializable, original_serializer: Callable[[Serializable], Serializable]) -> Serializable: extra_fields = list() model_copy = deepcopy(model) @@ -37,7 +39,7 @@ def json_pickle_serializer(model: JsonValue, original_serializer: Callable[[Json return original_dump -def json_pickle_validator(model: JsonValue) -> JsonValue: +def json_pickle_validator(model: SerialDict) -> SerialDict: model_copy = deepcopy(model) if _JSON_EXTRA_FIELDS_KEYS in model.keys(): @@ -55,4 +57,4 @@ def json_pickle_validator(model: JsonValue) -> JsonValue: JSONPickleSerializer = WrapSerializer(json_pickle_serializer, when_used="json") JSONPickleValidator = AfterValidator(json_pickle_validator) -JSONSerializableDict = Annotated[JsonValue, JSONPickleSerializer, JSONPickleValidator] +JSONSerializableDict = Annotated[SerialDict, JSONPickleSerializer, JSONPickleValidator] From ae35fb9601fea947fcc90de7ca395e99044fab74 Mon Sep 17 00:00:00 2001 From: pseusys Date: Mon, 10 Jun 2024 02:27:43 +0200 Subject: [PATCH 094/140] naming error fixed --- dff/utils/pydantic/serializing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dff/utils/pydantic/serializing.py b/dff/utils/pydantic/serializing.py index 17306e1bb..507aef46b 100644 --- a/dff/utils/pydantic/serializing.py +++ b/dff/utils/pydantic/serializing.py @@ -39,7 +39,7 @@ def json_pickle_serializer(model: Serializable, original_serializer: Callable[[S return original_dump -def json_pickle_validator(model: SerialDict) -> SerialDict: +def json_pickle_validator(model: Serializable) -> Serializable: model_copy = deepcopy(model) if _JSON_EXTRA_FIELDS_KEYS in model.keys(): @@ -57,4 +57,4 @@ def json_pickle_validator(model: SerialDict) -> SerialDict: JSONPickleSerializer = WrapSerializer(json_pickle_serializer, when_used="json") JSONPickleValidator = AfterValidator(json_pickle_validator) -JSONSerializableDict = Annotated[SerialDict, JSONPickleSerializer, JSONPickleValidator] +JSONSerializableDict = Annotated[Serializable, JSONPickleSerializer, JSONPickleValidator] From a1df33f32e293aed41b99686eae75dc47db0f9c3 Mon Sep 17 00:00:00 2001 From: pseusys Date: Mon, 10 Jun 2024 02:50:32 +0200 Subject: [PATCH 095/140] ok, we'll just use any I guess --- dff/utils/pydantic/serializing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dff/utils/pydantic/serializing.py b/dff/utils/pydantic/serializing.py index 507aef46b..cecc2bb41 100644 --- a/dff/utils/pydantic/serializing.py +++ b/dff/utils/pydantic/serializing.py @@ -3,12 +3,12 @@ from pickle import dumps, loads from typing import Any, Callable, Dict, List, Union from typing_extensions import Annotated, TypeAlias -from pydantic import AfterValidator, BaseModel, JsonValue, PlainSerializer, PlainValidator, PydanticSchemaGenerationError, TypeAdapter, WrapSerializer +from pydantic import AfterValidator, JsonValue, PlainSerializer, PlainValidator, PydanticSchemaGenerationError, TypeAdapter, WrapSerializer from pydantic_core import PydanticSerializationError _JSON_EXTRA_FIELDS_KEYS = "__pickled_extra_fields__" -Serializable: TypeAlias = Dict[str, Union[JsonValue, BaseModel, List[BaseModel], Dict[str, BaseModel]]] +Serializable: TypeAlias = Dict[str, Union[JsonValue, Any, List[Any], Dict[str, Any]]] def pickle_serializer(value: Any) -> JsonValue: From d6fbaafa230c534d46832b53a2b73f5c1c83e94d Mon Sep 17 00:00:00 2001 From: pseusys Date: Mon, 10 Jun 2024 03:09:25 +0200 Subject: [PATCH 096/140] telegram skip setup correctly --- dff/messengers/telegram/__init__.py | 1 + tests/messengers/telegram/test_tutorials.py | 28 ++++----------------- tests/messengers/telegram/utils.py | 19 +++++++++++++- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/dff/messengers/telegram/__init__.py b/dff/messengers/telegram/__init__.py index 68ea21beb..88c0bf3f9 100644 --- a/dff/messengers/telegram/__init__.py +++ b/dff/messengers/telegram/__init__.py @@ -1,3 +1,4 @@ # -*- coding: utf-8 -*- +from .abstract import telegram_available from .interface import PollingTelegramInterface, CallbackTelegramInterface diff --git a/tests/messengers/telegram/test_tutorials.py b/tests/messengers/telegram/test_tutorials.py index f815907f9..9d90a139d 100644 --- a/tests/messengers/telegram/test_tutorials.py +++ b/tests/messengers/telegram/test_tutorials.py @@ -1,36 +1,20 @@ from importlib import import_module from json import loads from pathlib import Path -from typing import TYPE_CHECKING, Dict, List import pytest -from dff.script.core.message import Message +from dff.messengers.telegram import telegram_available from tests.test_utils import get_path_from_tests_to_current_dir -if TYPE_CHECKING: - from tests.messengers.telegram.utils import PathStep +if telegram_available: + from tests.messengers.telegram.utils import cast_dict_to_happy_step, MockApplication dot_path_to_addon = get_path_from_tests_to_current_dir(__file__, separator=".") happy_paths_file = Path(__file__).parent / "test_happy_paths.json" -def _cast_dict_to_happy_step(dict: Dict) -> List["PathStep"]: - imports = globals().copy() - imports.update(import_module("telegram").__dict__) - imports.update(import_module("telegram.ext").__dict__) - imports.update(import_module("telegram.constants").__dict__) - - path_steps = list() - for step in dict: - update = eval(step["update"], imports) - received = Message.model_validate_json(step["received_message"]) - received.original_message = update - response = Message.model_validate_json(step["response_message"]) - path_steps += [(update, received, response, step["response_functions"])] - return path_steps - - +@pytest.mark.skipif(not telegram_available, reason="Telegram dependencies missing") @pytest.mark.telegram @pytest.mark.asyncio @pytest.mark.parametrize( @@ -38,10 +22,8 @@ def _cast_dict_to_happy_step(dict: Dict) -> List["PathStep"]: ["1_basic", "2_attachments", "3_advanced"], ) def test_tutorials_memory(tutorial_module_name: str): - from tests.messengers.telegram.utils import MockApplication - happy_path_data = loads(happy_paths_file.read_text())[tutorial_module_name] - happy_path_steps = _cast_dict_to_happy_step(happy_path_data) + happy_path_steps = cast_dict_to_happy_step(happy_path_data) module = import_module(f"tutorials.{dot_path_to_addon}.{tutorial_module_name}") module.interface.application = MockApplication.create(module.interface, happy_path_steps) module.pipeline.run() diff --git a/tests/messengers/telegram/utils.py b/tests/messengers/telegram/utils.py index e7fb7ce71..31fe24d37 100644 --- a/tests/messengers/telegram/utils.py +++ b/tests/messengers/telegram/utils.py @@ -1,6 +1,7 @@ from asyncio import get_event_loop from contextlib import contextmanager -from typing import Any, Hashable, Iterator, List, Optional, Tuple +from importlib import import_module +from typing import Any, Dict, Hashable, Iterator, List, Optional, Tuple from pydantic import BaseModel from telegram import Update @@ -14,6 +15,22 @@ PathStep: TypeAlias = Tuple[Update, Message, Message, List[str]] +def cast_dict_to_happy_step(dict: Dict) -> List["PathStep"]: + imports = globals().copy() + imports.update(import_module("telegram").__dict__) + imports.update(import_module("telegram.ext").__dict__) + imports.update(import_module("telegram.constants").__dict__) + + path_steps = list() + for step in dict: + update = eval(step["update"], imports) + received = Message.model_validate_json(step["received_message"]) + received.original_message = update + response = Message.model_validate_json(step["response_message"]) + path_steps += [(update, received, response, step["response_functions"])] + return path_steps + + class MockBot(BaseModel, arbitrary_types_allowed=True): latest_trace: List[str] = list() From 4416801103e6c189bea0d4a3b8a21734d5a3e9d6 Mon Sep 17 00:00:00 2001 From: pseusys Date: Mon, 10 Jun 2024 04:16:36 +0200 Subject: [PATCH 097/140] telegram import guarding fixed --- dff/messengers/telegram/abstract.py | 38 ++++++++++++++--------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index 67714544a..e459028a5 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -10,6 +10,25 @@ from dff.utils.messengers.verify_params import generate_extra_fields +from dff.messengers.common import MessengerInterface +from dff.pipeline.types import PipelineRunnerFunction +from dff.script.core.message import ( + Animation, + Audio, + CallbackQuery, + Contact, + DataAttachment, + Document, + Image, + Invoice, + Location, + Message, + Poll, + PollOption, + Sticker, + Video, +) + try: from telegram import ( InputMediaAnimation, @@ -23,25 +42,6 @@ from telegram.ext import Application, ExtBot, MessageHandler, CallbackQueryHandler, ContextTypes from telegram.ext.filters import ALL - from dff.messengers.common import MessengerInterface - from dff.pipeline.types import PipelineRunnerFunction - from dff.script.core.message import ( - Animation, - Audio, - CallbackQuery, - Contact, - DataAttachment, - Document, - Image, - Invoice, - Location, - Message, - Poll, - PollOption, - Sticker, - Video, - ) - telegram_available = True except ImportError: telegram_available = False From b4d9942c98208ff7240b4c15d2fde4bfdb8b56c5 Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 18 Jun 2024 18:31:24 +0200 Subject: [PATCH 098/140] Docs for new DFF classes and functions --- dff/messengers/common/interface.py | 22 +++++++ dff/messengers/telegram/abstract.py | 45 ++++++++++++++ dff/messengers/telegram/interface.py | 39 +++++++------ dff/script/conditions/std_conditions.py | 7 ++- dff/script/core/message.py | 78 ++++++++++++++++++++++--- dff/utils/messengers/verify_params.py | 9 +++ dff/utils/pydantic/serializing.py | 58 ++++++++++++++++-- pyproject.toml | 2 +- 8 files changed, 227 insertions(+), 33 deletions(-) diff --git a/dff/messengers/common/interface.py b/dff/messengers/common/interface.py index e1d10c2c0..80943ba4f 100644 --- a/dff/messengers/common/interface.py +++ b/dff/messengers/common/interface.py @@ -26,10 +26,22 @@ class MessengerInterface(abc.ABC): """ Class that represents a message interface used for communication between pipeline and users. It is responsible for connection between user and pipeline, as well as for request-response transactions. + + :param attachments_directory: Directory where attachments will be stored. + If not specified, the temporary directory will be used. """ supported_request_attachment_types: set[Type[Attachment]] = set() + """ + Types of attachment that this messenger interface can receive. + Attachments not in this list will be neglected. + """ + supported_response_attachment_types: set[type[Attachment]] = set() + """ + Types of attachment that this messenger interface can send. + Attachments not in this list will be neglected. + """ def __init__(self, attachments_directory: Optional[Path] = None) -> None: tempdir = gettempdir() @@ -58,6 +70,16 @@ async def connect(self, pipeline_runner: PipelineRunnerFunction): raise NotImplementedError async def populate_attachment(self, attachment: DataAttachment) -> bytes: + """ + Method that can be used by some messenger interfaces for attachment population. + E.g. if a file attachment consists of an URL of the file uploaded to the messenger servers, + this method is the right place to call the messenger API for the file downloading. + Since many messenger interfaces don't have built-in attachment population functionality, + this method is not abstract and thus should not always be overridden. + + :param attachment: Attachment that should be populated. + :return: The attachment bytes. + """ raise RuntimeError(f"Messanger interface {type(self).__name__} can't populate attachment {attachment}!") diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index e459028a5..57612223e 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -48,6 +48,10 @@ class _AbstractTelegramInterface(MessengerInterface): + """ + Messenger interface mixin for Telegram API usage. + """ + supported_request_attachment_types = {Location, Contact, Poll, Sticker, Audio, Video, Animation, Image, Document, Invoice} supported_response_attachment_types = {Location, Contact, Poll, Sticker, Audio, Video, Animation, Image, Document} @@ -69,6 +73,14 @@ async def populate_attachment(self, attachment: DataAttachment) -> bytes: raise ValueError(f"For attachment {attachment} id is not defined!") def extract_message_from_telegram(self, update: TelegramMessage) -> Message: + """ + Convert Telegram update to DFF message. + Extract text and supported attachments. + + :param update: Telegram update object. + :return: DFF message object. + """ + message = Message() message.attachments = list() @@ -199,6 +211,18 @@ def extract_message_from_telegram(self, update: TelegramMessage) -> Message: return message async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, message: Message) -> None: + """ + Send DFF message to Telegram. + Sometimes, if several attachments included into message can not be sent as one update, + several Telegram updates will be produced. + Sometimes, if no text and none of the supported attachments are included, + nothing will happen. + + :param bot: Telegram bot, that is used for connection to Telegram API. + :param chat_id: Telegram dialog ID that the message will be sent to. + :param message: DFF message that will be processed into Telegram updates. + """ + if message.attachments is not None: files = list() media_group_attachments_num = len([att for att in message.attachments if isinstance(att, DataAttachment)]) @@ -340,6 +364,13 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes async def _on_event( self, update: Update, _: ContextTypes.DEFAULT_TYPE, create_message: Callable[[Update], Message] ) -> None: + """ + Process Telegram update, run pipeline and send response to Telegram. + + :param update: Telegram update that will be processed. + :param create_message: function that converts Telegram update to DFF message. + """ + data_available = update.message is not None or update.callback_query is not None if update.effective_chat is not None and data_available: message = create_message(update) @@ -351,9 +382,23 @@ async def _on_event( ) async def on_message(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> None: + """ + Process normal Telegram update, extracting DFF message from it + using :py:meth:`~._AbstractTelegramInterface.extract_message_from_telegram`. + + :param update: Telegram update that will be processed. + """ + await self._on_event(update, _, lambda s: self.extract_message_from_telegram(s.message)) async def on_callback(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> None: + """ + Process Telegram callback update, creating empty DFF message + with only one callback query attachment from `callback_query.data` field. + + :param update: Telegram update that will be processed. + """ + await self._on_event(update, _, lambda s: Message(attachments=[CallbackQuery(query_string=s.callback_query.data)])) async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs): diff --git a/dff/messengers/telegram/interface.py b/dff/messengers/telegram/interface.py index 76106acb1..57d698efb 100644 --- a/dff/messengers/telegram/interface.py +++ b/dff/messengers/telegram/interface.py @@ -1,15 +1,22 @@ from pathlib import Path -from typing import Hashable, List, Optional, Tuple +from typing import Optional from telegram import Update -from dff.messengers.common.interface import PollingMessengerInterface, CallbackMessengerInterface from dff.pipeline.types import PipelineRunnerFunction -from dff.script import Context, Message from .abstract import _AbstractTelegramInterface -class PollingTelegramInterface(_AbstractTelegramInterface, PollingMessengerInterface): +class LongpollingTelegramInterface(_AbstractTelegramInterface): + """ + Telegram messenger interface, that requests Telegram API in a loop. + + :param token: The Telegram bot token. + :param attachments_directory: The directory for storing attachments. + :param interval: A time interval between polls (in seconds). + :param timeout: Timeout in seconds for long polling. + """ + def __init__( self, token: str, attachments_directory: Optional[Path] = None, interval: int = 2, timeout: int = 20 ) -> None: @@ -17,12 +24,6 @@ def __init__( self.interval = interval self.timeout = timeout - def _request(self) -> List[Tuple[Message, Hashable]]: - raise RuntimeError("_request method for PollingTelegramInterface is not specified") - - def _respond(self, _: List[Context]): - raise RuntimeError("_respond method for PollingTelegramInterface is not specified") - async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs): await super().connect(pipeline_runner, *args, **kwargs) self.application.run_polling( @@ -30,7 +31,17 @@ async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs ) -class CallbackTelegramInterface(_AbstractTelegramInterface, CallbackMessengerInterface): +class WebhookTelegramInterface(_AbstractTelegramInterface): + """ + Telegram messenger interface, that brings a special webserver up + and registers up for listening for Telegram updates. + + :param token: The Telegram bot token. + :param attachments_directory: The directory for storing attachments. + :param host: Local host name (or IP address). + :param port: Local port for running Telegram webhook. + """ + def __init__( self, token: str, attachments_directory: Optional[Path] = None, host: str = "localhost", port: int = 844 ): @@ -38,12 +49,6 @@ def __init__( self.listen = host self.port = port - def _request(self) -> List[Tuple[Message, Hashable]]: - raise RuntimeError("_request method for CallbackTelegramInterface is not specified") - - def _respond(self, _: List[Context]): - raise RuntimeError("_respond method for CallbackTelegramInterface is not specified") - async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs): await super().connect(pipeline_runner, *args, **kwargs) self.application.run_webhook(listen=self.listen, port=self.port, allowed_updates=Update.ALL_TYPES) diff --git a/dff/script/conditions/std_conditions.py b/dff/script/conditions/std_conditions.py index a6daeae97..a11370424 100644 --- a/dff/script/conditions/std_conditions.py +++ b/dff/script/conditions/std_conditions.py @@ -246,9 +246,12 @@ def false_handler(ctx: Context, pipeline: Pipeline) -> bool: """ -def has_callback_query(expected_query_string: str): +def has_callback_query(expected_query_string: str) -> Callable[[Context, Pipeline], bool]: """ - Condition that checks if :py:attr:`~.CallbackQuery.query_string` of the last message matches `expected`. + Condition that checks if :py:attr:`~.CallbackQuery.query_string` of the last message matches `expected_query_string`. + + :param expected_query_string: The expected query string to compare with. + :return: The callback query comparator function. """ def has_callback_query_handler(ctx: Context, _: Pipeline) -> bool: diff --git a/dff/script/core/message.py b/dff/script/core/message.py index ca8d1226a..01a7da03b 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -11,7 +11,7 @@ from urllib.request import urlopen from uuid import uuid4 -from pydantic import Field, JsonValue, field_validator, FilePath, HttpUrl, BaseModel, model_serializer, model_validator +from pydantic import BaseModel, Field, JsonValue, field_validator, FilePath, HttpUrl, model_serializer, model_validator from pydantic_core import Url from dff.messengers.common.interface import MessengerInterface @@ -20,7 +20,7 @@ class DataModel(BaseModel, extra="allow", arbitrary_types_allowed=True): """ - This class is a Pydantic BaseModel that serves as a base class for all DFF models. + This class is a Pydantic BaseModel that can have any type and number of extras. """ def __init__(self, **kwargs): @@ -29,6 +29,15 @@ def __init__(self, **kwargs): # TODO: inline once annotated __pydantic_extra__ will be available in pydantic def _json_extra_serializer(model: DataModel, original_serializer: Callable[[DataModel], JsonValue]) -> JsonValue: + """ + Serialize model along with the `extras` field: i.e. all the fields not listed in the model. + This function should be used as "wrap" serializer. + + :param model: Pydantic model for serialization. + :param original_serializer: Function originally used for serialization by Pydantic. + :return: Serialized model. + """ + model_copy = model.model_copy(deep=True) for extra_name in model.model_extra.keys(): delattr(model_copy, extra_name) @@ -39,6 +48,14 @@ def _json_extra_serializer(model: DataModel, original_serializer: Callable[[Data # TODO: inline once annotated __pydantic_extra__ will be available in pydantic def _json_extra_validator(model: DataModel) -> DataModel: + """ + Validate model along with the `extras` field: i.e. all the fields not listed in the model. + This function should be used as "after" validator. + + :param model: Pydantic model for validation. + :return: Validated model. + """ + model.__pydantic_extra__ = json_pickle_validator(model.__pydantic_extra__) return model @@ -63,6 +80,8 @@ class Command(DataModel): class Attachment(DataModel): """ + DFF Message attachment base class. + It is capable of serializing and validating all the model fields to JSON. """ @model_validator(mode="after") @@ -75,6 +94,12 @@ def extra_serializer(self, original_serializer: Callable[["Attachment"], Dict[st class CallbackQuery(Attachment): + """ + This class is a data model that represents a callback query attachment. + It is sent as a response to non-message events, e.g. keyboard UI interactions. + It has query string attribute, that represents the response data string. + """ + query_string: Optional[str] dff_attachment_type: Literal["callback_query"] = "callback_query" @@ -99,6 +124,11 @@ def __eq__(self, other): class Contact(Attachment): + """ + This class is a data model that represents a contact. + It includes phone number, and user first and last name. + """ + phone_number: str first_name: str last_name: Optional[str] @@ -106,6 +136,11 @@ class Contact(Attachment): class Invoice(Attachment): + """ + This class is a data model that represents an invoice. + It includes title, description, currency name and amount. + """ + title: str description: str currency: str @@ -114,12 +149,22 @@ class Invoice(Attachment): class PollOption(DataModel): + """ + This class is a data model that represents a poll option. + It includes the option name and votes number. + """ + text: str votes: int = Field(default=0) dff_attachment_type: Literal["poll_option"] = "poll_option" class Poll(Attachment): + """ + This class is a data model that represents a poll. + It includes a list of poll options. + """ + question: str options: List[PollOption] dff_attachment_type: Literal["poll"] = "poll" @@ -129,21 +174,39 @@ class DataAttachment(Attachment): """ This class represents an attachment that can be either a file or a URL, along with an optional ID and title. + This attachment can also be optionally cached for future use. """ source: Optional[Union[HttpUrl, FilePath]] = None cached_filename: Optional[FilePath] = None - id: Optional[str] = None # id field is made separate to simplify type validation + id: Optional[str] = None title: Optional[str] = None use_cache: bool = True async def _cache_attachment(self, data: bytes, directory: Path) -> None: + """ + Cache attachment, save bytes into a file. + + :param data: attachment data bytes. + :param directory: cache file where attachment will be saved. + """ + title = str(uuid4()) if self.title is None else self.title self.cached_filename = directory / title with open(self.cached_filename, "wb") as file: file.write(data) async def get_bytes(self, from_interface: MessengerInterface) -> Optional[bytes]: + """ + Download attachment bytes. + If the attachment is represented by URL or saved in a file, + it will be downloaded or read automatically. + Otherwise, a :py:meth:`~dff.messengers.common.MessengerInterface.populate_attachment` + will be used for receiving attachment bytes by ID and title. + + :param from_interface: messenger interface the attachment was received from. + """ + if isinstance(self.source, Path): with open(self.source, "rb") as file: return file.read() @@ -227,6 +290,11 @@ class Message(DataModel): """ Class representing a message and contains several class level variables to store message information. + + It includes message text, list of commands included in the message, + list of attachments, annotations, MISC dictionary (that consists of + user-defined parameters) and original message field that somehow represent + the update received from messenger interface API. """ text: Optional[str] = None @@ -235,10 +303,6 @@ class level variables to store message information. annotations: Optional[JSONSerializableDict] = None misc: Optional[JSONSerializableDict] = None original_message: Optional[SerializableVaue] = None - # commands and state options are required for integration with services - # that use an intermediate backend server, like Yandex's Alice - # state: Optional[Session] = Session.ACTIVE - # ui: Optional[Union[Keyboard, DataModel]] = None def __init__( self, diff --git a/dff/utils/messengers/verify_params.py b/dff/utils/messengers/verify_params.py index 34e8ad12c..da5f121b1 100644 --- a/dff/utils/messengers/verify_params.py +++ b/dff/utils/messengers/verify_params.py @@ -4,4 +4,13 @@ def generate_extra_fields(attachment: BaseModel, extra_fields: List[str]): + """ + Convenience method for passing attachment extras as named arguments to API functions. + This might be useful for making sure no typos appear in code. + Accepts a list of extra names and makes a dictionary of extras mathing these names. + + :param attachment: attachment whose extras will be used. + :param extra_fields: list of extras that will be used. + """ + return {extra_field: attachment.__pydantic_extra__.get(extra_field, None) for extra_field in extra_fields} diff --git a/dff/utils/pydantic/serializing.py b/dff/utils/pydantic/serializing.py index cecc2bb41..4b78b9f11 100644 --- a/dff/utils/pydantic/serializing.py +++ b/dff/utils/pydantic/serializing.py @@ -3,7 +3,7 @@ from pickle import dumps, loads from typing import Any, Callable, Dict, List, Union from typing_extensions import Annotated, TypeAlias -from pydantic import AfterValidator, JsonValue, PlainSerializer, PlainValidator, PydanticSchemaGenerationError, TypeAdapter, WrapSerializer +from pydantic import AfterValidator, JsonValue, PlainSerializer, PlainValidator, RootModel, WrapSerializer from pydantic_core import PydanticSerializationError _JSON_EXTRA_FIELDS_KEYS = "__pickled_extra_fields__" @@ -11,35 +11,79 @@ Serializable: TypeAlias = Dict[str, Union[JsonValue, Any, List[Any], Dict[str, Any]]] +class WrapperModel(RootModel): + """ + Wrapper model for testing whether an object is serializable to JSON. + """ + root: Any + + def pickle_serializer(value: Any) -> JsonValue: + """ + Serializer funtion that serializes any pickle-serializable value into JSON-serializable. + Serializes value with pickle and encodes bytes as base64 string. + + :param value: Pickle-serializable object. + :return: String-encoded object. + """ + return encodebytes(dumps(value)).decode() def pickle_validator(value: JsonValue) -> Any: + """ + Validator funtion that validates base64 string encoded bytes as a pickle-serializable value. + Decodes base64 string and validates value with pickle. + + :param value: String-encoded string. + :return: Pickle-serializable object. + """ + return loads(decodebytes(value.encode())) def json_pickle_serializer(model: Serializable, original_serializer: Callable[[Serializable], Serializable]) -> Serializable: + """ + Serializer function that serializes a dictionary or Pydantic object to JSON. + For every object field, it checks whether the field is JSON serializable, + and if it's not, serializes it using pickle. + It also keeps track of pickle-serializable field names in a special list. + + :param model: Pydantic model object or a dictionary. + :original_serializer: Original serializer function for model. + :return: model with all the fields serialized to JSON. + """ + extra_fields = list() model_copy = deepcopy(model) for field_name, field_value in model_copy.items(): try: if isinstance(field_value, bytes): - raise PydanticSchemaGenerationError("") + raise PydanticSerializationError("") else: - TypeAdapter(type(field_value)) - model_copy[field_name] = original_serializer(field_value) - except (PydanticSchemaGenerationError, PydanticSerializationError): + WrapperModel(root=field_value).model_dump_json() + except PydanticSerializationError: model_copy[field_name] = pickle_serializer(field_value) extra_fields += [field_name] original_dump = original_serializer(model_copy) - original_dump[_JSON_EXTRA_FIELDS_KEYS] = extra_fields + if len(extra_fields) > 0: + original_dump[_JSON_EXTRA_FIELDS_KEYS] = extra_fields return original_dump def json_pickle_validator(model: Serializable) -> Serializable: + """ + Validator function that validates a JSON dictionary to a python dictionary. + For every object field, it checks if the field is pickle-serialized, + and if it is, validates it using pickle. + + :param model: Pydantic model object or a dictionary. + :original_serializer: Original serializer function for model. + :return: model with all the fields serialized to JSON. + """ + model_copy = deepcopy(model) if _JSON_EXTRA_FIELDS_KEYS in model.keys(): @@ -54,7 +98,9 @@ def json_pickle_validator(model: Serializable) -> Serializable: PickleSerializer = PlainSerializer(pickle_serializer, when_used="json") PickleValidator = PlainValidator(pickle_validator) SerializableVaue = Annotated[Any, PickleSerializer, PickleValidator] +"""Annotation for field that makes it JSON serializable""" JSONPickleSerializer = WrapSerializer(json_pickle_serializer, when_used="json") JSONPickleValidator = AfterValidator(json_pickle_validator) JSONSerializableDict = Annotated[Serializable, JSONPickleSerializer, JSONPickleValidator] +"""Annotation for dictionary or Pydantic model that makes all its fields JSON serializable""" diff --git a/pyproject.toml b/pyproject.toml index 3c4bde2ba..9e838c8b4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,7 +73,7 @@ aiosqlite = { version = "*", optional = true } omegaconf = { version = "*", optional = true } cryptography = { version = "*", optional = true } requests = { version = "*", optional = true } -python-telegram-bot = { version = "*", optional = true } +python-telegram-bot = { version = "*", extras = ["all"], optional = true } opentelemetry-instrumentation = { version = "*", optional = true } sqlalchemy = { version = "*", extras = ["asyncio"], optional = true } opentelemetry-exporter-otlp = { version = ">=1.20.0", optional = true } # log body serialization is required From 4cc00b7a7e1623e8703ee92fd74e6b4929c62b1a Mon Sep 17 00:00:00 2001 From: pseusys Date: Wed, 19 Jun 2024 03:16:24 +0200 Subject: [PATCH 099/140] tests fixed and updated --- dff/messengers/telegram/__init__.py | 2 +- dff/messengers/telegram/abstract.py | 100 ++++++++++++++---- dff/script/core/message.py | 16 ++- .../messengers/telegram/test_happy_paths.json | 88 ++++++++------- tests/script/core/test_message.py | 2 + tutorials/messengers/telegram/1_basic.py | 4 +- .../messengers/telegram/2_attachments.py | 24 ++++- tutorials/messengers/telegram/3_advanced.py | 4 +- 8 files changed, 173 insertions(+), 67 deletions(-) diff --git a/dff/messengers/telegram/__init__.py b/dff/messengers/telegram/__init__.py index 88c0bf3f9..9f390d341 100644 --- a/dff/messengers/telegram/__init__.py +++ b/dff/messengers/telegram/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- from .abstract import telegram_available -from .interface import PollingTelegramInterface, CallbackTelegramInterface +from .interface import LongpollingTelegramInterface, WebhookTelegramInterface diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index 57612223e..d6f5ff6d1 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -14,6 +14,7 @@ from dff.pipeline.types import PipelineRunnerFunction from dff.script.core.message import ( Animation, + Attachment, Audio, CallbackQuery, Contact, @@ -27,6 +28,8 @@ PollOption, Sticker, Video, + VideoMessage, + VoiceMessage ) try: @@ -47,13 +50,24 @@ telegram_available = False +def _is_attachment_mediagroup_combinable(attachment: Attachment): + """ + Return true if the attachment can be sent in a mediagroup, false otherwise. + + :param attachment: Attachment to check. + :return: If the attachment can belong to a mediagroup. + """ + + return isinstance(attachment, DataAttachment) and not isinstance(attachment, VoiceMessage) and not isinstance(attachment, VideoMessage) + + class _AbstractTelegramInterface(MessengerInterface): """ Messenger interface mixin for Telegram API usage. """ - supported_request_attachment_types = {Location, Contact, Poll, Sticker, Audio, Video, Animation, Image, Document, Invoice} - supported_response_attachment_types = {Location, Contact, Poll, Sticker, Audio, Video, Animation, Image, Document} + supported_request_attachment_types = {Location, Contact, Poll, Sticker, Audio, Video, Animation, Image, Document, VoiceMessage, VideoMessage, Invoice} + supported_response_attachment_types = {Location, Contact, Poll, Sticker, Audio, Video, Animation, Image, Document, VoiceMessage, VideoMessage} def __init__(self, token: str, attachments_directory: Optional[Path] = None) -> None: super().__init__(attachments_directory) @@ -207,6 +221,27 @@ def extract_message_from_telegram(self, update: TelegramMessage) -> Message: thumbnail=thumbnail, ) ] + if update.voice is not None: + message.attachments += [ + VoiceMessage( + id=update.voice.file_id, + title=update.voice.file_unique_id, + mime_type=update.voice.mime_type, + ) + ] + if update.video_note is not None: + thumbnail = ( + Image(id=update.video_note.thumbnail.file_id, title=update.video_note.thumbnail.file_unique_id) + if update.video_note.thumbnail is not None + else None + ) + message.attachments += [ + VideoMessage( + id=update.video_note.file_id, + title=update.video_note.file_unique_id, + thumbnail=thumbnail, + ) + ] return message @@ -223,16 +258,17 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes :param message: DFF message that will be processed into Telegram updates. """ + message_text_covered = False if message.attachments is not None: files = list() - media_group_attachments_num = len([att for att in message.attachments if isinstance(att, DataAttachment)]) + media_group_attachments_num = len([att for att in message.attachments if _is_attachment_mediagroup_combinable(att)]) for attachment in message.attachments: if isinstance(attachment, Location): await bot.send_location( chat_id, attachment.latitude, attachment.longitude, - **generate_extra_fields(attachment, ["horizontal_accuracy", "disable_notification", "protect_content", "reply_markup"]), + **generate_extra_fields(attachment, ["horizontal_accuracy", "disable_notification", "protect_content", "reply_markup", "message_effect_id"]), ) if isinstance(attachment, Contact): await bot.send_contact( @@ -240,21 +276,21 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes attachment.phone_number, attachment.first_name, attachment.last_name, - **generate_extra_fields(attachment, ["vcard", "disable_notification", "protect_content", "reply_markup"]), + **generate_extra_fields(attachment, ["vcard", "disable_notification", "protect_content", "reply_markup", "message_effect_id"]), ) if isinstance(attachment, Poll): await bot.send_poll( chat_id, attachment.question, [option.text for option in attachment.options], - **generate_extra_fields(attachment, ["is_anonymous", "type", "allows_multiple_answers", "correct_option_id", "explanation", "explanation_parse_mode", "open_period", "is_closed", "disable_notification", "protect_content", "reply_markup"]), + **generate_extra_fields(attachment, ["is_anonymous", "type", "allows_multiple_answers", "correct_option_id", "explanation", "explanation_parse_mode", "open_period", "is_closed", "disable_notification", "protect_content", "reply_markup", "message_effect_id"]), ) if isinstance(attachment, Sticker): sticker = await attachment.get_bytes(self) if attachment.id is None else attachment.id await bot.send_sticker( chat_id, sticker, - **generate_extra_fields(attachment, ["disable_notification", "protect_content", "reply_markup", "emoji"]), + **generate_extra_fields(attachment, ["disable_notification", "protect_content", "reply_markup", "emoji", "message_effect_id"]), ) if isinstance(attachment, Audio): attachment_bytes = await attachment.get_bytes(self) @@ -269,11 +305,11 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes else: await bot.send_audio( chat_id, - audio=attachment_bytes, + attachment_bytes, caption=message.text, - **generate_extra_fields(attachment, ["performer", "title", "disable_notification", "reply_markup", "parse_mode", "thumbnail"]), + **generate_extra_fields(attachment, ["performer", "title", "disable_notification", "reply_markup", "parse_mode", "thumbnail", "message_effect_id"]), ) - return + message_text_covered = True if isinstance(attachment, Video): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: @@ -289,9 +325,9 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes chat_id, attachment_bytes, caption=message.text, - **generate_extra_fields(attachment, ["disable_notification", "reply_markup", "parse_mode", "supports_streaming", "has_spoiler", "thumbnail", "filename"]), + **generate_extra_fields(attachment, ["disable_notification", "reply_markup", "parse_mode", "supports_streaming", "has_spoiler", "thumbnail", "filename", "message_effect_id"]), ) - return + message_text_covered = True if isinstance(attachment, Animation): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: @@ -307,9 +343,9 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes chat_id, attachment_bytes, caption=message.text, - **generate_extra_fields(attachment, ["parse_mode", "disable_notification", "reply_markup", "has_spoiler", "thumbnail", "filename"]), + **generate_extra_fields(attachment, ["parse_mode", "disable_notification", "reply_markup", "has_spoiler", "thumbnail", "filename", "message_effect_id"]), ) - return + message_text_covered = True if isinstance(attachment, Image): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: @@ -325,9 +361,9 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes chat_id, attachment_bytes, caption=message.text, - **generate_extra_fields(attachment, ["disable_notification", "reply_markup", "parse_mode", "has_spoiler", "filename"]), + **generate_extra_fields(attachment, ["disable_notification", "reply_markup", "parse_mode", "has_spoiler", "filename", "message_effect_id"]), ) - return + message_text_covered = True if isinstance(attachment, Document): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: @@ -343,22 +379,42 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes chat_id, attachment_bytes, caption=message.text, - **generate_extra_fields(attachment, ["disable_notification", "reply_markup", "parse_mode", "thumbnail", "filename"]), + **generate_extra_fields(attachment, ["disable_notification", "reply_markup", "parse_mode", "thumbnail", "filename", "message_effect_id"]), ) - return + message_text_covered = True + if isinstance(attachment, VoiceMessage): + attachment_bytes = await attachment.get_bytes(self) + if attachment_bytes is not None: + await bot.send_voice( + chat_id, + attachment_bytes, + caption=message.text, + **generate_extra_fields(attachment, ["disable_notification", "reply_markup", "parse_mode", "filename", "protect_content", "message_effect_id"]), + ) + message_text_covered = True + if isinstance(attachment, VideoMessage): + attachment_bytes = await attachment.get_bytes(self) + if attachment_bytes is not None: + await bot.send_video_note( + chat_id, + attachment_bytes, + caption=message.text, + **generate_extra_fields(attachment, ["disable_notification", "reply_markup", "parse_mode", "thumbnail", "filename", "protect_content", "message_effect_id"]), + ) + message_text_covered = True if len(files) > 0: await bot.send_media_group( chat_id, files, caption=message.text, - **generate_extra_fields(message, ["disable_notification", "protect_content"]), + **generate_extra_fields(message, ["disable_notification", "protect_content", "message_effect_id"]), ) - return - if message.text is not None: + message_text_covered = True + if message.text is not None and not message_text_covered: await bot.send_message( chat_id, message.text, - **generate_extra_fields(message, ["parse_mode", "disable_notification", "protect_content", "reply_markup"]), + **generate_extra_fields(message, ["parse_mode", "disable_notification", "protect_content", "reply_markup", "message_effect_id"]), ) async def _on_event( diff --git a/dff/script/core/message.py b/dff/script/core/message.py index 01a7da03b..a6a410e0a 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -286,6 +286,18 @@ class Document(DataAttachment): dff_attachment_type: Literal["document"] = "document" +class VoiceMessage(DataAttachment): + """Represents a voice message.""" + + dff_attachment_type: Literal["voice_message"] = "voice_message" + + +class VideoMessage(DataAttachment): + """Represents a video message.""" + + dff_attachment_type: Literal["video_message"] = "video_message" + + class Message(DataModel): """ Class representing a message and contains several @@ -299,7 +311,7 @@ class level variables to store message information. text: Optional[str] = None commands: Optional[List[Command]] = None - attachments: Optional[List[Union[CallbackQuery, Location, Contact, Invoice, Poll, Audio, Video, Animation, Image, Sticker, Document]]] = None + attachments: Optional[List[Union[CallbackQuery, Location, Contact, Invoice, Poll, Audio, Video, Animation, Image, Sticker, Document, VoiceMessage, VideoMessage]]] = None annotations: Optional[JSONSerializableDict] = None misc: Optional[JSONSerializableDict] = None original_message: Optional[SerializableVaue] = None @@ -308,7 +320,7 @@ def __init__( self, text: Optional[str] = None, commands: Optional[List[Command]] = None, - attachments: Optional[List[Union[CallbackQuery, Location, Contact, Invoice, Poll, Audio, Video, Animation, Image, Sticker, Document]]] = None, + attachments: Optional[List[Union[CallbackQuery, Location, Contact, Invoice, Poll, Audio, Video, Animation, Image, Sticker, Document, VoiceMessage, VideoMessage]]] = None, annotations: Optional[JSONSerializableDict] = None, misc: Optional[JSONSerializableDict] = None, **kwargs, diff --git a/tests/messengers/telegram/test_happy_paths.json b/tests/messengers/telegram/test_happy_paths.json index bf493d163..276ecb9a4 100644 --- a/tests/messengers/telegram/test_happy_paths.json +++ b/tests/messengers/telegram/test_happy_paths.json @@ -5,7 +5,7 @@ "received_message": "{\"text\":\"/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Hi\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_message(42, 'Hi', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None)" + "send_message(42, 'Hi', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" ] }, { @@ -13,7 +13,7 @@ "received_message": "{\"text\":\"Hi\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Hi\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_message(42, 'Hi', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None)" + "send_message(42, 'Hi', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" ] } ], @@ -23,7 +23,7 @@ "received_message": "{\"text\":\"/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Type \\\"location\\\", \\\"contact\\\", \\\"poll\\\", \\\"sticker\\\" \\\"audio\\\", \\\"video\\\", \\\"animation\\\", \\\"image\\\", \\\"document\\\" or to receive a corresponding attachment!\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_message(42, 'Type \"location\", \"contact\", \"poll\", \"sticker\" \"audio\", \"video\", \"animation\", \"image\", \"document\" or to receive a corresponding attachment!', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None)" + "send_message(42, 'Type \"location\", \"contact\", \"poll\", \"sticker\" \"audio\", \"video\", \"animation\", \"image\", \"document\" or to receive a corresponding attachment!', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" ] }, { @@ -31,8 +31,8 @@ "received_message": "{\"text\":\"location\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your location!\",\"commands\":null,\"attachments\":[{\"longitude\":3.916667,\"latitude\":50.65,\"dff_attachment_type\":\"location\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_location(42, 50.65, 3.916667, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=None)", - "send_message(42, \"Here's your location!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None)" + "send_location(42, 50.65, 3.916667, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)", + "send_message(42, \"Here's your location!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" ] }, { @@ -40,8 +40,8 @@ "received_message": "{\"text\":\"contact\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your contact!\",\"commands\":null,\"attachments\":[{\"phone_number\":\"8-900-555-35-35\",\"first_name\":\"Hope\",\"last_name\":\"Credit\",\"dff_attachment_type\":\"contact\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_contact(42, '8-900-555-35-35', 'Hope', 'Credit', vcard=None, disable_notification=None, protect_content=None, reply_markup=None)", - "send_message(42, \"Here's your contact!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None)" + "send_contact(42, '8-900-555-35-35', 'Hope', 'Credit', vcard=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)", + "send_message(42, \"Here's your contact!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" ] }, { @@ -49,8 +49,8 @@ "received_message": "{\"text\":\"poll\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your poll!\",\"commands\":null,\"attachments\":[{\"question\":\"What is the poll question?\",\"options\":[{\"text\":\"This one!\",\"votes\":0,\"dff_attachment_type\":\"poll_option\"},{\"text\":\"Not this one :(\",\"votes\":0,\"dff_attachment_type\":\"poll_option\"}],\"dff_attachment_type\":\"poll\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_poll(42, 'What is the poll question?', ['This one!', 'Not this one :('], is_anonymous=None, type=None, allows_multiple_answers=None, correct_option_id=None, explanation=None, explanation_parse_mode=None, open_period=None, is_closed=None, disable_notification=None, protect_content=None, reply_markup=None)", - "send_message(42, \"Here's your poll!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None)" + "send_poll(42, 'What is the poll question?', ['This one!', 'Not this one :('], is_anonymous=None, type=None, allows_multiple_answers=None, correct_option_id=None, explanation=None, explanation_parse_mode=None, open_period=None, is_closed=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)", + "send_message(42, \"Here's your poll!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" ] }, { @@ -58,8 +58,8 @@ "received_message": "{\"text\":\"sticker\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your sticker!\",\"commands\":null,\"attachments\":[{\"source\":null,\"cached_filename\":null,\"id\":\"CAACAgIAAxkBAAErAAFXZibO5ksphCKSXSe1CYiw5588yqsAAkEAAzyKVxogmx2BPCogYDQE\",\"title\":\"A sticker I've just found\",\"use_cache\":true,\"dff_attachment_type\":\"sticker\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_sticker(42, 'CAACAgIAAxkBAAErAAFXZibO5ksphCKSXSe1CYiw5588yqsAAkEAAzyKVxogmx2BPCogYDQE', disable_notification=None, protect_content=None, reply_markup=None, emoji=None)", - "send_message(42, \"Here's your sticker!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None)" + "send_sticker(42, 'CAACAgIAAxkBAAErAAFXZibO5ksphCKSXSe1CYiw5588yqsAAkEAAzyKVxogmx2BPCogYDQE', disable_notification=None, protect_content=None, reply_markup=None, emoji=None, message_effect_id=None)", + "send_message(42, \"Here's your sticker!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" ] }, { @@ -67,7 +67,7 @@ "received_message": "{\"text\":\"audio\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your audio!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/separation-william-king.mp3\",\"cached_filename\":null,\"id\":null,\"title\":\"Separation melody by William King\",\"use_cache\":true,\"dff_attachment_type\":\"audio\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_audio(42, audio=<>, caption=\"Here's your audio!\", performer=None, title=None, disable_notification=None, reply_markup=None, parse_mode=None, thumbnail=None)" + "send_audio(42, <>, caption=\"Here's your audio!\", performer=None, title=None, disable_notification=None, reply_markup=None, parse_mode=None, thumbnail=None, message_effect_id=None)" ] }, { @@ -75,7 +75,7 @@ "received_message": "{\"text\":\"video\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your video!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/crownfall-lags-nkognit0.mp4\",\"cached_filename\":null,\"id\":null,\"title\":\"Epic Dota2 gameplay by Nkognit0\",\"use_cache\":true,\"dff_attachment_type\":\"video\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_video(42, <>, caption=\"Here's your video!\", disable_notification=None, reply_markup=None, parse_mode=None, supports_streaming=None, has_spoiler=None, thumbnail=None, filename=None)" + "send_video(42, <>, caption=\"Here's your video!\", disable_notification=None, reply_markup=None, parse_mode=None, supports_streaming=None, has_spoiler=None, thumbnail=None, filename=None, message_effect_id=None)" ] }, { @@ -83,7 +83,7 @@ "received_message": "{\"text\":\"animation\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your animation!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/hong-kong-simplyart4794.gif\",\"cached_filename\":null,\"id\":null,\"title\":\"Hong Kong skyscraper views by Simplyart4794\",\"use_cache\":true,\"dff_attachment_type\":\"animation\",\"filename\":\"hk.gif\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_animation(42, <>, caption=\"Here's your animation!\", parse_mode=None, disable_notification=None, reply_markup=None, has_spoiler=None, thumbnail=None, filename='hk.gif')" + "send_animation(42, <>, caption=\"Here's your animation!\", parse_mode=None, disable_notification=None, reply_markup=None, has_spoiler=None, thumbnail=None, filename='hk.gif', message_effect_id=None)" ] }, { @@ -91,7 +91,7 @@ "received_message": "{\"text\":\"image\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your image!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/deeppavlov.png\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov logo\",\"use_cache\":true,\"dff_attachment_type\":\"image\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_photo(42, <>, caption=\"Here's your image!\", disable_notification=None, reply_markup=None, parse_mode=None, has_spoiler=None, filename=None)" + "send_photo(42, <>, caption=\"Here's your image!\", disable_notification=None, reply_markup=None, parse_mode=None, has_spoiler=None, filename=None, message_effect_id=None)" ] }, { @@ -99,15 +99,31 @@ "received_message": "{\"text\":\"document\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your document!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/deeppavlov-article.pdf\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov article\",\"use_cache\":true,\"dff_attachment_type\":\"document\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_document(42, <>, caption=\"Here's your document!\", disable_notification=None, reply_markup=None, parse_mode=None, thumbnail=None, filename=None)" + "send_document(42, <>, caption=\"Here's your document!\", disable_notification=None, reply_markup=None, parse_mode=None, thumbnail=None, filename=None, message_effect_id=None)" ] }, { - "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 2, 32, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=11, supergroup_chat_created=False, text='something else'), update_id=11)", + "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 2, 46, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=11, supergroup_chat_created=False, text='voice message'), update_id=11)", + "received_message": "{\"text\":\"voice message\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_message": "{\"text\":\"Here's your voice message!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/separation-william-king.mp3\",\"cached_filename\":null,\"id\":null,\"title\":null,\"use_cache\":true,\"dff_attachment_type\":\"voice_message\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_functions": [ + "send_voice(42, <>, caption=\"Here's your voice message!\", disable_notification=None, reply_markup=None, parse_mode=None, filename=None, protect_content=None, message_effect_id=None)" + ] + }, + { + "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 3, 16, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=12, supergroup_chat_created=False, text='video message'), update_id=12)", + "received_message": "{\"text\":\"video message\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_message": "{\"text\":\"Here's your video message!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/crownfall-lags-nkognit0.mp4\",\"cached_filename\":null,\"id\":null,\"title\":null,\"use_cache\":true,\"dff_attachment_type\":\"video_message\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_functions": [ + "send_video_note(42, <>, caption=\"Here's your video message!\", disable_notification=None, reply_markup=None, parse_mode=None, thumbnail=None, filename=None, protect_content=None, message_effect_id=None)" + ] + }, + { + "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 2, 32, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=13, supergroup_chat_created=False, text='something else'), update_id=13)", "received_message": "{\"text\":\"something else\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Unknown attachment type, try again! Supported attachments are: \\\"location\\\", \\\"contact\\\", \\\"poll\\\", \\\"sticker\\\", \\\"audio\\\", \\\"video\\\", \\\"animation\\\", \\\"image\\\" and \\\"document\\\".\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_message(42, 'Unknown attachment type, try again! Supported attachments are: \"location\", \"contact\", \"poll\", \"sticker\", \"audio\", \"video\", \"animation\", \"image\" and \"document\".', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None)" + "send_message(42, 'Unknown attachment type, try again! Supported attachments are: \"location\", \"contact\", \"poll\", \"sticker\", \"audio\", \"video\", \"animation\", \"image\" and \"document\".', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" ] } ], @@ -117,7 +133,7 @@ "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))))" + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" ] }, { @@ -125,7 +141,7 @@ "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"formatted\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":\"\\nHere's your formatted text\\\\!\\nYou can see **text in bold** and _text in italic_\\\\.\\n\\\\> Here's a [link](https:\/\/github.com\/deeppavlov\/dialog_flow_framework) in a quote\\\\.\\nRun \/start command again to restart\\\\.\\n\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null,\"parse_mode\":\"MarkdownV2\",\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_message(42, \"\\nHere's your formatted text\\\\!\\nYou can see **text in bold** and _text in italic_\\\\.\\n\\\\> Here's a [link](https://github.com/deeppavlov/dialog_flow_framework) in a quote\\\\.\\nRun /start command again to restart\\\\.\\n\", parse_mode=, disable_notification=None, protect_content=None, reply_markup=None)" + "send_message(42, \"\\nHere's your formatted text\\\\!\\nYou can see **text in bold** and _text in italic_\\\\.\\n\\\\> Here's a [link](https://github.com/deeppavlov/dialog_flow_framework) in a quote\\\\.\\nRun /start command again to restart\\\\.\\n\", parse_mode=, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" ] }, { @@ -133,7 +149,7 @@ "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))))" + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" ] }, { @@ -141,9 +157,9 @@ "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"attachments\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":\"Here's your message with multiple attachments (a location and a sticker)!\\nRun \/start command again to restart.\",\"commands\":null,\"attachments\":[{\"longitude\":30.3141,\"latitude\":59.9386,\"dff_attachment_type\":\"location\",\"__pickled_extra_fields__\":[]},{\"source\":null,\"cached_filename\":null,\"id\":\"CAACAgIAAxkBAAErBZ1mKAbZvEOmhscojaIL5q0u8vgp1wACRygAAiSjCUtLa7RHZy76ezQE\",\"title\":null,\"use_cache\":true,\"dff_attachment_type\":\"sticker\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_location(42, 59.9386, 30.3141, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=None)", - "send_sticker(42, 'CAACAgIAAxkBAAErBZ1mKAbZvEOmhscojaIL5q0u8vgp1wACRygAAiSjCUtLa7RHZy76ezQE', disable_notification=None, protect_content=None, reply_markup=None, emoji=None)", - "send_message(42, \"Here's your message with multiple attachments (a location and a sticker)!\\nRun /start command again to restart.\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None)" + "send_location(42, 59.9386, 30.3141, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)", + "send_sticker(42, 'CAACAgIAAxkBAAErBZ1mKAbZvEOmhscojaIL5q0u8vgp1wACRygAAiSjCUtLa7RHZy76ezQE', disable_notification=None, protect_content=None, reply_markup=None, emoji=None, message_effect_id=None)", + "send_message(42, \"Here's your message with multiple attachments (a location and a sticker)!\\nRun /start command again to restart.\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" ] }, { @@ -151,7 +167,7 @@ "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))))" + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" ] }, { @@ -159,7 +175,7 @@ "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"secret\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":\"Here's your secret image! Run \/start command again to restart.\",\"commands\":null,\"attachments\":[{\"source\":\"https:\/\/github.com\/deeppavlov\/dialog_flow_framework\/wiki\/example_attachments\/deeppavlov.png\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov logo\",\"use_cache\":true,\"dff_attachment_type\":\"image\",\"has_spoiler\":true,\"filename\":\"deeppavlov_logo.png\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_photo(42, <>, caption=\"Here's your secret image! Run /start command again to restart.\", disable_notification=None, reply_markup=None, parse_mode=None, has_spoiler=True, filename='deeppavlov_logo.png')" + "send_photo(42, <>, caption=\"Here's your secret image! Run /start command again to restart.\", disable_notification=None, reply_markup=None, parse_mode=None, has_spoiler=True, filename='deeppavlov_logo.png', message_effect_id=None)" ] }, { @@ -167,7 +183,7 @@ "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))))" + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" ] }, { @@ -175,7 +191,7 @@ "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"thumbnail\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":\"Here's your document with tumbnail! Run \/start command again to restart.\",\"commands\":null,\"attachments\":[{\"source\":\"https:\/\/github.com\/deeppavlov\/dialog_flow_framework\/wiki\/example_attachments\/deeppavlov-article.pdf\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov article\",\"use_cache\":true,\"dff_attachment_type\":\"document\",\"filename\":\"deeppavlov_article.pdf\",\"thumbnail\":\"gASVySsAAAAAAABCwisAAIlQTkcNChoKAAAADUlIRFIAAADIAAAAyAgCAAAAIjo5yQAAK4lJREFU\\neJzsnXdcFFfXx+\/sbF\/KsnSQLiiIUlSwgo+KYi9RAbvYS4wleZ7EqNFojEaTqOlqLLFh77F3jaCA\\nCEgREJDeWbb394PwIiIus7NTFrPfj3\/g7My9h+U3t557DlWj0QAjRrCGQrYBRj5MjMIyggtGYRnB\\nBaOwjOCCUVhGcMEoLCO4YBSWEVwwCssILhiFZQQXqGQbgBIlf6+qYhHZVuAMbEOzP0NhdifbDjS0\\n1xZLVbmSbBPwR1WuEsSQbQRK2quwgEZCtgWEoBGTbQFK2q2wjBg2RmEZwQWjsIzgglFYRnDBKCw0\\n8GXsEhGPL2PrU4hcRX1a4Y6dUYZFe13H0s7DEu9tTycxYflgp8Ro76sQhOipx2Ve3yZEMWH5KNfY\\nyZ1ua7nzUn7Q7ucjulrm7gz5FbWRNwv9l91fkDl1DuoSDJkPU1ilYh6DIt8QvD\/65qdcunCoS8LG\\nJ5OFCtZnAcddzCrOvex9raB7oHXWbJ+rmxPq9ZfF77Cgy8VyiYVGAzb13jvk3OYulnnPq10flnSh\\nQqqvgw8czw515FQOd31yLCuExxBM9rrDl5mk1zg3VLcrddizKo\/edmmhDsm704Z9HXywWMj74\/mI\\n9cEHm1t1JPM\/D0u7MGH5V0EHM2s6XMzrNanjPZK+Idz5YLtCGqzqyC0Jc46\/Vej\/6YO5tqyavvap\\n824vyxdYr4md8XHXs\/vShqZVO5\/L7ZNe4+zFLVjxYD4AQAMgCqQGQEOnKDtxC77scVitATFZoQo1\\nvDF+MgDgh6cfmdFFzSs6md3vyIuBEzzurX88tUzC\/Tsv6G6R76X8IImS3sKkblYvV\/c4nFdnczwr\\n1MO8JNz5ybGsAcR+K8TxYbZYTYgVTAum8GZhQHKVuylNYsuqiSvtXCs3+eT+Ig5NSgFqAMB837\/N\\naKIfnk4AAMSWek++umplwClvXsHauGl3Cv3rFGwf3qto76urHkUnlHfkyznBdi+aV5FV6xhkm\/Gf\\nDsn97FOTq9wjve5cyO39qNRnR8gvDTeoNRAF0mg0YF\/6kCflncrEFiGOqTym0Ncyj6RvhQg+WGEV\\nCS1\/SxlxIifk0sjVpSIelyGY3OkOC5axqPX\/1vQ8DEEaJ9MKAEB6tVNiRcchzvEAgP4OqSeHbWgo\\n4VhW6Ibgv\/akhQMAmFTFoq7nh1345que9b3bS75dvsCmTMzN4dt15BZtSYi8VeB37VWPdUH1n\/Y5\\nucOGXdPdOhsAcDqnz+K7S4tnRQoVzN3Ph18Yufa\/D+sHVeVi82eV7jIV7XFZpyDbTLK\/LeyB161b\\nR7YNOpNUJLBWbtVyA5WigiDIhCZdH3zQw7w0xDGlQGjzoMS3g0mln3Wuv1XO+dzeVVKzYNuMvzLD\\nxEpmgHXOZ4EnmLDcgV3dyaKwoZBO3MLYMu+JHvd9eK\/sODU+vPwqqfmSbufYNHlcWSeJitnBpJIC\\naUa7xVox6+4U+23svd+HV8BliJmwfIRrnA+vAADAospMaeJ+DmkMWOlrmXe\/2HdOlyte3CKJkpFY\\n4dXLLqNAaNPbLv19v0iB2FvNGsamwzh8i\/gCta9zhQkFdRuu515IqxQvGIFJgb1Pbv8l9OfA162L\\nAbInLXz14+VL+nb4dICLOas9dS\/txtbcasmmG3kH4ksUKizfhEsjV5vSDXo\/u1ai3Hgjb09c8ReD\\nXJf2dyLbHKS0g1mhUqVefTnHc9M\/e+KKsVUVAIDHFNIoKmzLxINSgfyTsy\/cNj688aKabFsQYegt\\n1vnUioWnMorr5GQbYhDk1UjD\/ng6pBNvX4SPgzmDbHO0YbgtVoVQPn5f8ph9yUZVteBaZnWnzY9+\\neVBAtiHaMFBh7Xtc7L0l9kxqBdmGGChCuWrJmRcDfk14Xiok25bWMThhVYrkkw+mRh9LrxIrtNym\\n1iDb\/2vnKFTaFhru5tQG73iy61GRSk2gTcgwLGHdf1nTdWvc0aSyNu88n9uLEIvIRKGCrxcEar9H\\nJFfPP5kxdt+zaq3vIfEY0DrWqr+zt95+pVQjsodNlYY5JVix6vC3ixw0GpBY4ZlU2RHh\/Xam9PPR\\n3Xo6m+NsF1IMQlg1YsWMo2kX0irJNqR9w6RSNg33WB7qTLYhwCCElVkuGrsvOaO8vR5HMTSW9u+w\\nbZQnDSZ5kEOysO69rBn95zO+tB0sUbYj+rtzL0T7kbsFRKaw9sYVLziZoUA2qDKiE64WzDuLAl14\\nLLIMIEdYagDWXc7ZcONDdkgiHWcu88QM3yCShvMkCEupUn95Oee7268IrvdfiCWbejbar58bl\/iq\\nSRDWsrOZO+4XElzpvxZTBnw+2m9ARwuC6yVaWPNPpO+KLSayRiMsGuXvOf4Ea4vQSemS0xlGVRGP\\nRKEevifpQW4NkZUSJ6z\/Xcz65WERYdUZaY5EoR79Z3JCAXEbFQQJ69ubucbROrnUSJRDdydllImI\\nqY4IYR14Urzq75cEVGREO1UixcDfEssEMgLqwl1Yj1\/xF5z8AI83tVNKBPLwXUkiOe5bHfgKK7Nc\\nNHx3klRpeO5C\/2KSioXzTqTLcfbhwlFYApky8mBqlViJXxVG0HEksWzLLXy3PfASlkoN5p\/ISCo2\\nUMdZI19dyb2agaOfEl7C+vFe\/tGnbTuCGiELDQBRh57nVuF1phIXYT15Vbf6cg4eJRvBkBqJctJf\\nKTg5l2AvLIlCFXUoVaY0OsO0A+ILBeuv4tIEYC+sZWdf5ODWwBrBnK+v5z1+xce8WIyFdTWjyrgb\\n2O6IPJgqUWC8soWlsKpE8jnH3xuRx4jBklstXXEuC9sysXSbWXAi\/Y\/22FypVeySZJqwTOQYqDSx\\naf4JpJSZ5j9kVuXAMoGKaS4zcxS49FEzzcizFS9gCDxe1jOwA2a\/GmbCupdTM+j3pwhPBRoU1k\/2\\nmL2KrZ+BQ3DRgM9lPLfGD9Qqu39+4pSlNr9ZRWOV95gtdvAnx1Y88XcwebS0B5OGTZA3bLpChUq9\\n7NyL9qgqWFRl+lpVr18ylVnOraaPOMWJLVRVf79CYvfoF0algQZq04ekYuEvDzHz7MXmhNAfj4qe\\nFrXLRXYNBQYQBWgaN86cLU1H9LTvwGX06GBWEF+5Pe7Nnc4uLr6+vjeuX5fL5X1Lz2\/9JkahUqeW\\nCp8VCwv5sttZNbXSdr95tf5a7pRAOzszDAIkYdAVVork3ltiK0WGFTsAOc4pR2gv6hsqFpt1\/NQp\\nb2\/vhusymWxu9Ox\/Hj4EAMyZO3fFZ5\/S6fSE+PiICRMBAIdjjgb3ehM\/Qq5SpxQLb2fX3M+tvZxR\\nhXmAOMJY3Nfx5\/Gd9S8HA2GtbZ8HubhMamSA7YI+jn4OpokJiTk52UOHDjUzb3lYKutFFpfLtbax\\nbroya\/qM+\/furV67ZmZ0dKslK1TqI4mlx5LKr2RUtUd9PV0R5O9oqmch+naFxXzZd3fy9SyEYAId\\nTRf1dZzZ07HpGHpg98DA7q3HdfH08mxxxdHREQCQnfXeYRYNpszo6TCjp0OZQLb19qtdsUUCWXs6\\n6r3+2sszs\/z0LETfFuvj05k\/Yzfiw5t+buabhnv0d9frvMqd27dTklM6enYcNnw4kvsVKvWeuOLV\\nl3Oq248HUeKKoAD9Gi29hFUqkDl9\/bBdTAaDnM2+Dncf2smSLANEctX2e6++u51f1x4CVQzsaHFz\\nYRuhubSjl7AWn8r49R9DP3hjxoQ3hnss6ONIegAWAMCrGunnl7JjnpYZ\/rv4bGVQNwf0jRZ6YfGl\\nSpu19+SGPf0Z2om3P9IHk\/kzhlxKq1xwMqOQT8ShBtREBdgemeqL+nH0L\/E313MNWVVUCrRrYucr\\n8wIMTVUAgBE+Vokrg0Z3sSLbEG0cfVqWVYE+aBlKYSnVmt1xBrMtqFZRheX0mnxGTV7DP0+W9OHH\\n3ef2ciTbsvdizaGfi\/bbPMKDUfuKKqoAakMceP3+CP04B2VXeCSxdMrh56hr1R8av4hdlsoue04V\\nV9FElZDmzR+mi2+XP\/fvt7Iy6PagiatXrny2YqVIKpOb2Co51mIbH7G9n5JjEMYzqJSqDSEcVCmi\\nUAor8Ic4cvZw1ErT\/Fjuiyt0YWmrn\/fr33\/vgf0UCvnjdOQ8S3q2cN688vLypisSS8+aTsMk9t1I\\ntaue3z7qtKBPBxQPohHW00JB4I+PUVSmJ5xXsVYpJ6jS97o7jhk7dsu2rVSqoedxeZfCgoIJ48ZX\\nVr51bEbKdan0j5JZIg2cjAe9XMweLe2J4kE0b\/aBeKJHV1Rhuf397+2e7NGiqtFjxnz3\/bb2qCoA\\nQAcnp1Nnzzg4ODS\/yKzNd7yzhffsGKQkLelLbH7do7xaFA\/qLCylWnOW2EwkjKoch3tb2eXafFND\\nBwzYvPU7GG5\/CSObcOzQ4fc9u1tsVkJAY5F93fbRzxQ5ac4jp5LR\/Ll1Ftbl9Mr8GuIWYFhlaQ73\\nttIk2mI7+fr6bv9pJ53eMrl3u8PHx+fnX39lMpktrnPK0+zv\/0iRCUix6nRKOYrj+DoL62Ryuc6V\\noIVZnmH\/z06KWtsWm42NzW+7d5ma6rsbbyD06dtn9Vdr373OrM23f7gDaP0qcCK3WhpfoPMxHt2E\\nJVOqL6dX6VoHOmiCUtvHu6C2vsodP\/9kb29PjEnEEBkVNSli0rvXmTV5No93k2ER2BWr84KWbsJK\\nKKirIMahT6O2jt9HlbURge6T5ct7BgURYQ+xrFm3zt3D493rpkUJJv\/vSE0kt7J1DjOpm7CQ5OXC\\nBIv0i6zqNk7oevv4fPzJUmLsIRgWi7Xz558gqJXUedaJByGllGB78qqlz4p0G+HpJqzrmUQkJKbI\\nBOY5N9u4h0LZvnMHAcaQRWdv70VLlrx7naKScTOvEG\/PZR1D0+ggrIwyUaYeu5LI4WZegeVthMpc\\nsGihR0cyVw4JYM68ue7u7u9et3hxBaiIPmHwIFe38bsOwnpCSMxdSCExzX+o\/R4nZ+ely5YRYAy5\\nmJqafva\/\/757HVIrTfP\/IdiY6y+qq0Q6rNPqICxiFhpYFZnUthYD5y9c0E5X2HUlbOjQ3n36vHud\\nU5RIsCVyleZ5qQ4Rl3UQVjIh4fk4JUnab+js7T1u\/HgCLDEQlq9c8e5FZlU28b3h5QwdVpqQCiu9\\nVJRXg\/9kRKNhad26AQDMmh3NYBic7x5+BHbv7u\/f8kQ\/rJKxKl8QbEkaHi3Wy2oiQl5RheU0sbbX\\nwsbGZviIEQRYYlAsXd7KgJImIjrT8a2cGhniANhIhXU3h4hMLLCijVlnRFQki0Vackey6NO3r5OT\\nU4uLlLYmzpgjlKmQJ7ZAKqzCWiIW5WCtm80wDE+KiCDADEODSqV+NHFCi4uwhIg1xRZkI47ViHRu\\ndVP3RX0UwFrngz2Dguzf9lgiHpXghFp0UcsNsNl0CnsQ5vWOHT9+5\/YdavWbngjSkJCW4V5OzUfd\\nbJDciUhYLyvF5UIi5iCQVnfWEaNGEmCDdjTyFLXwmJYbKKw+AGAvrA4dOvQMCoqLfbNR2Mp2D\/5U\\nId4pRiSsKjH5kWRodNrgsDCyrQAAdoAYAVpvwOscxKDBg5oLixSQZ4RAJCxDiILs69vV2tqabCsA\\nlbsAcBeQUnW\/kBAAviGl6iYyKjAdvCcUkuO72JzQ0FCyTSAZLy8vNptNrg0qNRAgiy+HqMWqM4BY\\ndZ28MYgGpj+qugMqgbYxFsxdBHPwGguGDR1y7sxZnApHSEa5qKdzyyhi74JIWHyyhUWj0VrdMiMe\\njSJHI7ml7Q6TMfjVHhgYSLqwXlZJMBPWS7LHWB09PU1MTMi1oRGqE8QM1nYDbItf5W6tedEQDMIB\\nd\/sQlo0NorUTAqCazwXmc8mqPSBQr5hVmFCAbKkc0eCd9GSW3fzIP2xOInm5uc+Skhpclkk\/O4kw\\nzF77iHFga4tj\/2KwSCSSixcuTI2aPPg\/A9PT0houurm5kWuVGllMhvbhLkej0cg2gVAqKipOHj8R\\nc\/RoUWHL+K5OLs7Z2WSmL0AY6rJtYUnwz3jeJtYGM8bCG4lE8v3WrTFHjkqlrQ9l7OxIPkRZK8Fo\\nHYv0tQYAAJfLJdsE3MnLy1uz6stH\/7R0Znd3d1+\/cUPTaguX2\/ZUH1cQ6qFtYRlCNEhDWWvAAZVK\\ndfP6jZijR+\/dvdv8OgzDYUOGRERF9g8JaX7dhOxgAgj10D7GWKRPhfCgurr67OnTMUdjXua8dTSX\\nx+ONHjt2yrSprY7T28tX0T6EhTq085d\/Z1\/F9JDtwj6Os4P1DW2anZW1e9euy5f+Fovf8pj19PKa\\nO2\/ekPChWlpoDPNL4kr7EJZIhNINN7daiu0OeqkAfQw0pVJ58fyFYzFH45\/EN9cHlUodNnz4lGlT\\nu3fvDrUV5FIkJNojGR1tC8sEVWxTbOHzsc+GTSTV1dVHDh0+deJEQUFB8+s8Hi8iKnJSZOS7Lu3v\\ng89HE18PQxDqoW1hmTLJb9XKyvQNRjKzpz266L8NJBTWxeajPAi+etWqvy9eqqt763Hfrl0jo6KG\\njxxhZqZbttzSUoLisrwPHhuRHsgXDRJk71nUQc6m4R72emQS2HwrD7WwYo4cbfoZgqCh4eFz58\/3\\n80eZXutVPsm51syRNTTtQ1jV1SScSMEWM3Pz2XPmTJ4y2YLH06ecFlNI4oEpiLztEQnLnAnzSc1Z\\nlfwsmcTa9aRnUM\/IqKiRo0frv1Igk8kUCpLPH7QWtKsVEAnL3ZJFbsrn4iKUuTc6mDO62HIaUuvo\\nY4AVh9ZQjhVH513Lo8eP61N1c1JTUrAqCjUOyEYUiITlYckmV1g5OTkymQxFyIbvRnl+N6plilQU\\nzAl2nKP38pX+vMjMJNsE4GGJ6CQ6ImEhnAjgh0wmi330KHTAAMxLlkqlL3NyGg6C0mg0Ty8vAtKl\\nFBYW1tY0HgB2cHTkIR51paam4mkXItwxFBabRv5SVkZ6OrbCevjgwYF9+588fiwQvFlBtbGxCRkQ\\nOmPWrKZc9hhSXV198MCBy39fzs7KaroIQZB\/QMC48eMmToqg0dvoZ69eJiFIZAu8bThIbkP0dvo7\\nkr8HfO\/uPayKqigvX7p4yYyp027dvCkQCKhUqourq6ubG5VKLS8vP3n8xISx47Z8++37HFfQcfHC\\nhWFhQ37asbNBVRYWFp5enjweT6PRPE1MXLt6zbgxY5KfPWv12ZI6WWM7V0vy6igEAJeNaJSJqMXy\\nsUUkUlx5lpRUV1en63Liu7zMyZkxdVpJSQmDwZgwaeL0mTPNzMzOnjmjUqpGjRktkUgOHvjr+LGY\\n3X\/sSkxI\/H3XH3quDjTw7Tff\/Ll7DwDAz98\/Iipy7LhxTXk0FArF6VOnYg4fSUlJmTBu\/A87to8c\\nNar5s2dSyiMPpu6N8FanthHwlwA6WiEN9YOoxTIjb\/F9dqTgt01VENBIpdLrV6\/pWVpeXl7kpIiS\\nkpKQ0NCbd++s37DBxcUlcuLELZu+3fbddyPCh1lZWa3f8PWtu3eDe\/dKiI+fEhkll+ubIOnrdev+\\n3L2HzeHs+PmnU2fPTIqIaJ6dhUajRURGnrlwftPmzVQqddnHS4\/HxDR9uvZKzvj9KUq1Rq7S3Lh2\\nXU9L9KeHE1KnHUTC8rThcFkkaGvx9LoVc4QhQfIfv6qBIM2li9rCvCCBTqezmMyZ0bP2HthvZ2fX\\nMNLKz2tcyxYKBNevXgUA2Nvb\/3Xo0JixYyEKRf8O0dLS0tzc\/PS5syNGajvIOiky4ujxY6ampqrX\\nmWvUAET8lbLhep4ZA742P2CkKzU+Pl5PS\/THgoV0tQWRXCiv8+WfTiE06dfi6XWLpjfu5Hu5KV0c\\nlU8eP66qrLTUI3Wqg4PDqbNnrJrFgHBxdaXT6U3NkrOLS8MPMAxv+\/EHkUikv4\/h4o8\/njJ1KtfC\\nos07\/fz9H8Q+4nA4ZQL58N1JiUUCPweTmGm+nW04e\/7YJZORn5y8rytS\/1WkU2snLqFhP0eHiRdO\\na1RVVS20aLVFXiFNIpFc1LvRsno7soirq+vylSs4HA6DwZg1Ozq4V6+mjyAIwspzFYmqGuBw6oez\\n4\/clJxYJLNnUMzO7dX49Czt96hQmluhJZ8SjbaTCCvVA+tXoj7uzYu0n\/IatA6kMTF9umVfY2AIf\\n2Lcf8z2NufPnP05MiE96+uWaNdiWjJqTM7t6WLKqxMqPDiQX82WJCYkvXhAdyvZdGFTIG3NheVoR\\nFOcEhjVbV9Wy\/j9l3+qt5nkFb\/r1V\/n5N65jP4ZlMBgGFdrU3oyRsCJopLfl0yJhjx8ff\/bjfrIt\\nqmeAhwUL8YomUmF525o4mhGRaXJ0mKRzx8ZzILcfMS7faSnovXv2NI+Y+KFizqQem951RahTiUB+\\n2zJc0AFNYmZs8bXTYdUJqbBgCvDRpVx0MBia6EmNm5JyOdj6eyuT26eJT69euYq3JYYAmw5\/P9or\\nnJaloVCVLOKGIu9jaCdL5DfrsC\/2UVfcT42G9hC6Ozf658RcYOcXtT65\/XnnTrwtMRD+efgwK2aL\\n85UvLLL0XcPTEwgCvvY6TGV0EFZvxFNN1Ezo9ibfyZkr7x30ZGZk7NlFTq5RIlEoFN9u\/AYAQCMj\\n8nYLBna00MkFVwdhdXMwdeW1zIONITCkGuka1\/BzYirtRa62Id3O7dtLiovxM8YQ2L93X3p6Gwlg\\nCKOfm26H0XVbT\/+Ph8W+6hIdTUJKkE0GldI4Kk\/JaGOFVywWf7pi5eGYozgZQwxqeYZaeF6jyNYo\\nXwF1HQTbAKoDhdkLZg8qKFbt+PFHsg18w0gf3daldRPW1O52+57gJaxQxzf+x0+etT0DjYuNPX3q\\n1PiPPsLJHlxRS5+oqjepxZebX2w4aqiu26uE6JkJ7mymSCol32Hp9fIH3d9Rt+1\/3ZzaermYmzPx\\n+lU7mLzJOlRWiaiWLz\/\/orlvUztBoaz8QlE4sIWq3kIj7x+YcWlfxZAQ8gOh17\/z7lyqju6Put3O\\npsNhXhi4kbQO9N7\/vA+FQrFk0eKKCkI3MfVCLZUXR6lqfwSg7f0DM1PN1i9royeRHwt9fm+d3bJ1\\ndsNFmEqFMLKzslYuW65Ukh9rCQmKypUasQ7bnVQYLJstDA8ls91yMKP3cdU5jJTOwprgZ2tnSsQS\\nPHL+efhw7ZerybaibVSCE+q6P3V9CobBuhV8SwvSjt+N6WJN17UjRCEsKgUa44tLuhil+o0xpia6\\nfY\/Hjx37et06HIzCDrVIWbUW3aOmHM2SGaR1iONR9VFoTqRMCbRD8VSbpFa9CQfVK0Bnv82\/9h\/Y\\ntuU7rI3CDJXwIlDmon58fLjEzpqE7r6LLWcwqlE1GmH1d7fwssbeF+Dqq+5NP3u6ofkSf\/\/tt7Wr\\n16hV5AdNfReV6Lw+j1OpYEAvEhz95gSjTBCJ8gzd8hBndA9qoUBoWyZuHCSGBMnsrNHo48ihQ4sX\\nLRIKyTxe2yptJEpBgI8X0YfrYQhM64Eyli5KYc3sac\/UfUDXJmdeNoZwhWEQPgDlVOj61WsfjR2n\\nf+QjjFHrm6LWxZHornB2sIOl7iEFGkApDiYNnhOMfVzo\/Sn\/afp5yliRqQlKv6uc7OzhQ4ZeuqCX\\nH7NYrhLK6v\/pU0gDapVQqGAKFUypEn3AeksLor3QPu6HNBzcu6BvdT4f5IZ59ti0XM6dR41b6A62\\n6qjR6MMi8vn85Z98surzL1B3iyG\/JJiuumO66o5K7z9oeoXGas8pqz2nIq+uQl2IWEJott5hnXk6\\n+cm0AL2wHM0Zk\/wxXixV0Tnb95oq\/7+NmDdZ6OyAvv1Xq9XHY2LGjBx55\/Ztfayqk+o7uGnK5kCB\\n0Is0v5DQE3irBuuVW0WvcdK3Izrq8\/i7qBimWbm0\/ScaXVVZTLBjXTUV1itOcH5e\/pxZ0XOjZyPY\\nVVQCIH99pA+83r9q\/HKuvdDXHepKRlXDD\/Yc9EUVlRG3Id3L2UxXP5kW6CUsNx4rKgDT9ElUhgpm\\n7NxnmpLR+HZ6uat+2VjdPGy9h4fH77t37T1woE\/fvsgLvn3rVnjYkGUfLy0pafDOkKtEV5UVKxUl\\nkbK8TrJs5ut\/JrJsM1k2u\/7nHPN+vEMNzy46lXEtswr173QsqWzzrcYzsSH26ANc3biPozNcCzbr\\n3WRAesYNzygT+X0fJ1dhFnzc6fIXdHGFvY1y95ZqN6fGHuTvW8zV27gyef0g4+DhQ71fS0omk4X2\\n7VdZWYm8cEsLVXiodOFsDwtWUps5Fh6Vdh589juVprGdgCBgb0qnNAtoZ8aEfe1MhnlbTgm0o8GU\\nrArxTw8KnhYJCmplqtfJ19QaTUmdvKkaDlXyfMocOzaawB5PntFmrnxrw6POLaQicDqKotpkYEeL\\nmwv1TYyob7fd2ZYzr5fjzw9bJqlCjYrNBeKKknLq0rUWf2yudrCt75gCfOUOtsrc1+fAmP9\/TovB\\nYAwcNOj4MW0Zmpvo6KqYPEY8Llz8OlLQUySP9LbLmO1zedfzxnPxGg0ornt7P4AP0srEx5+Vv6yS\\nTPSz7fNTvPYp5Ajz8+hUpdaAXw8Sl+nkqyEYZK7DYC1q1WBXDGPBK9iN7+XLAtr8z3kl5ZQaPrRg\\nFS\/3\/08X7ty+XSJpXOKi0dveDre3UW78tPbUH5URo8RtxZ9qyQ\/9\/ljc9RyH2saK2jc38uafSNei\\nKopMwEs9lXn4ak4+mjf5xEXW4ySCTqJH+NuEYHE4GYOJhr0Z438DXdZceal\/UfXAb\/74LwtokxZa\\nmXA0r4rf2Hn\/3v2Tx49PmzGjzTDdNJpmyljR4ulCNgtlT02lqL\/vt+t\/gcfSa5wzapyqpGYaTWNX\\nqAGgSmp2vaB7Nt9RrQGPXgfrhpQyVkUGvbYA0rwWmUYDywT0uiIGv4CilAkBZeGXFod3VFlb6jA3\\njHtK3\/IbQRm\/WDTKhnAPTIrCZga7ItR5\/5MShGmotaOivjVErebD1e9kpTDnWjR4+V1\/\/6loF0fl\\n1ytre3TDYBvEhs23YaeEOrYy7lao4YmXV195FQRey8jhzhYm\/5WWoopKqVM+sfxpfU0nD0TLKBdv\\nMr\/eYd4wuCSA\/\/7HxdMamyPv2AiLTYe3juo4fj8GMX0VHOv3fdS7T28nJ+d+If2HjxhRP3PZtIn\/\\nngh3vQJl276ssTDHPZ8RjaL6LPCEs2lFQgrtZZpQu6oaKCqlzlhpOWuicE6kSEt87vJKyva9pueu\\nsRA60+qPE5fx6QDMtoD1nRU2Z\/Kh1KNP9d2hY1a+cLzbuvfL2Qvnfbt2BQBUVlRu2rjx\/Llzrd42\\nOkz87f+Izr0jkYLpyy3TsnRwgTThqKMnCXsFyrt1VjTNNWVyEJvIuBvLOH6J3dTttgrms8KbCwIG\\nemLmd46lsAprpQE\/PK4U6dX70GvynW5taPWjhYsXWfB4D+7de3D\/wfvCN0SMEq39BGVuEj1RqcC0\\nZZbP0tG417JZag5bU1tHUSiQtk\/YCmtyoO3hKb5YlYaxsAAAB+NLph9N06cESCF1O78EXes\/anB9\\nW4UwdQIeCITQvC94yai0pSsYCsvRjJG4MsjGBEuzMXZ9mdbDXk\/HZQ2NqTBB46Havats\/QoyVfXa\\no1rz84ZqF0eSs5Loyh8TO2OrKuyFBQDYPdHbxgS9c8jrHUOdN9XtrFXbv6phGMAhD0uuZuuqWiaj\\n3QRaig6yH6HjKWckYC8saxO6nr211FK3jSoqrNn6ZQ2Payg5bbt0Uq6cS\/5hQCR4WDJ\/Ht8Jj5Jx\\nSe8x2Iu3JswV9eMS68463T9ppDjQ17B6n4jR4uAALPMP4AGLRjkzyw95kD6dwCtvzBeDXAd7otwZ\\nUHJ0aJmteaolM9E3D2K913SFoubDusY\/EkwB\/50vYNB1a0RRZKHShz2TvLvq4cqnHbyExaLB+6N8\\nEGaKaoHCxFbBQrqgMm+y0NwUZSf4LI02bIbNzYfo\/5z7jnPGz7fKL6rXE9X2L6rN701faeeOytFh\\nYuRF+XTpcvn6tb79dPAF0ofFfR0n43OMrwEcM105mjPPzOrGoeteBQQpTBC5eXHNVJGjdfjjNaeq\\nBprzP15lNbz0K4vLt3V2ddJowE\/7TbbtMisqpc5cYQm438Cmk2CzaVT7kwBqbAbmRgnbdM5pILhX\\nr5gTx52dnXfv3dvFF8v1pFbp78b9YYwXrlXgm0Ktq73J4Sm+KFYAxPbdkNw2f4oQdRI4SwvNgqkN\\n7vDQp99YfL7ZHHmA\/tIKSvSnvN8PNbqyzJwoBMI\/gaq6vhPkDKc5xQF6vf2OdupxQ9vua1d+9tnh\\nmKNsNhsAEPvoURbOkbfdecwzs7rSYXz\/9Ljn5hvja71lpM7uiFKee5v3UGHN+GF6DZFmR4g+ia6D\\nKfWNyoUb7CFTbY6cY\/PrtL0IxWWUnftMhs+wefyM8bpt1Xy+iD9jghgoX8oLBwNlaf13SvdgOMfC\\n1jsAzWPCiPc2qAwGY9z48ddu3li4eFHDlevXri9esFD\/7D1asDGh3VoUaMnBfWEG45X39\/H5xawt\\nt9veoH2DRuN8ZRVNrC0+0eC+kh3rMUizdvwia\/OvbzwITDjqIf2lQX4ydxcVk6GhUTVCMSSRQk+e\\n0VMy6HfjGE1beEyGZv3y2pGDm83+qB40+yMURlOGerVKeOnbHxLjnyQIBHXVVdVMJtPaxsbe3r5P\\nv77Dhg+3sXlzGuXk8RNfrVmDPK8JipV3Dh2+Pt+\/t+6hY1BAkLAAAFGHUmN02aK2jt9vlv9Ayw2b\\n\/lszZgg2U\/rMHOoXW8wzX+rwHnu5KTZ8Wuvb6R3vF8iEarkR5s5B7jnC5\/N\/3Pb9oYMHdbBYd2Gx\\naZQjU33H+L7XeQRbiBOWSg0iDyafTEYaJI1TmGAX99v7P9c8vVyqq0eoFtRqcOEm6\/eDJs2dClvF\\n3kY5f4pwzBCJltoheleq5UYKZ5B2eUml0nNnzn6\/bVt1lc6HNXQSFgRAzDTfSf6YnnzRCnFH1WAK\\nODzFV6ZMuZCG6PiDyMFPyeRSpa13dl07KzBUVf3AiALGhElGDpQkZ9Cu32dm5VKT0uhiSeMYlGeu\\n8vFSuDsrB\/aRBXaVtznw1chTFCVjANUVNp0EMfwlmkEmpm+8QIsKC+Pi4uJiY2\/duFlTo+\/R+zaB\\nADg+3XeCH3GqIlRYAAA6lXJiRtfIgylnUxFoi0IV2fuZ595t9cPOHrgstcMwCOiiCOiiaGjDauvq\\nR1cmHA3KpTJlnqrmu\/pZwnLLogpnCx5PrVaXFBfX1RHn2ANB4ECkD8GqImJW2AIGlXJierepyJbm\\nhI7d3\/cRujhHOkGhAB5X42inRr0A24S7s7K0tDQ9LS0zI4NIVVEp0MnpXVFHjNEHooX1epkAOjCl\\ny+ygtn9bqa2Pktn6OYIunoa1Oagdax4JIbtM6PDfc\/zQxePTHxKE1VDrngifTcPbXqyq9Rza6nU3\\np\/YRzbYB4q3lsakPlnQP0yWtEraQI6wGvhjktjfCm03TZkOdW381pZWBoLmZoTjJIMEWVRA51Pg7\\nmCStDPZzJO6M67uQKSwAwKwgh2vzA7Q4BmpoLIFTL2KNwh5TDnGvwfDOltfnBzhxiQv00CokCwsA\\n0NeNm\/xpr27v99+o6TJWQ9QRKJwgzGF69WDXC3P8rbD2M0YB+cKq7ylM6XGf9JzXq\/U4qioWV+Dc\\nvhstKf5hablM6rlZ3TYM86AYxjtoEMJ6HXuS8sdE74OTfXjsVkZUtZ2Ga6C3TMVzoxZ7qmqw99JU\\nN\/vbBTmbJa4IGk3Udg0SDEVYDUztbv9gSY8+72TcVJjZ891Cm18pLDWItFgIKSzBQVg0VkMnu3qw\\n662FgW6ofCrxw7CE9TqrOefhxz02hLvT4bfa9NrOw1W0N2EFMnIw3dDBmZcF2O9wqJjm7pbM+4u7\\nbxjmwcEu2g9WGJywGlgd5pa0Mrhvs6ZLxbKo7jK26b\/J6e1JWDV8zL9naNmIwLT\/9u6rX0BH\/DBQ\\nYTU0XQ8+7vHLeK+mUON1riEy08YBfjGBATn1J+4pltM0H7+A0xcurJ8\/kYFDqH2sMFzLGljU1+n5\\nZ8GzguxhCgRgakXgdA1UL6l\/4gk90KIPFVUUvgCb18CCx1v99cbz505369oFkwLxw9CF9XoxgrE3\\nwufhku4DO1rIrDrWufYDAEhklLQsQl0zUHMvDoN3gMnmTJkx89qt2zOnT8HCKNxpH38bAECwi\/nN\\nhYE3s6qXnYD5RzLpwtIzV9g+nuQEltGJK\/f0WgSnUKnhI0Z99tlKpw46pzklEeI8SLFCrQaXHzzZ\\ntW1TWUHC3RPlZJvTBlU1lJCJNuiCp8FU2pjx42fPndvJE5vwjUTS\/oTVgFqtvnX7jrPJZhfLe2Tb\\noo29xzjf79Yt\/3t9P0JnTIyMmhU9y90V+yxrxNBehdWEVJSqqNpKkZykwgaXplClAn3H2wpEOgxk\\nbRw6RERGzps\/l2UIoXP0oN0LqwGVUiSu+AkSHaSBHLJteUPMedaGncjWmSAoZOCgydOmDx7QH3ez\\nCOEDEVYTgpoEWH5Jzf+FRiE6DGkLpDIQPs2morqNhQbvbv6TJ0cNGxbONde5xzRkPjRhNVFXeRWS\\n3wOSK1TVc9TH8PVhy2+mf51q3RfIxNSsq5\/f4LCwoeHhdrYGtHOMIR+ssJoQ8p8D6TW1+AakTKND\\nJcRU+iKXGrnYqnl8dogCe3h6+gd2Dw7uGRYWZsLBJpy6wfLhC6s5UmGyVBgPSf6mQ3lKSTqdhst4\\nXyyBJi2yzC2gmfOsnJw6dPMPCAsb7Onp+aE2Tq3y7xJWc6RSPlWTIxZmaqSJGlkCiy4W8DNNWSij\\njKhUQKp2YHNsJQrH\/MretfJe9ra2HTu6w6R0wwbAv1dYrVInKGFSSqmwhi8oA6pKJk0sEpZSNLVQ\\ns8yoSjWDzrCm0y3FcjbLxJFJ54hldCrTncn4wHs3nTAKywgu\/EsbaiN4YxSWEVwwCssILhiFZQQX\\njMIyggtGYRnBBaOwjOCCUVhGcOH\/AgAA\/\/+S+QX6OV9s5wAAAABJRU5ErkJggpQu\\n\",\"__pickled_extra_fields__\":[\"thumbnail\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_document(42, <>, caption=\"Here's your document with tumbnail! Run /start command again to restart.\", disable_notification=None, reply_markup=None, parse_mode=None, thumbnail=<>, filename='deeppavlov_article.pdf')" + "send_document(42, <>, caption=\"Here's your document with tumbnail! Run /start command again to restart.\", disable_notification=None, reply_markup=None, parse_mode=None, thumbnail=<>, filename='deeppavlov_article.pdf', message_effect_id=None)" ] }, { @@ -183,7 +199,7 @@ "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))))" + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" ] }, { @@ -191,7 +207,7 @@ "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"hash\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":\"Alright! Now send me a message with data attachment (audio, video, animation, image, sticker or document)!\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_message(42, 'Alright! Now send me a message with data attachment (audio, video, animation, image, sticker or document)!', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None)" + "send_message(42, 'Alright! Now send me a message with data attachment (audio, video, animation, image, sticker or document)!', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" ] }, { @@ -199,7 +215,7 @@ "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"source\":null,\"cached_filename\":null,\"id\":\"CAACAgIAAxkBAAIEtGZOUac-j5GbuhH9xH-xsJ8QGWKHAAIsAgACRwZDBdGLCpfBS4KvNQQ\",\"title\":null,\"use_cache\":true,\"dff_attachment_type\":\"sticker\",\"is_animated\":false,\"is_video\":false,\"type\":\"regular\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":\"Here's your previous request hash: `0`!\\nRun \/start command again to restart.\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_message(42, \"Here's your previous request hash: `0`!\\nRun /start command again to restart.\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None)" + "send_message(42, \"Here's your previous request hash: `0`!\\nRun /start command again to restart.\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" ] }, { @@ -207,7 +223,7 @@ "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))))" + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" ] }, { @@ -215,7 +231,7 @@ "received_message": "{\"text\":\"some text\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":\"Bot has entered unrecoverable state :\/\\nRun \/start command again to restart.\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_message(42, 'Bot has entered unrecoverable state :/\\nRun /start command again to restart.', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None)" + "send_message(42, 'Bot has entered unrecoverable state :/\\nRun /start command again to restart.', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" ] }, { @@ -223,7 +239,7 @@ "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))))" + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" ] }, { @@ -231,7 +247,7 @@ "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"restart\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))))" + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" ] }, { @@ -239,7 +255,7 @@ "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"quit\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":\"Bot has entered unrecoverable state :\/\\nRun \/start command again to restart.\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_message(42, 'Bot has entered unrecoverable state :/\\nRun /start command again to restart.', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None)" + "send_message(42, 'Bot has entered unrecoverable state :/\\nRun /start command again to restart.', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" ] } ] diff --git a/tests/script/core/test_message.py b/tests/script/core/test_message.py index 2b4c66601..a862c640a 100644 --- a/tests/script/core/test_message.py +++ b/tests/script/core/test_message.py @@ -43,6 +43,8 @@ def __eq__(self, value: object) -> bool: class DFFCLIMessengerInterface(CLIMessengerInterface): + supported_response_attachment_types = {Document} + def __init__(self, attachments_directory: Optional[Path] = None): MessengerInterface.__init__(self, attachments_directory) self._ctx_id: Optional[Hashable] = None diff --git a/tutorials/messengers/telegram/1_basic.py b/tutorials/messengers/telegram/1_basic.py index 6b7ac7c73..efb92748e 100644 --- a/tutorials/messengers/telegram/1_basic.py +++ b/tutorials/messengers/telegram/1_basic.py @@ -20,7 +20,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) from dff.script import conditions as cnd from dff.script import labels as lbl from dff.script import RESPONSE, TRANSITIONS, Message -from dff.messengers.telegram import PollingTelegramInterface +from dff.messengers.telegram import LongpollingTelegramInterface from dff.pipeline import Pipeline from dff.utils.testing.common import is_interactive_mode @@ -59,7 +59,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) # %% -interface = PollingTelegramInterface(token=os.environ["TG_BOT_TOKEN"]) +interface = LongpollingTelegramInterface(token=os.environ["TG_BOT_TOKEN"]) # %% diff --git a/tutorials/messengers/telegram/2_attachments.py b/tutorials/messengers/telegram/2_attachments.py index 05383b9b3..51e5f5642 100644 --- a/tutorials/messengers/telegram/2_attachments.py +++ b/tutorials/messengers/telegram/2_attachments.py @@ -21,7 +21,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) from dff.script import conditions as cnd from dff.script import GLOBAL, RESPONSE, TRANSITIONS, Message -from dff.messengers.telegram import PollingTelegramInterface +from dff.messengers.telegram import LongpollingTelegramInterface from dff.pipeline import Pipeline from dff.script.core.message import ( Animation, @@ -34,6 +34,8 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) PollOption, Sticker, Video, + VideoMessage, + VoiceMessage ) from dff.utils.testing.common import is_interactive_mode @@ -121,6 +123,12 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) ("main_flow", "document_node"): cnd.exact_match( Message("document") ), + ("main_flow", "voice_message_node"): cnd.exact_match( + Message("voice message") + ), + ("main_flow", "video_message_node"): cnd.exact_match( + Message("video message") + ), } }, "main_flow": { @@ -196,6 +204,18 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) attachments=[Document(**document_data)], ), }, + "voice_message_node": { + RESPONSE: Message( + "Here's your voice message!", + attachments=[VoiceMessage(source=audio_data["source"])], + ), + }, + "video_message_node": { + RESPONSE: Message( + "Here's your video message!", + attachments=[VideoMessage(source=video_data["source"])], + ), + }, "fallback_node": { RESPONSE: Message( "Unknown attachment type, try again! " @@ -209,7 +229,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) # %% -interface = PollingTelegramInterface(token=os.environ["TG_BOT_TOKEN"]) +interface = LongpollingTelegramInterface(token=os.environ["TG_BOT_TOKEN"]) # %% diff --git a/tutorials/messengers/telegram/3_advanced.py b/tutorials/messengers/telegram/3_advanced.py index 9e121bba5..3360476ff 100644 --- a/tutorials/messengers/telegram/3_advanced.py +++ b/tutorials/messengers/telegram/3_advanced.py @@ -24,7 +24,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) from dff.script import conditions as cnd from dff.script import RESPONSE, TRANSITIONS, Message -from dff.messengers.telegram import PollingTelegramInterface +from dff.messengers.telegram import LongpollingTelegramInterface from dff.pipeline import Pipeline from dff.script.core.context import Context from dff.script.core.keywords import GLOBAL @@ -218,7 +218,7 @@ async def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: # %% -interface = PollingTelegramInterface(token=os.environ["TG_BOT_TOKEN"]) +interface = LongpollingTelegramInterface(token=os.environ["TG_BOT_TOKEN"]) # %% From d74a2bc7809761be53655fc7c4fd4a04e65ea0f2 Mon Sep 17 00:00:00 2001 From: pseusys Date: Wed, 19 Jun 2024 03:21:54 +0200 Subject: [PATCH 100/140] telegram interfaces renamed --- dff/messengers/telegram/__init__.py | 2 +- dff/messengers/telegram/interface.py | 4 ++-- tutorials/messengers/telegram/1_basic.py | 4 ++-- tutorials/messengers/telegram/2_attachments.py | 4 ++-- tutorials/messengers/telegram/3_advanced.py | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/dff/messengers/telegram/__init__.py b/dff/messengers/telegram/__init__.py index 9f390d341..771e96332 100644 --- a/dff/messengers/telegram/__init__.py +++ b/dff/messengers/telegram/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- from .abstract import telegram_available -from .interface import LongpollingTelegramInterface, WebhookTelegramInterface +from .interface import LongpollingInterface, WebhookInterface diff --git a/dff/messengers/telegram/interface.py b/dff/messengers/telegram/interface.py index 57d698efb..81c82fb51 100644 --- a/dff/messengers/telegram/interface.py +++ b/dff/messengers/telegram/interface.py @@ -7,7 +7,7 @@ from .abstract import _AbstractTelegramInterface -class LongpollingTelegramInterface(_AbstractTelegramInterface): +class LongpollingInterface(_AbstractTelegramInterface): """ Telegram messenger interface, that requests Telegram API in a loop. @@ -31,7 +31,7 @@ async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs ) -class WebhookTelegramInterface(_AbstractTelegramInterface): +class WebhookInterface(_AbstractTelegramInterface): """ Telegram messenger interface, that brings a special webserver up and registers up for listening for Telegram updates. diff --git a/tutorials/messengers/telegram/1_basic.py b/tutorials/messengers/telegram/1_basic.py index efb92748e..e27ecfda9 100644 --- a/tutorials/messengers/telegram/1_basic.py +++ b/tutorials/messengers/telegram/1_basic.py @@ -20,7 +20,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) from dff.script import conditions as cnd from dff.script import labels as lbl from dff.script import RESPONSE, TRANSITIONS, Message -from dff.messengers.telegram import LongpollingTelegramInterface +from dff.messengers.telegram import LongpollingInterface from dff.pipeline import Pipeline from dff.utils.testing.common import is_interactive_mode @@ -59,7 +59,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) # %% -interface = LongpollingTelegramInterface(token=os.environ["TG_BOT_TOKEN"]) +interface = LongpollingInterface(token=os.environ["TG_BOT_TOKEN"]) # %% diff --git a/tutorials/messengers/telegram/2_attachments.py b/tutorials/messengers/telegram/2_attachments.py index 51e5f5642..378f40740 100644 --- a/tutorials/messengers/telegram/2_attachments.py +++ b/tutorials/messengers/telegram/2_attachments.py @@ -21,7 +21,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) from dff.script import conditions as cnd from dff.script import GLOBAL, RESPONSE, TRANSITIONS, Message -from dff.messengers.telegram import LongpollingTelegramInterface +from dff.messengers.telegram import LongpollingInterface from dff.pipeline import Pipeline from dff.script.core.message import ( Animation, @@ -229,7 +229,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) # %% -interface = LongpollingTelegramInterface(token=os.environ["TG_BOT_TOKEN"]) +interface = LongpollingInterface(token=os.environ["TG_BOT_TOKEN"]) # %% diff --git a/tutorials/messengers/telegram/3_advanced.py b/tutorials/messengers/telegram/3_advanced.py index 3360476ff..89d6969a0 100644 --- a/tutorials/messengers/telegram/3_advanced.py +++ b/tutorials/messengers/telegram/3_advanced.py @@ -24,7 +24,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) from dff.script import conditions as cnd from dff.script import RESPONSE, TRANSITIONS, Message -from dff.messengers.telegram import LongpollingTelegramInterface +from dff.messengers.telegram import LongpollingInterface from dff.pipeline import Pipeline from dff.script.core.context import Context from dff.script.core.keywords import GLOBAL @@ -218,7 +218,7 @@ async def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: # %% -interface = LongpollingTelegramInterface(token=os.environ["TG_BOT_TOKEN"]) +interface = LongpollingInterface(token=os.environ["TG_BOT_TOKEN"]) # %% From de12ca260e1a75fec0dc803db11dc8a338fd4018 Mon Sep 17 00:00:00 2001 From: pseusys Date: Wed, 19 Jun 2024 03:56:45 +0200 Subject: [PATCH 101/140] tutorial documentation introduced --- tests/messengers/__init__.py | 0 tests/messengers/telegram/__init__.py | 0 tutorials/messengers/telegram/1_basic.py | 9 +++++---- tutorials/messengers/telegram/2_attachments.py | 7 +++++-- tutorials/messengers/telegram/3_advanced.py | 6 ++++-- 5 files changed, 14 insertions(+), 8 deletions(-) create mode 100644 tests/messengers/__init__.py create mode 100644 tests/messengers/telegram/__init__.py diff --git a/tests/messengers/__init__.py b/tests/messengers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/messengers/telegram/__init__.py b/tests/messengers/telegram/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tutorials/messengers/telegram/1_basic.py b/tutorials/messengers/telegram/1_basic.py index e27ecfda9..efb7eb2a4 100644 --- a/tutorials/messengers/telegram/1_basic.py +++ b/tutorials/messengers/telegram/1_basic.py @@ -5,7 +5,7 @@ The following tutorial shows how to run a regular DFF script in Telegram. It asks users for the '/start' command and then loops in one place. -Here, %mddoclink(api,messengers.telegram.interface,PollingTelegramInterface) +Here, %mddoclink(api,messengers.telegram.interface,LongpollingInterface) class and [python-telegram-bot](https://docs.python-telegram-bot.org/) library are used for accessing telegram API in polling mode. @@ -28,10 +28,11 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) # %% [markdown] """ In order to integrate your script with Telegram, you need an instance of -`TelegramMessenger` class and one of the following interfaces: -`PollingMessengerInterface` or `CallbackTelegramInterface`. +`_AbstractTelegramMessenger` class and one of the following interfaces: +`LongpollingInterface` or `WebhookInterface`. +The latter requires a webserver, so here we use long polling interface. -`TelegramMessenger` encapsulates the bot logic. The` only required +`_AbstractTelegramMessenger` encapsulates the bot logic. The only required argument for a bot to run is a token. Some other parameters (such as host, port, interval, etc.) can be passed as keyword arguments. diff --git a/tutorials/messengers/telegram/2_attachments.py b/tutorials/messengers/telegram/2_attachments.py index 378f40740..2c802166f 100644 --- a/tutorials/messengers/telegram/2_attachments.py +++ b/tutorials/messengers/telegram/2_attachments.py @@ -5,7 +5,7 @@ The following tutorial shows how to send different attachments using telegram interfaces. -Here, %mddoclink(api,messengers.telegram.interface,PollingTelegramInterface) +Here, %mddoclink(api,messengers.telegram.interface,LongpollingInterface) class and [python-telegram-bot](https://docs.python-telegram-bot.org/) library are used for accessing telegram API in polling mode. @@ -45,7 +45,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) Example attachment data is specified below in form of dictionaries. List of attachments that telegram messenger interface can send can be found here: -%mddoclink(api,messengers.telegram.abstract,_AbstractTelegramInterface#response_attachments). +%mddoclink(api,messengers.telegram.abstract,_AbstractTelegramInterface#supported_request_attachment_types). """ # %% @@ -102,6 +102,9 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) # %% [markdown] """ The bot below sends different attachments on request. + +[Here](%doclink(api,script.core.message)) you can find +all the attachment options available. """ # %% diff --git a/tutorials/messengers/telegram/3_advanced.py b/tutorials/messengers/telegram/3_advanced.py index 89d6969a0..ce133786f 100644 --- a/tutorials/messengers/telegram/3_advanced.py +++ b/tutorials/messengers/telegram/3_advanced.py @@ -4,7 +4,7 @@ The following tutorial shows several advanced cases of user-to-bot interaction. -Here, %mddoclink(api,messengers.telegram.interface,PollingTelegramInterface) +Here, %mddoclink(api,messengers.telegram.interface,LongpollingInterface) class and [python-telegram-bot](https://docs.python-telegram-bot.org/) library are used for accessing telegram API in polling mode. @@ -14,7 +14,6 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) # %pip install dff[telegram] # %% -from asyncio import get_event_loop import os from urllib.request import urlopen @@ -50,6 +49,9 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) 5. Document with a thumbnail. 6. Attachment bytes hash. +Check out [this](https://docs.python-telegram-bot.org/en/v21.3/telegram.bot.html#telegram.Bot) +class for information about different arguments for sending attachments, `send_...` methods. + Last option ("Raw attachments!") button might be especially interesting, because it shows how bot precepts different telegram attachments sent by user in terms and datastructures of Dialog Flow Framework. From eeb1bf750d0d473d37d2a9903c9bf61940eb1d81 Mon Sep 17 00:00:00 2001 From: pseusys Date: Thu, 20 Jun 2024 01:33:30 +0200 Subject: [PATCH 102/140] lockfile updated --- poetry.lock | 2114 +++++++++++++++++++++++++++------------------------ 1 file changed, 1140 insertions(+), 974 deletions(-) diff --git a/poetry.lock b/poetry.lock index 97b626383..0c223de15 100644 --- a/poetry.lock +++ b/poetry.lock @@ -140,6 +140,17 @@ yarl = ">=1.0,<2.0" [package.extras] speedups = ["Brotli", "aiodns", "brotlicffi"] +[[package]] +name = "aiolimiter" +version = "1.1.0" +description = "asyncio rate limiter, a leaky bucket implementation" +optional = true +python-versions = ">=3.7,<4.0" +files = [ + {file = "aiolimiter-1.1.0-py3-none-any.whl", hash = "sha256:0b4997961fc58b8df40279e739f9cf0d3e255e63e9a44f64df567a8c17241e24"}, + {file = "aiolimiter-1.1.0.tar.gz", hash = "sha256:461cf02f82a29347340d031626c92853645c099cb5ff85577b831a7bd21132b5"}, +] + [[package]] name = "aiosignal" version = "1.3.1" @@ -210,13 +221,13 @@ doc = ["docutils", "jinja2", "myst-parser", "numpydoc", "pillow (>=9,<10)", "pyd [[package]] name = "annotated-types" -version = "0.6.0" +version = "0.7.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" files = [ - {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, - {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, ] [package.dependencies] @@ -234,13 +245,13 @@ files = [ [[package]] name = "anyio" -version = "4.3.0" +version = "4.4.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.8" files = [ - {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"}, - {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"}, + {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"}, + {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"}, ] [package.dependencies] @@ -265,6 +276,34 @@ files = [ {file = "appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee"}, ] +[[package]] +name = "apscheduler" +version = "3.10.4" +description = "In-process task scheduler with Cron-like capabilities" +optional = true +python-versions = ">=3.6" +files = [ + {file = "APScheduler-3.10.4-py3-none-any.whl", hash = "sha256:fb91e8a768632a4756a585f79ec834e0e27aad5860bac7eaa523d9ccefd87661"}, + {file = "APScheduler-3.10.4.tar.gz", hash = "sha256:e6df071b27d9be898e486bc7940a7be50b4af2e9da7c08f0744a96d4bd4cef4a"}, +] + +[package.dependencies] +pytz = "*" +six = ">=1.4.0" +tzlocal = ">=2.0,<3.dev0 || >=4.dev0" + +[package.extras] +doc = ["sphinx", "sphinx-rtd-theme"] +gevent = ["gevent"] +mongodb = ["pymongo (>=3.0)"] +redis = ["redis (>=3.0)"] +rethinkdb = ["rethinkdb (>=2.4.0)"] +sqlalchemy = ["sqlalchemy (>=1.4)"] +testing = ["pytest", "pytest-asyncio", "pytest-cov", "pytest-tornado5"] +tornado = ["tornado (>=4.3)"] +twisted = ["twisted"] +zookeeper = ["kazoo"] + [[package]] name = "argon2-cffi" version = "23.1.0" @@ -571,6 +610,34 @@ files = [ {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, ] +[[package]] +name = "backports-zoneinfo" +version = "0.2.1" +description = "Backport of the standard library zoneinfo module" +optional = true +python-versions = ">=3.6" +files = [ + {file = "backports.zoneinfo-0.2.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:da6013fd84a690242c310d77ddb8441a559e9cb3d3d59ebac9aca1a57b2e18bc"}, + {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:89a48c0d158a3cc3f654da4c2de1ceba85263fafb861b98b59040a5086259722"}, + {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:1c5742112073a563c81f786e77514969acb58649bcdf6cdf0b4ed31a348d4546"}, + {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win32.whl", hash = "sha256:e8236383a20872c0cdf5a62b554b27538db7fa1bbec52429d8d106effbaeca08"}, + {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:8439c030a11780786a2002261569bdf362264f605dfa4d65090b64b05c9f79a7"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:f04e857b59d9d1ccc39ce2da1021d196e47234873820cbeaad210724b1ee28ac"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:17746bd546106fa389c51dbea67c8b7c8f0d14b5526a579ca6ccf5ed72c526cf"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5c144945a7752ca544b4b78c8c41544cdfaf9786f25fe5ffb10e838e19a27570"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win32.whl", hash = "sha256:e55b384612d93be96506932a786bbcde5a2db7a9e6a4bb4bffe8b733f5b9036b"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a76b38c52400b762e48131494ba26be363491ac4f9a04c1b7e92483d169f6582"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:8961c0f32cd0336fb8e8ead11a1f8cd99ec07145ec2931122faaac1c8f7fd987"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e81b76cace8eda1fca50e345242ba977f9be6ae3945af8d46326d776b4cf78d1"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7b0a64cda4145548fed9efc10322770f929b944ce5cee6c0dfe0c87bf4c0c8c9"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-win32.whl", hash = "sha256:1b13e654a55cd45672cb54ed12148cd33628f672548f373963b0bff67b217328"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:4a0f800587060bf8880f954dbef70de6c11bbe59c673c3d818921f042f9954a6"}, + {file = "backports.zoneinfo-0.2.1.tar.gz", hash = "sha256:fadbfe37f74051d024037f223b8e001611eac868b5c5b06144ef4d8b799862f2"}, +] + +[package.extras] +tzdata = ["tzdata"] + [[package]] name = "beautifulsoup4" version = "4.12.3" @@ -818,13 +885,13 @@ files = [ [[package]] name = "certifi" -version = "2024.2.2" +version = "2024.6.2" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, - {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, + {file = "certifi-2024.6.2-py3-none-any.whl", hash = "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56"}, + {file = "certifi-2024.6.2.tar.gz", hash = "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516"}, ] [[package]] @@ -1075,63 +1142,63 @@ files = [ [[package]] name = "coverage" -version = "7.5.1" +version = "7.5.3" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0884920835a033b78d1c73b6d3bbcda8161a900f38a488829a83982925f6c2e"}, - {file = "coverage-7.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:39afcd3d4339329c5f58de48a52f6e4e50f6578dd6099961cf22228feb25f38f"}, - {file = "coverage-7.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b0ceee8147444347da6a66be737c9d78f3353b0681715b668b72e79203e4a"}, - {file = "coverage-7.5.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a9ca3f2fae0088c3c71d743d85404cec8df9be818a005ea065495bedc33da35"}, - {file = "coverage-7.5.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd215c0c7d7aab005221608a3c2b46f58c0285a819565887ee0b718c052aa4e"}, - {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4bf0655ab60d754491004a5efd7f9cccefcc1081a74c9ef2da4735d6ee4a6223"}, - {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:61c4bf1ba021817de12b813338c9be9f0ad5b1e781b9b340a6d29fc13e7c1b5e"}, - {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:db66fc317a046556a96b453a58eced5024af4582a8dbdc0c23ca4dbc0d5b3146"}, - {file = "coverage-7.5.1-cp310-cp310-win32.whl", hash = "sha256:b016ea6b959d3b9556cb401c55a37547135a587db0115635a443b2ce8f1c7228"}, - {file = "coverage-7.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:df4e745a81c110e7446b1cc8131bf986157770fa405fe90e15e850aaf7619bc8"}, - {file = "coverage-7.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:796a79f63eca8814ca3317a1ea443645c9ff0d18b188de470ed7ccd45ae79428"}, - {file = "coverage-7.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fc84a37bfd98db31beae3c2748811a3fa72bf2007ff7902f68746d9757f3746"}, - {file = "coverage-7.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6175d1a0559986c6ee3f7fccfc4a90ecd12ba0a383dcc2da30c2b9918d67d8a3"}, - {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fc81d5878cd6274ce971e0a3a18a8803c3fe25457165314271cf78e3aae3aa2"}, - {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:556cf1a7cbc8028cb60e1ff0be806be2eded2daf8129b8811c63e2b9a6c43bca"}, - {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9981706d300c18d8b220995ad22627647be11a4276721c10911e0e9fa44c83e8"}, - {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d7fed867ee50edf1a0b4a11e8e5d0895150e572af1cd6d315d557758bfa9c057"}, - {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef48e2707fb320c8f139424a596f5b69955a85b178f15af261bab871873bb987"}, - {file = "coverage-7.5.1-cp311-cp311-win32.whl", hash = "sha256:9314d5678dcc665330df5b69c1e726a0e49b27df0461c08ca12674bcc19ef136"}, - {file = "coverage-7.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:5fa567e99765fe98f4e7d7394ce623e794d7cabb170f2ca2ac5a4174437e90dd"}, - {file = "coverage-7.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b6cf3764c030e5338e7f61f95bd21147963cf6aa16e09d2f74f1fa52013c1206"}, - {file = "coverage-7.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ec92012fefebee89a6b9c79bc39051a6cb3891d562b9270ab10ecfdadbc0c34"}, - {file = "coverage-7.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16db7f26000a07efcf6aea00316f6ac57e7d9a96501e990a36f40c965ec7a95d"}, - {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beccf7b8a10b09c4ae543582c1319c6df47d78fd732f854ac68d518ee1fb97fa"}, - {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8748731ad392d736cc9ccac03c9845b13bb07d020a33423fa5b3a36521ac6e4e"}, - {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7352b9161b33fd0b643ccd1f21f3a3908daaddf414f1c6cb9d3a2fd618bf2572"}, - {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7a588d39e0925f6a2bff87154752481273cdb1736270642aeb3635cb9b4cad07"}, - {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:68f962d9b72ce69ea8621f57551b2fa9c70509af757ee3b8105d4f51b92b41a7"}, - {file = "coverage-7.5.1-cp312-cp312-win32.whl", hash = "sha256:f152cbf5b88aaeb836127d920dd0f5e7edff5a66f10c079157306c4343d86c19"}, - {file = "coverage-7.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:5a5740d1fb60ddf268a3811bcd353de34eb56dc24e8f52a7f05ee513b2d4f596"}, - {file = "coverage-7.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e2213def81a50519d7cc56ed643c9e93e0247f5bbe0d1247d15fa520814a7cd7"}, - {file = "coverage-7.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5037f8fcc2a95b1f0e80585bd9d1ec31068a9bcb157d9750a172836e98bc7a90"}, - {file = "coverage-7.5.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3721c2c9e4c4953a41a26c14f4cef64330392a6d2d675c8b1db3b645e31f0e"}, - {file = "coverage-7.5.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca498687ca46a62ae590253fba634a1fe9836bc56f626852fb2720f334c9e4e5"}, - {file = "coverage-7.5.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cdcbc320b14c3e5877ee79e649677cb7d89ef588852e9583e6b24c2e5072661"}, - {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:57e0204b5b745594e5bc14b9b50006da722827f0b8c776949f1135677e88d0b8"}, - {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fe7502616b67b234482c3ce276ff26f39ffe88adca2acf0261df4b8454668b4"}, - {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9e78295f4144f9dacfed4f92935fbe1780021247c2fabf73a819b17f0ccfff8d"}, - {file = "coverage-7.5.1-cp38-cp38-win32.whl", hash = "sha256:1434e088b41594baa71188a17533083eabf5609e8e72f16ce8c186001e6b8c41"}, - {file = "coverage-7.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:0646599e9b139988b63704d704af8e8df7fa4cbc4a1f33df69d97f36cb0a38de"}, - {file = "coverage-7.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4cc37def103a2725bc672f84bd939a6fe4522310503207aae4d56351644682f1"}, - {file = "coverage-7.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fc0b4d8bfeabd25ea75e94632f5b6e047eef8adaed0c2161ada1e922e7f7cece"}, - {file = "coverage-7.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d0a0f5e06881ecedfe6f3dd2f56dcb057b6dbeb3327fd32d4b12854df36bf26"}, - {file = "coverage-7.5.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9735317685ba6ec7e3754798c8871c2f49aa5e687cc794a0b1d284b2389d1bd5"}, - {file = "coverage-7.5.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d21918e9ef11edf36764b93101e2ae8cc82aa5efdc7c5a4e9c6c35a48496d601"}, - {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c3e757949f268364b96ca894b4c342b41dc6f8f8b66c37878aacef5930db61be"}, - {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:79afb6197e2f7f60c4824dd4b2d4c2ec5801ceb6ba9ce5d2c3080e5660d51a4f"}, - {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d1d0d98d95dd18fe29dc66808e1accf59f037d5716f86a501fc0256455219668"}, - {file = "coverage-7.5.1-cp39-cp39-win32.whl", hash = "sha256:1cc0fe9b0b3a8364093c53b0b4c0c2dd4bb23acbec4c9240b5f284095ccf7981"}, - {file = "coverage-7.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:dde0070c40ea8bb3641e811c1cfbf18e265d024deff6de52c5950677a8fb1e0f"}, - {file = "coverage-7.5.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:6537e7c10cc47c595828b8a8be04c72144725c383c4702703ff4e42e44577312"}, - {file = "coverage-7.5.1.tar.gz", hash = "sha256:54de9ef3a9da981f7af93eafde4ede199e0846cd819eb27c88e2b712aae9708c"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"}, + {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"}, + {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"}, + {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"}, + {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"}, + {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"}, + {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0"}, + {file = "coverage-7.5.3-cp38-cp38-win32.whl", hash = "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485"}, + {file = "coverage-7.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd"}, + {file = "coverage-7.5.3-cp39-cp39-win32.whl", hash = "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d"}, + {file = "coverage-7.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0"}, + {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"}, + {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"}, ] [package.dependencies] @@ -1153,43 +1220,43 @@ files = [ [[package]] name = "cryptography" -version = "42.0.7" +version = "42.0.8" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" files = [ - {file = "cryptography-42.0.7-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a987f840718078212fdf4504d0fd4c6effe34a7e4740378e59d47696e8dfb477"}, - {file = "cryptography-42.0.7-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bd13b5e9b543532453de08bcdc3cc7cebec6f9883e886fd20a92f26940fd3e7a"}, - {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a79165431551042cc9d1d90e6145d5d0d3ab0f2d66326c201d9b0e7f5bf43604"}, - {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a47787a5e3649008a1102d3df55424e86606c9bae6fb77ac59afe06d234605f8"}, - {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:02c0eee2d7133bdbbc5e24441258d5d2244beb31da5ed19fbb80315f4bbbff55"}, - {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5e44507bf8d14b36b8389b226665d597bc0f18ea035d75b4e53c7b1ea84583cc"}, - {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7f8b25fa616d8b846aef64b15c606bb0828dbc35faf90566eb139aa9cff67af2"}, - {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:93a3209f6bb2b33e725ed08ee0991b92976dfdcf4e8b38646540674fc7508e13"}, - {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e6b8f1881dac458c34778d0a424ae5769de30544fc678eac51c1c8bb2183e9da"}, - {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3de9a45d3b2b7d8088c3fbf1ed4395dfeff79d07842217b38df14ef09ce1d8d7"}, - {file = "cryptography-42.0.7-cp37-abi3-win32.whl", hash = "sha256:789caea816c6704f63f6241a519bfa347f72fbd67ba28d04636b7c6b7da94b0b"}, - {file = "cryptography-42.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:8cb8ce7c3347fcf9446f201dc30e2d5a3c898d009126010cbd1f443f28b52678"}, - {file = "cryptography-42.0.7-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:a3a5ac8b56fe37f3125e5b72b61dcde43283e5370827f5233893d461b7360cd4"}, - {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:779245e13b9a6638df14641d029add5dc17edbef6ec915688f3acb9e720a5858"}, - {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d563795db98b4cd57742a78a288cdbdc9daedac29f2239793071fe114f13785"}, - {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:31adb7d06fe4383226c3e963471f6837742889b3c4caa55aac20ad951bc8ffda"}, - {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:efd0bf5205240182e0f13bcaea41be4fdf5c22c5129fc7ced4a0282ac86998c9"}, - {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a9bc127cdc4ecf87a5ea22a2556cab6c7eda2923f84e4f3cc588e8470ce4e42e"}, - {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:3577d029bc3f4827dd5bf8bf7710cac13527b470bbf1820a3f394adb38ed7d5f"}, - {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2e47577f9b18723fa294b0ea9a17d5e53a227867a0a4904a1a076d1646d45ca1"}, - {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1a58839984d9cb34c855197043eaae2c187d930ca6d644612843b4fe8513c886"}, - {file = "cryptography-42.0.7-cp39-abi3-win32.whl", hash = "sha256:e6b79d0adb01aae87e8a44c2b64bc3f3fe59515280e00fb6d57a7267a2583cda"}, - {file = "cryptography-42.0.7-cp39-abi3-win_amd64.whl", hash = "sha256:16268d46086bb8ad5bf0a2b5544d8a9ed87a0e33f5e77dd3c3301e63d941a83b"}, - {file = "cryptography-42.0.7-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2954fccea107026512b15afb4aa664a5640cd0af630e2ee3962f2602693f0c82"}, - {file = "cryptography-42.0.7-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:362e7197754c231797ec45ee081f3088a27a47c6c01eff2ac83f60f85a50fe60"}, - {file = "cryptography-42.0.7-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4f698edacf9c9e0371112792558d2f705b5645076cc0aaae02f816a0171770fd"}, - {file = "cryptography-42.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5482e789294854c28237bba77c4c83be698be740e31a3ae5e879ee5444166582"}, - {file = "cryptography-42.0.7-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e9b2a6309f14c0497f348d08a065d52f3020656f675819fc405fb63bbcd26562"}, - {file = "cryptography-42.0.7-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d8e3098721b84392ee45af2dd554c947c32cc52f862b6a3ae982dbb90f577f14"}, - {file = "cryptography-42.0.7-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c65f96dad14f8528a447414125e1fc8feb2ad5a272b8f68477abbcc1ea7d94b9"}, - {file = "cryptography-42.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:36017400817987670037fbb0324d71489b6ead6231c9604f8fc1f7d008087c68"}, - {file = "cryptography-42.0.7.tar.gz", hash = "sha256:ecbfbc00bf55888edda9868a4cf927205de8499e7fabe6c050322298382953f2"}, + {file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:81d8a521705787afe7a18d5bfb47ea9d9cc068206270aad0b96a725022e18d2e"}, + {file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:961e61cefdcb06e0c6d7e3a1b22ebe8b996eb2bf50614e89384be54c48c6b63d"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3ec3672626e1b9e55afd0df6d774ff0e953452886e06e0f1eb7eb0c832e8902"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e599b53fd95357d92304510fb7bda8523ed1f79ca98dce2f43c115950aa78801"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5226d5d21ab681f432a9c1cf8b658c0cb02533eece706b155e5fbd8a0cdd3949"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6b7c4f03ce01afd3b76cf69a5455caa9cfa3de8c8f493e0d3ab7d20611c8dae9"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:2346b911eb349ab547076f47f2e035fc8ff2c02380a7cbbf8d87114fa0f1c583"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ad803773e9df0b92e0a817d22fd8a3675493f690b96130a5e24f1b8fabbea9c7"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2f66d9cd9147ee495a8374a45ca445819f8929a3efcd2e3df6428e46c3cbb10b"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d45b940883a03e19e944456a558b67a41160e367a719833c53de6911cabba2b7"}, + {file = "cryptography-42.0.8-cp37-abi3-win32.whl", hash = "sha256:a0c5b2b0585b6af82d7e385f55a8bc568abff8923af147ee3c07bd8b42cda8b2"}, + {file = "cryptography-42.0.8-cp37-abi3-win_amd64.whl", hash = "sha256:57080dee41209e556a9a4ce60d229244f7a66ef52750f813bfbe18959770cfba"}, + {file = "cryptography-42.0.8-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:dea567d1b0e8bc5764b9443858b673b734100c2871dc93163f58c46a97a83d28"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4783183f7cb757b73b2ae9aed6599b96338eb957233c58ca8f49a49cc32fd5e"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0608251135d0e03111152e41f0cc2392d1e74e35703960d4190b2e0f4ca9c70"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dc0fdf6787f37b1c6b08e6dfc892d9d068b5bdb671198c72072828b80bd5fe4c"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9c0c1716c8447ee7dbf08d6db2e5c41c688544c61074b54fc4564196f55c25a7"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fff12c88a672ab9c9c1cf7b0c80e3ad9e2ebd9d828d955c126be4fd3e5578c9e"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:cafb92b2bc622cd1aa6a1dce4b93307792633f4c5fe1f46c6b97cf67073ec961"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:31f721658a29331f895a5a54e7e82075554ccfb8b163a18719d342f5ffe5ecb1"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b297f90c5723d04bcc8265fc2a0f86d4ea2e0f7ab4b6994459548d3a6b992a14"}, + {file = "cryptography-42.0.8-cp39-abi3-win32.whl", hash = "sha256:2f88d197e66c65be5e42cd72e5c18afbfae3f741742070e3019ac8f4ac57262c"}, + {file = "cryptography-42.0.8-cp39-abi3-win_amd64.whl", hash = "sha256:fa76fbb7596cc5839320000cdd5d0955313696d9511debab7ee7278fc8b5c84a"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ba4f0a211697362e89ad822e667d8d340b4d8d55fae72cdd619389fb5912eefe"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:81884c4d096c272f00aeb1f11cf62ccd39763581645b0812e99a91505fa48e0c"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c9bb2ae11bfbab395bdd072985abde58ea9860ed84e59dbc0463a5d0159f5b71"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7016f837e15b0a1c119d27ecd89b3515f01f90a8615ed5e9427e30d9cdbfed3d"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5a94eccb2a81a309806027e1670a358b99b8fe8bfe9f8d329f27d72c094dde8c"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dec9b018df185f08483f294cae6ccac29e7a6e0678996587363dc352dc65c842"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:343728aac38decfdeecf55ecab3264b015be68fc2816ca800db649607aeee648"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:013629ae70b40af70c9a7a5db40abe5d9054e6f4380e50ce769947b73bf3caad"}, + {file = "cryptography-42.0.8.tar.gz", hash = "sha256:8d09d05439ce7baa8e9e95b07ec5b6c886f548deb7e0f69ef25f64b3bce842f2"}, ] [package.dependencies] @@ -1406,13 +1473,13 @@ pgp = ["gpg"] [[package]] name = "email-validator" -version = "2.1.1" +version = "2.1.2" description = "A robust email address syntax and deliverability validation library." optional = false python-versions = ">=3.8" files = [ - {file = "email_validator-2.1.1-py3-none-any.whl", hash = "sha256:97d882d174e2a65732fb43bfce81a3a834cbc1bde8bf419e30ef5ea976370a05"}, - {file = "email_validator-2.1.1.tar.gz", hash = "sha256:200a70680ba08904be6d1eef729205cc0d687634399a5924d842533efb824b84"}, + {file = "email_validator-2.1.2-py3-none-any.whl", hash = "sha256:d89f6324e13b1e39889eab7f9ca2f91dc9aebb6fa50a6d8bd4329ab50f251115"}, + {file = "email_validator-2.1.2.tar.gz", hash = "sha256:14c0f3d343c4beda37400421b39fa411bbe33a75df20825df73ad53e06a9f04c"}, ] [package.dependencies] @@ -1490,29 +1557,30 @@ all = ["email_validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)" [[package]] name = "fastapi-cli" -version = "0.0.3" +version = "0.0.4" description = "Run and manage FastAPI apps from the command line with FastAPI CLI. 🚀" optional = false python-versions = ">=3.8" files = [ - {file = "fastapi_cli-0.0.3-py3-none-any.whl", hash = "sha256:ae233115f729945479044917d949095e829d2d84f56f55ce1ca17627872825a5"}, - {file = "fastapi_cli-0.0.3.tar.gz", hash = "sha256:3b6e4d2c4daee940fb8db59ebbfd60a72c4b962bcf593e263e4cc69da4ea3d7f"}, + {file = "fastapi_cli-0.0.4-py3-none-any.whl", hash = "sha256:a2552f3a7ae64058cdbb530be6fa6dbfc975dc165e4fa66d224c3d396e25e809"}, + {file = "fastapi_cli-0.0.4.tar.gz", hash = "sha256:e2e9ffaffc1f7767f488d6da34b6f5a377751c996f397902eb6abb99a67bde32"}, ] [package.dependencies] -fastapi = "*" typer = ">=0.12.3" -uvicorn = {version = ">=0.15.0", extras = ["standard"]} + +[package.extras] +standard = ["fastapi", "uvicorn[standard] (>=0.15.0)"] [[package]] name = "fastjsonschema" -version = "2.19.1" +version = "2.20.0" description = "Fastest Python implementation of JSON schema" optional = false python-versions = "*" files = [ - {file = "fastjsonschema-2.19.1-py3-none-any.whl", hash = "sha256:3672b47bc94178c9f23dbb654bf47440155d4db9df5f7bc47643315f9c405cd0"}, - {file = "fastjsonschema-2.19.1.tar.gz", hash = "sha256:e3126a94bdc4623d3de4485f8d468a12f02a67921315ddc87836d6e456dc789d"}, + {file = "fastjsonschema-2.20.0-py3-none-any.whl", hash = "sha256:5875f0b0fa7a0043a91e93a9b8f793bcbbba9691e7fd83dca95c28ba26d21f0a"}, + {file = "fastjsonschema-2.20.0.tar.gz", hash = "sha256:3d48fc5300ee96f5d116f10fe6f28d938e6008f59a6a025c2649475b87f76a23"}, ] [package.extras] @@ -1520,34 +1588,34 @@ devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benc [[package]] name = "filelock" -version = "3.14.0" +version = "3.15.3" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.14.0-py3-none-any.whl", hash = "sha256:43339835842f110ca7ae60f1e1c160714c5a6afd15a2873419ab185334975c0f"}, - {file = "filelock-3.14.0.tar.gz", hash = "sha256:6ea72da3be9b8c82afd3edcf99f2fffbb5076335a5ae4d03248bb5b6c3eae78a"}, + {file = "filelock-3.15.3-py3-none-any.whl", hash = "sha256:0151273e5b5d6cf753a61ec83b3a9b7d8821c39ae9af9d7ecf2f9e2f17404103"}, + {file = "filelock-3.15.3.tar.gz", hash = "sha256:e1199bf5194a2277273dacd50269f0d87d0682088a3c561c15674ea9005d8635"}, ] [package.extras] docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)", "virtualenv (>=20.26.2)"] typing = ["typing-extensions (>=4.8)"] [[package]] name = "flake8" -version = "7.0.0" +version = "7.1.0" description = "the modular source code checker: pep8 pyflakes and co" optional = false python-versions = ">=3.8.1" files = [ - {file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"}, - {file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"}, + {file = "flake8-7.1.0-py2.py3-none-any.whl", hash = "sha256:2e416edcc62471a64cea09353f4e7bdba32aeb079b6e360554c659a122b1bc6a"}, + {file = "flake8-7.1.0.tar.gz", hash = "sha256:48a07b626b55236e0fb4784ee69a465fbf59d79eec1f5b4785c3d3bc57d17aa5"}, ] [package.dependencies] mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.11.0,<2.12.0" +pycodestyle = ">=2.12.0,<2.13.0" pyflakes = ">=3.2.0,<3.3.0" [[package]] @@ -1753,8 +1821,8 @@ files = [ [package.dependencies] cffi = {version = ">=1.12.2", markers = "platform_python_implementation == \"CPython\" and sys_platform == \"win32\""} greenlet = [ - {version = ">=2.0.0", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""}, {version = ">=3.0rc3", markers = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\""}, + {version = ">=2.0.0", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""}, ] "zope.event" = "*" "zope.interface" = "*" @@ -1923,17 +1991,17 @@ test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", [[package]] name = "googleapis-common-protos" -version = "1.63.0" +version = "1.63.1" description = "Common protobufs used in Google APIs" optional = true python-versions = ">=3.7" files = [ - {file = "googleapis-common-protos-1.63.0.tar.gz", hash = "sha256:17ad01b11d5f1d0171c06d3ba5c04c54474e883b66b949722b4938ee2694ef4e"}, - {file = "googleapis_common_protos-1.63.0-py2.py3-none-any.whl", hash = "sha256:ae45f75702f7c08b541f750854a678bd8f534a1a6bace6afe975f1d0a82d6632"}, + {file = "googleapis-common-protos-1.63.1.tar.gz", hash = "sha256:c6442f7a0a6b2a80369457d79e6672bb7dcbaab88e0848302497e3ec80780a6a"}, + {file = "googleapis_common_protos-1.63.1-py2.py3-none-any.whl", hash = "sha256:0e1c2cdfcbc354b76e4a211a35ea35d6926a835cba1377073c4861db904a1877"}, ] [package.dependencies] -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0.dev0" [package.extras] grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] @@ -2011,61 +2079,61 @@ test = ["objgraph", "psutil"] [[package]] name = "grpcio" -version = "1.63.0" +version = "1.64.1" description = "HTTP/2-based RPC framework" optional = true python-versions = ">=3.8" files = [ - {file = "grpcio-1.63.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:2e93aca840c29d4ab5db93f94ed0a0ca899e241f2e8aec6334ab3575dc46125c"}, - {file = "grpcio-1.63.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:91b73d3f1340fefa1e1716c8c1ec9930c676d6b10a3513ab6c26004cb02d8b3f"}, - {file = "grpcio-1.63.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:b3afbd9d6827fa6f475a4f91db55e441113f6d3eb9b7ebb8fb806e5bb6d6bd0d"}, - {file = "grpcio-1.63.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f3f6883ce54a7a5f47db43289a0a4c776487912de1a0e2cc83fdaec9685cc9f"}, - {file = "grpcio-1.63.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf8dae9cc0412cb86c8de5a8f3be395c5119a370f3ce2e69c8b7d46bb9872c8d"}, - {file = "grpcio-1.63.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:08e1559fd3b3b4468486b26b0af64a3904a8dbc78d8d936af9c1cf9636eb3e8b"}, - {file = "grpcio-1.63.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5c039ef01516039fa39da8a8a43a95b64e288f79f42a17e6c2904a02a319b357"}, - {file = "grpcio-1.63.0-cp310-cp310-win32.whl", hash = "sha256:ad2ac8903b2eae071055a927ef74121ed52d69468e91d9bcbd028bd0e554be6d"}, - {file = "grpcio-1.63.0-cp310-cp310-win_amd64.whl", hash = "sha256:b2e44f59316716532a993ca2966636df6fbe7be4ab6f099de6815570ebe4383a"}, - {file = "grpcio-1.63.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:f28f8b2db7b86c77916829d64ab21ff49a9d8289ea1564a2b2a3a8ed9ffcccd3"}, - {file = "grpcio-1.63.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:65bf975639a1f93bee63ca60d2e4951f1b543f498d581869922910a476ead2f5"}, - {file = "grpcio-1.63.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:b5194775fec7dc3dbd6a935102bb156cd2c35efe1685b0a46c67b927c74f0cfb"}, - {file = "grpcio-1.63.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4cbb2100ee46d024c45920d16e888ee5d3cf47c66e316210bc236d5bebc42b3"}, - {file = "grpcio-1.63.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ff737cf29b5b801619f10e59b581869e32f400159e8b12d7a97e7e3bdeee6a2"}, - {file = "grpcio-1.63.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cd1e68776262dd44dedd7381b1a0ad09d9930ffb405f737d64f505eb7f77d6c7"}, - {file = "grpcio-1.63.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:93f45f27f516548e23e4ec3fbab21b060416007dbe768a111fc4611464cc773f"}, - {file = "grpcio-1.63.0-cp311-cp311-win32.whl", hash = "sha256:878b1d88d0137df60e6b09b74cdb73db123f9579232c8456f53e9abc4f62eb3c"}, - {file = "grpcio-1.63.0-cp311-cp311-win_amd64.whl", hash = "sha256:756fed02dacd24e8f488f295a913f250b56b98fb793f41d5b2de6c44fb762434"}, - {file = "grpcio-1.63.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:93a46794cc96c3a674cdfb59ef9ce84d46185fe9421baf2268ccb556f8f81f57"}, - {file = "grpcio-1.63.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a7b19dfc74d0be7032ca1eda0ed545e582ee46cd65c162f9e9fc6b26ef827dc6"}, - {file = "grpcio-1.63.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:8064d986d3a64ba21e498b9a376cbc5d6ab2e8ab0e288d39f266f0fca169b90d"}, - {file = "grpcio-1.63.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:219bb1848cd2c90348c79ed0a6b0ea51866bc7e72fa6e205e459fedab5770172"}, - {file = "grpcio-1.63.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2d60cd1d58817bc5985fae6168d8b5655c4981d448d0f5b6194bbcc038090d2"}, - {file = "grpcio-1.63.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9e350cb096e5c67832e9b6e018cf8a0d2a53b2a958f6251615173165269a91b0"}, - {file = "grpcio-1.63.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:56cdf96ff82e3cc90dbe8bac260352993f23e8e256e063c327b6cf9c88daf7a9"}, - {file = "grpcio-1.63.0-cp312-cp312-win32.whl", hash = "sha256:3a6d1f9ea965e750db7b4ee6f9fdef5fdf135abe8a249e75d84b0a3e0c668a1b"}, - {file = "grpcio-1.63.0-cp312-cp312-win_amd64.whl", hash = "sha256:d2497769895bb03efe3187fb1888fc20e98a5f18b3d14b606167dacda5789434"}, - {file = "grpcio-1.63.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:fdf348ae69c6ff484402cfdb14e18c1b0054ac2420079d575c53a60b9b2853ae"}, - {file = "grpcio-1.63.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a3abfe0b0f6798dedd2e9e92e881d9acd0fdb62ae27dcbbfa7654a57e24060c0"}, - {file = "grpcio-1.63.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:6ef0ad92873672a2a3767cb827b64741c363ebaa27e7f21659e4e31f4d750280"}, - {file = "grpcio-1.63.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b416252ac5588d9dfb8a30a191451adbf534e9ce5f56bb02cd193f12d8845b7f"}, - {file = "grpcio-1.63.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3b77eaefc74d7eb861d3ffbdf91b50a1bb1639514ebe764c47773b833fa2d91"}, - {file = "grpcio-1.63.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b005292369d9c1f80bf70c1db1c17c6c342da7576f1c689e8eee4fb0c256af85"}, - {file = "grpcio-1.63.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cdcda1156dcc41e042d1e899ba1f5c2e9f3cd7625b3d6ebfa619806a4c1aadda"}, - {file = "grpcio-1.63.0-cp38-cp38-win32.whl", hash = "sha256:01799e8649f9e94ba7db1aeb3452188048b0019dc37696b0f5ce212c87c560c3"}, - {file = "grpcio-1.63.0-cp38-cp38-win_amd64.whl", hash = "sha256:6a1a3642d76f887aa4009d92f71eb37809abceb3b7b5a1eec9c554a246f20e3a"}, - {file = "grpcio-1.63.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:75f701ff645858a2b16bc8c9fc68af215a8bb2d5a9b647448129de6e85d52bce"}, - {file = "grpcio-1.63.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cacdef0348a08e475a721967f48206a2254a1b26ee7637638d9e081761a5ba86"}, - {file = "grpcio-1.63.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:0697563d1d84d6985e40ec5ec596ff41b52abb3fd91ec240e8cb44a63b895094"}, - {file = "grpcio-1.63.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6426e1fb92d006e47476d42b8f240c1d916a6d4423c5258ccc5b105e43438f61"}, - {file = "grpcio-1.63.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e48cee31bc5f5a31fb2f3b573764bd563aaa5472342860edcc7039525b53e46a"}, - {file = "grpcio-1.63.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:50344663068041b34a992c19c600236e7abb42d6ec32567916b87b4c8b8833b3"}, - {file = "grpcio-1.63.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:259e11932230d70ef24a21b9fb5bb947eb4703f57865a404054400ee92f42f5d"}, - {file = "grpcio-1.63.0-cp39-cp39-win32.whl", hash = "sha256:a44624aad77bf8ca198c55af811fd28f2b3eaf0a50ec5b57b06c034416ef2d0a"}, - {file = "grpcio-1.63.0-cp39-cp39-win_amd64.whl", hash = "sha256:166e5c460e5d7d4656ff9e63b13e1f6029b122104c1633d5f37eaea348d7356d"}, - {file = "grpcio-1.63.0.tar.gz", hash = "sha256:f3023e14805c61bc439fb40ca545ac3d5740ce66120a678a3c6c2c55b70343d1"}, -] - -[package.extras] -protobuf = ["grpcio-tools (>=1.63.0)"] + {file = "grpcio-1.64.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:55697ecec192bc3f2f3cc13a295ab670f51de29884ca9ae6cd6247df55df2502"}, + {file = "grpcio-1.64.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:3b64ae304c175671efdaa7ec9ae2cc36996b681eb63ca39c464958396697daff"}, + {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:bac71b4b28bc9af61efcdc7630b166440bbfbaa80940c9a697271b5e1dabbc61"}, + {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c024ffc22d6dc59000faf8ad781696d81e8e38f4078cb0f2630b4a3cf231a90"}, + {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7cd5c1325f6808b8ae31657d281aadb2a51ac11ab081ae335f4f7fc44c1721d"}, + {file = "grpcio-1.64.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0a2813093ddb27418a4c99f9b1c223fab0b053157176a64cc9db0f4557b69bd9"}, + {file = "grpcio-1.64.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2981c7365a9353f9b5c864595c510c983251b1ab403e05b1ccc70a3d9541a73b"}, + {file = "grpcio-1.64.1-cp310-cp310-win32.whl", hash = "sha256:1262402af5a511c245c3ae918167eca57342c72320dffae5d9b51840c4b2f86d"}, + {file = "grpcio-1.64.1-cp310-cp310-win_amd64.whl", hash = "sha256:19264fc964576ddb065368cae953f8d0514ecc6cb3da8903766d9fb9d4554c33"}, + {file = "grpcio-1.64.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:58b1041e7c870bb30ee41d3090cbd6f0851f30ae4eb68228955d973d3efa2e61"}, + {file = "grpcio-1.64.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bbc5b1d78a7822b0a84c6f8917faa986c1a744e65d762ef6d8be9d75677af2ca"}, + {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:5841dd1f284bd1b3d8a6eca3a7f062b06f1eec09b184397e1d1d43447e89a7ae"}, + {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8caee47e970b92b3dd948371230fcceb80d3f2277b3bf7fbd7c0564e7d39068e"}, + {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73819689c169417a4f978e562d24f2def2be75739c4bed1992435d007819da1b"}, + {file = "grpcio-1.64.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6503b64c8b2dfad299749cad1b595c650c91e5b2c8a1b775380fcf8d2cbba1e9"}, + {file = "grpcio-1.64.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1de403fc1305fd96cfa75e83be3dee8538f2413a6b1685b8452301c7ba33c294"}, + {file = "grpcio-1.64.1-cp311-cp311-win32.whl", hash = "sha256:d4d29cc612e1332237877dfa7fe687157973aab1d63bd0f84cf06692f04c0367"}, + {file = "grpcio-1.64.1-cp311-cp311-win_amd64.whl", hash = "sha256:5e56462b05a6f860b72f0fa50dca06d5b26543a4e88d0396259a07dc30f4e5aa"}, + {file = "grpcio-1.64.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:4657d24c8063e6095f850b68f2d1ba3b39f2b287a38242dcabc166453e950c59"}, + {file = "grpcio-1.64.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:62b4e6eb7bf901719fce0ca83e3ed474ae5022bb3827b0a501e056458c51c0a1"}, + {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:ee73a2f5ca4ba44fa33b4d7d2c71e2c8a9e9f78d53f6507ad68e7d2ad5f64a22"}, + {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:198908f9b22e2672a998870355e226a725aeab327ac4e6ff3a1399792ece4762"}, + {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39b9d0acaa8d835a6566c640f48b50054f422d03e77e49716d4c4e8e279665a1"}, + {file = "grpcio-1.64.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5e42634a989c3aa6049f132266faf6b949ec2a6f7d302dbb5c15395b77d757eb"}, + {file = "grpcio-1.64.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b1a82e0b9b3022799c336e1fc0f6210adc019ae84efb7321d668129d28ee1efb"}, + {file = "grpcio-1.64.1-cp312-cp312-win32.whl", hash = "sha256:55260032b95c49bee69a423c2f5365baa9369d2f7d233e933564d8a47b893027"}, + {file = "grpcio-1.64.1-cp312-cp312-win_amd64.whl", hash = "sha256:c1a786ac592b47573a5bb7e35665c08064a5d77ab88a076eec11f8ae86b3e3f6"}, + {file = "grpcio-1.64.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:a011ac6c03cfe162ff2b727bcb530567826cec85eb8d4ad2bfb4bd023287a52d"}, + {file = "grpcio-1.64.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:4d6dab6124225496010bd22690f2d9bd35c7cbb267b3f14e7a3eb05c911325d4"}, + {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:a5e771d0252e871ce194d0fdcafd13971f1aae0ddacc5f25615030d5df55c3a2"}, + {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c3c1b90ab93fed424e454e93c0ed0b9d552bdf1b0929712b094f5ecfe7a23ad"}, + {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20405cb8b13fd779135df23fabadc53b86522d0f1cba8cca0e87968587f50650"}, + {file = "grpcio-1.64.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0cc79c982ccb2feec8aad0e8fb0d168bcbca85bc77b080d0d3c5f2f15c24ea8f"}, + {file = "grpcio-1.64.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a3a035c37ce7565b8f4f35ff683a4db34d24e53dc487e47438e434eb3f701b2a"}, + {file = "grpcio-1.64.1-cp38-cp38-win32.whl", hash = "sha256:1257b76748612aca0f89beec7fa0615727fd6f2a1ad580a9638816a4b2eb18fd"}, + {file = "grpcio-1.64.1-cp38-cp38-win_amd64.whl", hash = "sha256:0a12ddb1678ebc6a84ec6b0487feac020ee2b1659cbe69b80f06dbffdb249122"}, + {file = "grpcio-1.64.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:75dbbf415026d2862192fe1b28d71f209e2fd87079d98470db90bebe57b33179"}, + {file = "grpcio-1.64.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e3d9f8d1221baa0ced7ec7322a981e28deb23749c76eeeb3d33e18b72935ab62"}, + {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:5f8b75f64d5d324c565b263c67dbe4f0af595635bbdd93bb1a88189fc62ed2e5"}, + {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c84ad903d0d94311a2b7eea608da163dace97c5fe9412ea311e72c3684925602"}, + {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:940e3ec884520155f68a3b712d045e077d61c520a195d1a5932c531f11883489"}, + {file = "grpcio-1.64.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f10193c69fc9d3d726e83bbf0f3d316f1847c3071c8c93d8090cf5f326b14309"}, + {file = "grpcio-1.64.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ac15b6c2c80a4d1338b04d42a02d376a53395ddf0ec9ab157cbaf44191f3ffdd"}, + {file = "grpcio-1.64.1-cp39-cp39-win32.whl", hash = "sha256:03b43d0ccf99c557ec671c7dede64f023c7da9bb632ac65dbc57f166e4970040"}, + {file = "grpcio-1.64.1-cp39-cp39-win_amd64.whl", hash = "sha256:ed6091fa0adcc7e4ff944090cf203a52da35c37a130efa564ded02b7aff63bcd"}, + {file = "grpcio-1.64.1.tar.gz", hash = "sha256:8d51dd1c59d5fa0f34266b80a3805ec29a1f26425c2a54736133f6d87fc4968a"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.64.1)"] [[package]] name = "h11" @@ -2078,6 +2146,32 @@ files = [ {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, ] +[[package]] +name = "h2" +version = "4.1.0" +description = "HTTP/2 State-Machine based protocol implementation" +optional = true +python-versions = ">=3.6.1" +files = [ + {file = "h2-4.1.0-py3-none-any.whl", hash = "sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d"}, + {file = "h2-4.1.0.tar.gz", hash = "sha256:a83aca08fbe7aacb79fec788c9c0bac936343560ed9ec18b82a13a12c28d2abb"}, +] + +[package.dependencies] +hpack = ">=4.0,<5" +hyperframe = ">=6.0,<7" + +[[package]] +name = "hpack" +version = "4.0.0" +description = "Pure-Python HPACK header compression" +optional = true +python-versions = ">=3.6.1" +files = [ + {file = "hpack-4.0.0-py3-none-any.whl", hash = "sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c"}, + {file = "hpack-4.0.0.tar.gz", hash = "sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095"}, +] + [[package]] name = "httpcore" version = "1.0.5" @@ -2161,9 +2255,11 @@ files = [ [package.dependencies] anyio = "*" certifi = "*" +h2 = {version = ">=3,<5", optional = true, markers = "extra == \"http2\""} httpcore = "==1.*" idna = "*" sniffio = "*" +socksio = {version = "==1.*", optional = true, markers = "extra == \"socks\""} [package.extras] brotli = ["brotli", "brotlicffi"] @@ -2185,6 +2281,17 @@ files = [ [package.extras] tests = ["freezegun", "pytest", "pytest-cov"] +[[package]] +name = "hyperframe" +version = "6.0.1" +description = "HTTP/2 framing layer for Python" +optional = true +python-versions = ">=3.6.1" +files = [ + {file = "hyperframe-6.0.1-py3-none-any.whl", hash = "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15"}, + {file = "hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914"}, +] + [[package]] name = "idna" version = "3.7" @@ -2209,22 +2316,22 @@ files = [ [[package]] name = "importlib-metadata" -version = "7.0.0" +version = "7.1.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-7.0.0-py3-none-any.whl", hash = "sha256:d97503976bb81f40a193d41ee6570868479c69d5068651eb039c40d850c59d67"}, - {file = "importlib_metadata-7.0.0.tar.gz", hash = "sha256:7fc841f8b8332803464e5dc1c63a2e59121f46ca186c0e2e182e80bf8c1319f7"}, + {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"}, + {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"}, ] [package.dependencies] zipp = ">=0.5" [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] [[package]] name = "importlib-resources" @@ -2340,21 +2447,21 @@ test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.21)", "pa [[package]] name = "ipywidgets" -version = "8.1.2" +version = "8.1.3" description = "Jupyter interactive widgets" optional = false python-versions = ">=3.7" files = [ - {file = "ipywidgets-8.1.2-py3-none-any.whl", hash = "sha256:bbe43850d79fb5e906b14801d6c01402857996864d1e5b6fa62dd2ee35559f60"}, - {file = "ipywidgets-8.1.2.tar.gz", hash = "sha256:d0b9b41e49bae926a866e613a39b0f0097745d2b9f1f3dd406641b4a57ec42c9"}, + {file = "ipywidgets-8.1.3-py3-none-any.whl", hash = "sha256:efafd18f7a142248f7cb0ba890a68b96abd4d6e88ddbda483c9130d12667eaf2"}, + {file = "ipywidgets-8.1.3.tar.gz", hash = "sha256:f5f9eeaae082b1823ce9eac2575272952f40d748893972956dc09700a6392d9c"}, ] [package.dependencies] comm = ">=0.1.3" ipython = ">=6.1.0" -jupyterlab-widgets = ">=3.0.10,<3.1.0" +jupyterlab-widgets = ">=3.0.11,<3.1.0" traitlets = ">=4.3.1" -widgetsnbextension = ">=4.0.10,<4.1.0" +widgetsnbextension = ">=4.0.11,<4.1.0" [package.extras] test = ["ipykernel", "jsonschema", "pytest (>=3.6.0)", "pytest-cov", "pytz"] @@ -2480,13 +2587,13 @@ files = [ [[package]] name = "jsonpointer" -version = "2.4" +version = "3.0.0" description = "Identify specific nodes in a JSON document (RFC 6901)" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" +python-versions = ">=3.7" files = [ - {file = "jsonpointer-2.4-py2.py3-none-any.whl", hash = "sha256:15d51bba20eea3165644553647711d150376234112651b4f1811022aecad7d7a"}, - {file = "jsonpointer-2.4.tar.gz", hash = "sha256:585cee82b70211fa9e6043b7bb89db6e1aa49524340dde8ad6b63206ea689d88"}, + {file = "jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942"}, + {file = "jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef"}, ] [[package]] @@ -2557,13 +2664,13 @@ qtconsole = "*" [[package]] name = "jupyter-client" -version = "8.6.1" +version = "8.6.2" description = "Jupyter protocol implementation and client libraries" optional = false python-versions = ">=3.8" files = [ - {file = "jupyter_client-8.6.1-py3-none-any.whl", hash = "sha256:3b7bd22f058434e3b9a7ea4b1500ed47de2713872288c0d511d19926f99b459f"}, - {file = "jupyter_client-8.6.1.tar.gz", hash = "sha256:e842515e2bab8e19186d89fdfea7abd15e39dd581f94e399f00e2af5a1652d3f"}, + {file = "jupyter_client-8.6.2-py3-none-any.whl", hash = "sha256:50cbc5c66fd1b8f65ecb66bc490ab73217993632809b6e505687de18e9dea39f"}, + {file = "jupyter_client-8.6.2.tar.gz", hash = "sha256:2bda14d55ee5ba58552a8c53ae43d215ad9868853489213f37da060ced54d8df"}, ] [package.dependencies] @@ -2576,7 +2683,7 @@ traitlets = ">=5.3" [package.extras] docs = ["ipykernel", "myst-parser", "pydata-sphinx-theme", "sphinx (>=4)", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] -test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pytest", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"] +test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pytest (<8.2.0)", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"] [[package]] name = "jupyter-console" @@ -2664,13 +2771,13 @@ jupyter-server = ">=1.1.2" [[package]] name = "jupyter-server" -version = "2.14.0" +version = "2.14.1" description = "The backend—i.e. core services, APIs, and REST endpoints—to Jupyter web applications." optional = false python-versions = ">=3.8" files = [ - {file = "jupyter_server-2.14.0-py3-none-any.whl", hash = "sha256:fb6be52c713e80e004fac34b35a0990d6d36ba06fd0a2b2ed82b899143a64210"}, - {file = "jupyter_server-2.14.0.tar.gz", hash = "sha256:659154cea512083434fd7c93b7fe0897af7a2fd0b9dd4749282b42eaac4ae677"}, + {file = "jupyter_server-2.14.1-py3-none-any.whl", hash = "sha256:16f7177c3a4ea8fe37784e2d31271981a812f0b2874af17339031dc3510cc2a5"}, + {file = "jupyter_server-2.14.1.tar.gz", hash = "sha256:12558d158ec7a0653bf96cc272bc7ad79e0127d503b982ed144399346694f726"}, ] [package.dependencies] @@ -2695,7 +2802,7 @@ traitlets = ">=5.6.0" websocket-client = ">=1.7" [package.extras] -docs = ["ipykernel", "jinja2", "jupyter-client", "jupyter-server", "myst-parser", "nbformat", "prometheus-client", "pydata-sphinx-theme", "send2trash", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-openapi (>=0.8.0)", "sphinxcontrib-spelling", "sphinxemoji", "tornado", "typing-extensions"] +docs = ["ipykernel", "jinja2", "jupyter-client", "myst-parser", "nbformat", "prometheus-client", "pydata-sphinx-theme", "send2trash", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-openapi (>=0.8.0)", "sphinxcontrib-spelling", "sphinxemoji", "tornado", "typing-extensions"] test = ["flaky", "ipykernel", "pre-commit", "pytest (>=7.0,<9)", "pytest-console-scripts", "pytest-jupyter[server] (>=0.7)", "pytest-timeout", "requests"] [[package]] @@ -2719,13 +2826,13 @@ test = ["jupyter-server (>=2.0.0)", "pytest (>=7.0)", "pytest-jupyter[server] (> [[package]] name = "jupyterlab" -version = "4.1.8" +version = "4.2.2" description = "JupyterLab computational environment" optional = false python-versions = ">=3.8" files = [ - {file = "jupyterlab-4.1.8-py3-none-any.whl", hash = "sha256:c3baf3a2f91f89d110ed5786cd18672b9a357129d4e389d2a0dead15e11a4d2c"}, - {file = "jupyterlab-4.1.8.tar.gz", hash = "sha256:3384aded8680e7ce504fd63b8bb89a39df21c9c7694d9e7dc4a68742cdb30f9b"}, + {file = "jupyterlab-4.2.2-py3-none-any.whl", hash = "sha256:59ee9b839f43308c3dfd55d72d1f1a299ed42a7f91f2d1afe9c12a783f9e525f"}, + {file = "jupyterlab-4.2.2.tar.gz", hash = "sha256:a534b6a25719a92a40d514fb133a9fe8f0d9981b0bbce5d8a5fcaa33344a3038"}, ] [package.dependencies] @@ -2741,16 +2848,17 @@ jupyter-server = ">=2.4.0,<3" jupyterlab-server = ">=2.27.1,<3" notebook-shim = ">=0.2" packaging = "*" +setuptools = ">=40.1.0" tomli = {version = ">=1.2.2", markers = "python_version < \"3.11\""} tornado = ">=6.2.0" traitlets = "*" [package.extras] -dev = ["build", "bump2version", "coverage", "hatch", "pre-commit", "pytest-cov", "ruff (==0.2.0)"] +dev = ["build", "bump2version", "coverage", "hatch", "pre-commit", "pytest-cov", "ruff (==0.3.5)"] docs = ["jsx-lexer", "myst-parser", "pydata-sphinx-theme (>=0.13.0)", "pytest", "pytest-check-links", "pytest-jupyter", "sphinx (>=1.8,<7.3.0)", "sphinx-copybutton"] -docs-screenshots = ["altair (==5.2.0)", "ipython (==8.16.1)", "ipywidgets (==8.1.1)", "jupyterlab-geojson (==3.4.0)", "jupyterlab-language-pack-zh-cn (==4.0.post6)", "matplotlib (==3.8.2)", "nbconvert (>=7.0.0)", "pandas (==2.2.0)", "scipy (==1.12.0)", "vega-datasets (==0.9.0)"] +docs-screenshots = ["altair (==5.3.0)", "ipython (==8.16.1)", "ipywidgets (==8.1.2)", "jupyterlab-geojson (==3.4.0)", "jupyterlab-language-pack-zh-cn (==4.1.post2)", "matplotlib (==3.8.3)", "nbconvert (>=7.0.0)", "pandas (==2.2.1)", "scipy (==1.12.0)", "vega-datasets (==0.9.0)"] test = ["coverage", "pytest (>=7.0)", "pytest-check-links (>=0.7)", "pytest-console-scripts", "pytest-cov", "pytest-jupyter (>=0.5.3)", "pytest-timeout", "pytest-tornasync", "requests", "requests-cache", "virtualenv"] -upgrade-extension = ["copier (>=8.0,<9.0)", "jinja2-time (<0.3)", "pydantic (<2.0)", "pyyaml-include (<2.0)", "tomli-w (<2.0)"] +upgrade-extension = ["copier (>=8,<10)", "jinja2-time (<0.3)", "pydantic (<2.0)", "pyyaml-include (<2.0)", "tomli-w (<2.0)"] [[package]] name = "jupyterlab-pygments" @@ -2765,13 +2873,13 @@ files = [ [[package]] name = "jupyterlab-server" -version = "2.27.1" +version = "2.27.2" description = "A set of server components for JupyterLab and JupyterLab like applications." optional = false python-versions = ">=3.8" files = [ - {file = "jupyterlab_server-2.27.1-py3-none-any.whl", hash = "sha256:f5e26156e5258b24d532c84e7c74cc212e203bff93eb856f81c24c16daeecc75"}, - {file = "jupyterlab_server-2.27.1.tar.gz", hash = "sha256:097b5ac709b676c7284ac9c5e373f11930a561f52cd5a86e4fc7e5a9c8a8631d"}, + {file = "jupyterlab_server-2.27.2-py3-none-any.whl", hash = "sha256:54aa2d64fd86383b5438d9f0c032f043c4d8c0264b8af9f60bd061157466ea43"}, + {file = "jupyterlab_server-2.27.2.tar.gz", hash = "sha256:15cbb349dc45e954e09bacf81b9f9bcb10815ff660fb2034ecd7417db3a7ea27"}, ] [package.dependencies] @@ -2791,13 +2899,13 @@ test = ["hatch", "ipykernel", "openapi-core (>=0.18.0,<0.19.0)", "openapi-spec-v [[package]] name = "jupyterlab-widgets" -version = "3.0.10" +version = "3.0.11" description = "Jupyter interactive widgets for JupyterLab" optional = false python-versions = ">=3.7" files = [ - {file = "jupyterlab_widgets-3.0.10-py3-none-any.whl", hash = "sha256:dd61f3ae7a5a7f80299e14585ce6cf3d6925a96c9103c978eda293197730cb64"}, - {file = "jupyterlab_widgets-3.0.10.tar.gz", hash = "sha256:04f2ac04976727e4f9d0fa91cdc2f1ab860f965e504c29dbd6a65c882c9d04c0"}, + {file = "jupyterlab_widgets-3.0.11-py3-none-any.whl", hash = "sha256:78287fd86d20744ace330a61625024cf5521e1c012a352ddc0a3cdc2348becd0"}, + {file = "jupyterlab_widgets-3.0.11.tar.gz", hash = "sha256:dd5ac679593c969af29c9bed054c24f26842baa51352114736756bc035deee27"}, ] [[package]] @@ -3000,13 +3108,13 @@ files = [ [[package]] name = "mdit-py-plugins" -version = "0.4.0" +version = "0.4.1" description = "Collection of plugins for markdown-it-py" optional = false python-versions = ">=3.8" files = [ - {file = "mdit_py_plugins-0.4.0-py3-none-any.whl", hash = "sha256:b51b3bb70691f57f974e257e367107857a93b36f322a9e6d44ca5bf28ec2def9"}, - {file = "mdit_py_plugins-0.4.0.tar.gz", hash = "sha256:d8ab27e9aed6c38aa716819fedfde15ca275715955f8a185a8e1cf90fb1d2c1b"}, + {file = "mdit_py_plugins-0.4.1-py3-none-any.whl", hash = "sha256:1020dfe4e6bfc2c79fb49ae4e3f5b297f5ccd20f010187acc52af2921e27dc6a"}, + {file = "mdit_py_plugins-0.4.1.tar.gz", hash = "sha256:834b8ac23d1cd60cec703646ffd22ae97b7955a6d596eb1d304be1e251ae499c"}, ] [package.dependencies] @@ -3057,13 +3165,13 @@ test = ["pytest", "pytest-cov"] [[package]] name = "more-itertools" -version = "10.2.0" +version = "10.3.0" description = "More routines for operating on iterables, beyond itertools" optional = false python-versions = ">=3.8" files = [ - {file = "more-itertools-10.2.0.tar.gz", hash = "sha256:8fccb480c43d3e99a00087634c06dd02b0d50fbf088b380de5a41a015ec239e1"}, - {file = "more_itertools-10.2.0-py3-none-any.whl", hash = "sha256:686b06abe565edfab151cb8fd385a05651e1fdf8f0a14191e4439283421f8684"}, + {file = "more-itertools-10.3.0.tar.gz", hash = "sha256:e5d93ef411224fbcef366a6e8ddc4c5781bc6359d43412a65dd5964e46111463"}, + {file = "more_itertools-10.3.0-py3-none-any.whl", hash = "sha256:ea6a02e24a9161e51faad17a8782b92a0df82c12c1c8886fec7f0c3fa1a1b320"}, ] [[package]] @@ -3425,26 +3533,26 @@ files = [ [[package]] name = "notebook" -version = "7.1.3" +version = "7.2.1" description = "Jupyter Notebook - A web-based notebook environment for interactive computing" optional = false python-versions = ">=3.8" files = [ - {file = "notebook-7.1.3-py3-none-any.whl", hash = "sha256:919b911e59f41f6e3857ce93c9d93535ba66bb090059712770e5968c07e1004d"}, - {file = "notebook-7.1.3.tar.gz", hash = "sha256:41fcebff44cf7bb9377180808bcbae066629b55d8c7722f1ebbe75ca44f9cfc1"}, + {file = "notebook-7.2.1-py3-none-any.whl", hash = "sha256:f45489a3995746f2195a137e0773e2130960b51c9ac3ce257dbc2705aab3a6ca"}, + {file = "notebook-7.2.1.tar.gz", hash = "sha256:4287b6da59740b32173d01d641f763d292f49c30e7a51b89c46ba8473126341e"}, ] [package.dependencies] jupyter-server = ">=2.4.0,<3" -jupyterlab = ">=4.1.1,<4.2" -jupyterlab-server = ">=2.22.1,<3" +jupyterlab = ">=4.2.0,<4.3" +jupyterlab-server = ">=2.27.1,<3" notebook-shim = ">=0.2,<0.3" tornado = ">=6.2.0" [package.extras] dev = ["hatch", "pre-commit"] docs = ["myst-parser", "nbsphinx", "pydata-sphinx-theme", "sphinx (>=1.3.6)", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] -test = ["importlib-resources (>=5.0)", "ipykernel", "jupyter-server[test] (>=2.4.0,<3)", "jupyterlab-server[test] (>=2.22.1,<3)", "nbval", "pytest (>=7.0)", "pytest-console-scripts", "pytest-timeout", "pytest-tornasync", "requests"] +test = ["importlib-resources (>=5.0)", "ipykernel", "jupyter-server[test] (>=2.4.0,<3)", "jupyterlab-server[test] (>=2.27.1,<3)", "nbval", "pytest (>=7.0)", "pytest-console-scripts", "pytest-timeout", "pytest-tornasync", "requests"] [[package]] name = "notebook-shim" @@ -3517,57 +3625,57 @@ PyYAML = ">=5.1.0" [[package]] name = "opentelemetry-api" -version = "1.24.0" +version = "1.25.0" description = "OpenTelemetry Python API" optional = true python-versions = ">=3.8" files = [ - {file = "opentelemetry_api-1.24.0-py3-none-any.whl", hash = "sha256:0f2c363d98d10d1ce93330015ca7fd3a65f60be64e05e30f557c61de52c80ca2"}, - {file = "opentelemetry_api-1.24.0.tar.gz", hash = "sha256:42719f10ce7b5a9a73b10a4baf620574fb8ad495a9cbe5c18d76b75d8689c67e"}, + {file = "opentelemetry_api-1.25.0-py3-none-any.whl", hash = "sha256:757fa1aa020a0f8fa139f8959e53dec2051cc26b832e76fa839a6d76ecefd737"}, + {file = "opentelemetry_api-1.25.0.tar.gz", hash = "sha256:77c4985f62f2614e42ce77ee4c9da5fa5f0bc1e1821085e9a47533a9323ae869"}, ] [package.dependencies] deprecated = ">=1.2.6" -importlib-metadata = ">=6.0,<=7.0" +importlib-metadata = ">=6.0,<=7.1" [[package]] name = "opentelemetry-exporter-otlp" -version = "1.24.0" +version = "1.25.0" description = "OpenTelemetry Collector Exporters" optional = true python-versions = ">=3.8" files = [ - {file = "opentelemetry_exporter_otlp-1.24.0-py3-none-any.whl", hash = "sha256:1dfe2e4befe1f0efc193a896837740407669b2929233b406ac0a813151200cac"}, - {file = "opentelemetry_exporter_otlp-1.24.0.tar.gz", hash = "sha256:649c6e249e55cbdebe99ba2846e3851c04c9f328570328c35b3af9c094314b55"}, + {file = "opentelemetry_exporter_otlp-1.25.0-py3-none-any.whl", hash = "sha256:d67a831757014a3bc3174e4cd629ae1493b7ba8d189e8a007003cacb9f1a6b60"}, + {file = "opentelemetry_exporter_otlp-1.25.0.tar.gz", hash = "sha256:ce03199c1680a845f82e12c0a6a8f61036048c07ec7a0bd943142aca8fa6ced0"}, ] [package.dependencies] -opentelemetry-exporter-otlp-proto-grpc = "1.24.0" -opentelemetry-exporter-otlp-proto-http = "1.24.0" +opentelemetry-exporter-otlp-proto-grpc = "1.25.0" +opentelemetry-exporter-otlp-proto-http = "1.25.0" [[package]] name = "opentelemetry-exporter-otlp-proto-common" -version = "1.24.0" +version = "1.25.0" description = "OpenTelemetry Protobuf encoding" optional = true python-versions = ">=3.8" files = [ - {file = "opentelemetry_exporter_otlp_proto_common-1.24.0-py3-none-any.whl", hash = "sha256:e51f2c9735054d598ad2df5d3eca830fecfb5b0bda0a2fa742c9c7718e12f641"}, - {file = "opentelemetry_exporter_otlp_proto_common-1.24.0.tar.gz", hash = "sha256:5d31fa1ff976cacc38be1ec4e3279a3f88435c75b38b1f7a099a1faffc302461"}, + {file = "opentelemetry_exporter_otlp_proto_common-1.25.0-py3-none-any.whl", hash = "sha256:15637b7d580c2675f70246563363775b4e6de947871e01d0f4e3881d1848d693"}, + {file = "opentelemetry_exporter_otlp_proto_common-1.25.0.tar.gz", hash = "sha256:c93f4e30da4eee02bacd1e004eb82ce4da143a2f8e15b987a9f603e0a85407d3"}, ] [package.dependencies] -opentelemetry-proto = "1.24.0" +opentelemetry-proto = "1.25.0" [[package]] name = "opentelemetry-exporter-otlp-proto-grpc" -version = "1.24.0" +version = "1.25.0" description = "OpenTelemetry Collector Protobuf over gRPC Exporter" optional = true python-versions = ">=3.8" files = [ - {file = "opentelemetry_exporter_otlp_proto_grpc-1.24.0-py3-none-any.whl", hash = "sha256:f40d62aa30a0a43cc1657428e59fcf82ad5f7ea8fff75de0f9d9cb6f739e0a3b"}, - {file = "opentelemetry_exporter_otlp_proto_grpc-1.24.0.tar.gz", hash = "sha256:217c6e30634f2c9797999ea9da29f7300479a94a610139b9df17433f915e7baa"}, + {file = "opentelemetry_exporter_otlp_proto_grpc-1.25.0-py3-none-any.whl", hash = "sha256:3131028f0c0a155a64c430ca600fd658e8e37043cb13209f0109db5c1a3e4eb4"}, + {file = "opentelemetry_exporter_otlp_proto_grpc-1.25.0.tar.gz", hash = "sha256:c0b1661415acec5af87625587efa1ccab68b873745ca0ee96b69bb1042087eac"}, ] [package.dependencies] @@ -3575,42 +3683,39 @@ deprecated = ">=1.2.6" googleapis-common-protos = ">=1.52,<2.0" grpcio = ">=1.0.0,<2.0.0" opentelemetry-api = ">=1.15,<2.0" -opentelemetry-exporter-otlp-proto-common = "1.24.0" -opentelemetry-proto = "1.24.0" -opentelemetry-sdk = ">=1.24.0,<1.25.0" - -[package.extras] -test = ["pytest-grpc"] +opentelemetry-exporter-otlp-proto-common = "1.25.0" +opentelemetry-proto = "1.25.0" +opentelemetry-sdk = ">=1.25.0,<1.26.0" [[package]] name = "opentelemetry-exporter-otlp-proto-http" -version = "1.24.0" +version = "1.25.0" description = "OpenTelemetry Collector Protobuf over HTTP Exporter" optional = true python-versions = ">=3.8" files = [ - {file = "opentelemetry_exporter_otlp_proto_http-1.24.0-py3-none-any.whl", hash = "sha256:25af10e46fdf4cd3833175e42f4879a1255fc01655fe14c876183a2903949836"}, - {file = "opentelemetry_exporter_otlp_proto_http-1.24.0.tar.gz", hash = "sha256:704c066cc96f5131881b75c0eac286cd73fc735c490b054838b4513254bd7850"}, + {file = "opentelemetry_exporter_otlp_proto_http-1.25.0-py3-none-any.whl", hash = "sha256:2eca686ee11b27acd28198b3ea5e5863a53d1266b91cda47c839d95d5e0541a6"}, + {file = "opentelemetry_exporter_otlp_proto_http-1.25.0.tar.gz", hash = "sha256:9f8723859e37c75183ea7afa73a3542f01d0fd274a5b97487ea24cb683d7d684"}, ] [package.dependencies] deprecated = ">=1.2.6" googleapis-common-protos = ">=1.52,<2.0" opentelemetry-api = ">=1.15,<2.0" -opentelemetry-exporter-otlp-proto-common = "1.24.0" -opentelemetry-proto = "1.24.0" -opentelemetry-sdk = ">=1.24.0,<1.25.0" +opentelemetry-exporter-otlp-proto-common = "1.25.0" +opentelemetry-proto = "1.25.0" +opentelemetry-sdk = ">=1.25.0,<1.26.0" requests = ">=2.7,<3.0" [[package]] name = "opentelemetry-instrumentation" -version = "0.45b0" +version = "0.46b0" description = "Instrumentation Tools & Auto Instrumentation for OpenTelemetry Python" optional = true python-versions = ">=3.8" files = [ - {file = "opentelemetry_instrumentation-0.45b0-py3-none-any.whl", hash = "sha256:06c02e2c952c1b076e8eaedf1b82f715e2937ba7eeacab55913dd434fbcec258"}, - {file = "opentelemetry_instrumentation-0.45b0.tar.gz", hash = "sha256:6c47120a7970bbeb458e6a73686ee9ba84b106329a79e4a4a66761f933709c7e"}, + {file = "opentelemetry_instrumentation-0.46b0-py3-none-any.whl", hash = "sha256:89cd721b9c18c014ca848ccd11181e6b3fd3f6c7669e35d59c48dc527408c18b"}, + {file = "opentelemetry_instrumentation-0.46b0.tar.gz", hash = "sha256:974e0888fb2a1e01c38fbacc9483d024bb1132aad92d6d24e2e5543887a7adda"}, ] [package.dependencies] @@ -3620,13 +3725,13 @@ wrapt = ">=1.0.0,<2.0.0" [[package]] name = "opentelemetry-proto" -version = "1.24.0" +version = "1.25.0" description = "OpenTelemetry Python Proto" optional = true python-versions = ">=3.8" files = [ - {file = "opentelemetry_proto-1.24.0-py3-none-any.whl", hash = "sha256:bcb80e1e78a003040db71ccf83f2ad2019273d1e0828089d183b18a1476527ce"}, - {file = "opentelemetry_proto-1.24.0.tar.gz", hash = "sha256:ff551b8ad63c6cabb1845ce217a6709358dfaba0f75ea1fa21a61ceddc78cab8"}, + {file = "opentelemetry_proto-1.25.0-py3-none-any.whl", hash = "sha256:f07e3341c78d835d9b86665903b199893befa5e98866f63d22b00d0b7ca4972f"}, + {file = "opentelemetry_proto-1.25.0.tar.gz", hash = "sha256:35b6ef9dc4a9f7853ecc5006738ad40443701e52c26099e197895cbda8b815a3"}, ] [package.dependencies] @@ -3634,84 +3739,87 @@ protobuf = ">=3.19,<5.0" [[package]] name = "opentelemetry-sdk" -version = "1.24.0" +version = "1.25.0" description = "OpenTelemetry Python SDK" optional = true python-versions = ">=3.8" files = [ - {file = "opentelemetry_sdk-1.24.0-py3-none-any.whl", hash = "sha256:fa731e24efe832e98bcd90902085b359dcfef7d9c9c00eb5b9a18587dae3eb59"}, - {file = "opentelemetry_sdk-1.24.0.tar.gz", hash = "sha256:75bc0563affffa827700e0f4f4a68e1e257db0df13372344aebc6f8a64cde2e5"}, + {file = "opentelemetry_sdk-1.25.0-py3-none-any.whl", hash = "sha256:d97ff7ec4b351692e9d5a15af570c693b8715ad78b8aafbec5c7100fe966b4c9"}, + {file = "opentelemetry_sdk-1.25.0.tar.gz", hash = "sha256:ce7fc319c57707ef5bf8b74fb9f8ebdb8bfafbe11898410e0d2a761d08a98ec7"}, ] [package.dependencies] -opentelemetry-api = "1.24.0" -opentelemetry-semantic-conventions = "0.45b0" +opentelemetry-api = "1.25.0" +opentelemetry-semantic-conventions = "0.46b0" typing-extensions = ">=3.7.4" [[package]] name = "opentelemetry-semantic-conventions" -version = "0.45b0" +version = "0.46b0" description = "OpenTelemetry Semantic Conventions" optional = true python-versions = ">=3.8" files = [ - {file = "opentelemetry_semantic_conventions-0.45b0-py3-none-any.whl", hash = "sha256:a4a6fb9a7bacd9167c082aa4681009e9acdbfa28ffb2387af50c2fef3d30c864"}, - {file = "opentelemetry_semantic_conventions-0.45b0.tar.gz", hash = "sha256:7c84215a44ac846bc4b8e32d5e78935c5c43482e491812a0bb8aaf87e4d92118"}, + {file = "opentelemetry_semantic_conventions-0.46b0-py3-none-any.whl", hash = "sha256:6daef4ef9fa51d51855d9f8e0ccd3a1bd59e0e545abe99ac6203804e36ab3e07"}, + {file = "opentelemetry_semantic_conventions-0.46b0.tar.gz", hash = "sha256:fbc982ecbb6a6e90869b15c1673be90bd18c8a56ff1cffc0864e38e2edffaefa"}, ] +[package.dependencies] +opentelemetry-api = "1.25.0" + [[package]] name = "orjson" -version = "3.10.3" +version = "3.10.5" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.8" files = [ - {file = "orjson-3.10.3-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9fb6c3f9f5490a3eb4ddd46fc1b6eadb0d6fc16fb3f07320149c3286a1409dd8"}, - {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:252124b198662eee80428f1af8c63f7ff077c88723fe206a25df8dc57a57b1fa"}, - {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9f3e87733823089a338ef9bbf363ef4de45e5c599a9bf50a7a9b82e86d0228da"}, - {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8334c0d87103bb9fbbe59b78129f1f40d1d1e8355bbed2ca71853af15fa4ed3"}, - {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1952c03439e4dce23482ac846e7961f9d4ec62086eb98ae76d97bd41d72644d7"}, - {file = "orjson-3.10.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c0403ed9c706dcd2809f1600ed18f4aae50be263bd7112e54b50e2c2bc3ebd6d"}, - {file = "orjson-3.10.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:382e52aa4270a037d41f325e7d1dfa395b7de0c367800b6f337d8157367bf3a7"}, - {file = "orjson-3.10.3-cp310-none-win32.whl", hash = "sha256:be2aab54313752c04f2cbaab4515291ef5af8c2256ce22abc007f89f42f49109"}, - {file = "orjson-3.10.3-cp310-none-win_amd64.whl", hash = "sha256:416b195f78ae461601893f482287cee1e3059ec49b4f99479aedf22a20b1098b"}, - {file = "orjson-3.10.3-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:73100d9abbbe730331f2242c1fc0bcb46a3ea3b4ae3348847e5a141265479700"}, - {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:544a12eee96e3ab828dbfcb4d5a0023aa971b27143a1d35dc214c176fdfb29b3"}, - {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:520de5e2ef0b4ae546bea25129d6c7c74edb43fc6cf5213f511a927f2b28148b"}, - {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ccaa0a401fc02e8828a5bedfd80f8cd389d24f65e5ca3954d72c6582495b4bcf"}, - {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7bc9e8bc11bac40f905640acd41cbeaa87209e7e1f57ade386da658092dc16"}, - {file = "orjson-3.10.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3582b34b70543a1ed6944aca75e219e1192661a63da4d039d088a09c67543b08"}, - {file = "orjson-3.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1c23dfa91481de880890d17aa7b91d586a4746a4c2aa9a145bebdbaf233768d5"}, - {file = "orjson-3.10.3-cp311-none-win32.whl", hash = "sha256:1770e2a0eae728b050705206d84eda8b074b65ee835e7f85c919f5705b006c9b"}, - {file = "orjson-3.10.3-cp311-none-win_amd64.whl", hash = "sha256:93433b3c1f852660eb5abdc1f4dd0ced2be031ba30900433223b28ee0140cde5"}, - {file = "orjson-3.10.3-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a39aa73e53bec8d410875683bfa3a8edf61e5a1c7bb4014f65f81d36467ea098"}, - {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0943a96b3fa09bee1afdfccc2cb236c9c64715afa375b2af296c73d91c23eab2"}, - {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e852baafceff8da3c9defae29414cc8513a1586ad93e45f27b89a639c68e8176"}, - {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18566beb5acd76f3769c1d1a7ec06cdb81edc4d55d2765fb677e3eaa10fa99e0"}, - {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bd2218d5a3aa43060efe649ec564ebedec8ce6ae0a43654b81376216d5ebd42"}, - {file = "orjson-3.10.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cf20465e74c6e17a104ecf01bf8cd3b7b252565b4ccee4548f18b012ff2f8069"}, - {file = "orjson-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ba7f67aa7f983c4345eeda16054a4677289011a478ca947cd69c0a86ea45e534"}, - {file = "orjson-3.10.3-cp312-none-win32.whl", hash = "sha256:17e0713fc159abc261eea0f4feda611d32eabc35708b74bef6ad44f6c78d5ea0"}, - {file = "orjson-3.10.3-cp312-none-win_amd64.whl", hash = "sha256:4c895383b1ec42b017dd2c75ae8a5b862fc489006afde06f14afbdd0309b2af0"}, - {file = "orjson-3.10.3-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:be2719e5041e9fb76c8c2c06b9600fe8e8584e6980061ff88dcbc2691a16d20d"}, - {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0175a5798bdc878956099f5c54b9837cb62cfbf5d0b86ba6d77e43861bcec2"}, - {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:978be58a68ade24f1af7758626806e13cff7748a677faf95fbb298359aa1e20d"}, - {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16bda83b5c61586f6f788333d3cf3ed19015e3b9019188c56983b5a299210eb5"}, - {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ad1f26bea425041e0a1adad34630c4825a9e3adec49079b1fb6ac8d36f8b754"}, - {file = "orjson-3.10.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:9e253498bee561fe85d6325ba55ff2ff08fb5e7184cd6a4d7754133bd19c9195"}, - {file = "orjson-3.10.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0a62f9968bab8a676a164263e485f30a0b748255ee2f4ae49a0224be95f4532b"}, - {file = "orjson-3.10.3-cp38-none-win32.whl", hash = "sha256:8d0b84403d287d4bfa9bf7d1dc298d5c1c5d9f444f3737929a66f2fe4fb8f134"}, - {file = "orjson-3.10.3-cp38-none-win_amd64.whl", hash = "sha256:8bc7a4df90da5d535e18157220d7915780d07198b54f4de0110eca6b6c11e290"}, - {file = "orjson-3.10.3-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9059d15c30e675a58fdcd6f95465c1522b8426e092de9fff20edebfdc15e1cb0"}, - {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d40c7f7938c9c2b934b297412c067936d0b54e4b8ab916fd1a9eb8f54c02294"}, - {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4a654ec1de8fdaae1d80d55cee65893cb06494e124681ab335218be6a0691e7"}, - {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:831c6ef73f9aa53c5f40ae8f949ff7681b38eaddb6904aab89dca4d85099cb78"}, - {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99b880d7e34542db89f48d14ddecbd26f06838b12427d5a25d71baceb5ba119d"}, - {file = "orjson-3.10.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2e5e176c994ce4bd434d7aafb9ecc893c15f347d3d2bbd8e7ce0b63071c52e25"}, - {file = "orjson-3.10.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b69a58a37dab856491bf2d3bbf259775fdce262b727f96aafbda359cb1d114d8"}, - {file = "orjson-3.10.3-cp39-none-win32.whl", hash = "sha256:b8d4d1a6868cde356f1402c8faeb50d62cee765a1f7ffcfd6de732ab0581e063"}, - {file = "orjson-3.10.3-cp39-none-win_amd64.whl", hash = "sha256:5102f50c5fc46d94f2033fe00d392588564378260d64377aec702f21a7a22912"}, - {file = "orjson-3.10.3.tar.gz", hash = "sha256:2b166507acae7ba2f7c315dcf185a9111ad5e992ac81f2d507aac39193c2c818"}, + {file = "orjson-3.10.5-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:545d493c1f560d5ccfc134803ceb8955a14c3fcb47bbb4b2fee0232646d0b932"}, + {file = "orjson-3.10.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4324929c2dd917598212bfd554757feca3e5e0fa60da08be11b4aa8b90013c1"}, + {file = "orjson-3.10.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c13ca5e2ddded0ce6a927ea5a9f27cae77eee4c75547b4297252cb20c4d30e6"}, + {file = "orjson-3.10.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b6c8e30adfa52c025f042a87f450a6b9ea29649d828e0fec4858ed5e6caecf63"}, + {file = "orjson-3.10.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:338fd4f071b242f26e9ca802f443edc588fa4ab60bfa81f38beaedf42eda226c"}, + {file = "orjson-3.10.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6970ed7a3126cfed873c5d21ece1cd5d6f83ca6c9afb71bbae21a0b034588d96"}, + {file = "orjson-3.10.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:235dadefb793ad12f7fa11e98a480db1f7c6469ff9e3da5e73c7809c700d746b"}, + {file = "orjson-3.10.5-cp310-none-win32.whl", hash = "sha256:be79e2393679eda6a590638abda16d167754393f5d0850dcbca2d0c3735cebe2"}, + {file = "orjson-3.10.5-cp310-none-win_amd64.whl", hash = "sha256:c4a65310ccb5c9910c47b078ba78e2787cb3878cdded1702ac3d0da71ddc5228"}, + {file = "orjson-3.10.5-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:cdf7365063e80899ae3a697def1277c17a7df7ccfc979990a403dfe77bb54d40"}, + {file = "orjson-3.10.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b68742c469745d0e6ca5724506858f75e2f1e5b59a4315861f9e2b1df77775a"}, + {file = "orjson-3.10.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7d10cc1b594951522e35a3463da19e899abe6ca95f3c84c69e9e901e0bd93d38"}, + {file = "orjson-3.10.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dcbe82b35d1ac43b0d84072408330fd3295c2896973112d495e7234f7e3da2e1"}, + {file = "orjson-3.10.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c0eb7e0c75e1e486c7563fe231b40fdd658a035ae125c6ba651ca3b07936f5"}, + {file = "orjson-3.10.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:53ed1c879b10de56f35daf06dbc4a0d9a5db98f6ee853c2dbd3ee9d13e6f302f"}, + {file = "orjson-3.10.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:099e81a5975237fda3100f918839af95f42f981447ba8f47adb7b6a3cdb078fa"}, + {file = "orjson-3.10.5-cp311-none-win32.whl", hash = "sha256:1146bf85ea37ac421594107195db8bc77104f74bc83e8ee21a2e58596bfb2f04"}, + {file = "orjson-3.10.5-cp311-none-win_amd64.whl", hash = "sha256:36a10f43c5f3a55c2f680efe07aa93ef4a342d2960dd2b1b7ea2dd764fe4a37c"}, + {file = "orjson-3.10.5-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:68f85ecae7af14a585a563ac741b0547a3f291de81cd1e20903e79f25170458f"}, + {file = "orjson-3.10.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28afa96f496474ce60d3340fe8d9a263aa93ea01201cd2bad844c45cd21f5268"}, + {file = "orjson-3.10.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cd684927af3e11b6e754df80b9ffafd9fb6adcaa9d3e8fdd5891be5a5cad51e"}, + {file = "orjson-3.10.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d21b9983da032505f7050795e98b5d9eee0df903258951566ecc358f6696969"}, + {file = "orjson-3.10.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ad1de7fef79736dde8c3554e75361ec351158a906d747bd901a52a5c9c8d24b"}, + {file = "orjson-3.10.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2d97531cdfe9bdd76d492e69800afd97e5930cb0da6a825646667b2c6c6c0211"}, + {file = "orjson-3.10.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d69858c32f09c3e1ce44b617b3ebba1aba030e777000ebdf72b0d8e365d0b2b3"}, + {file = "orjson-3.10.5-cp312-none-win32.whl", hash = "sha256:64c9cc089f127e5875901ac05e5c25aa13cfa5dbbbd9602bda51e5c611d6e3e2"}, + {file = "orjson-3.10.5-cp312-none-win_amd64.whl", hash = "sha256:b2efbd67feff8c1f7728937c0d7f6ca8c25ec81373dc8db4ef394c1d93d13dc5"}, + {file = "orjson-3.10.5-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:03b565c3b93f5d6e001db48b747d31ea3819b89abf041ee10ac6988886d18e01"}, + {file = "orjson-3.10.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:584c902ec19ab7928fd5add1783c909094cc53f31ac7acfada817b0847975f26"}, + {file = "orjson-3.10.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a35455cc0b0b3a1eaf67224035f5388591ec72b9b6136d66b49a553ce9eb1e6"}, + {file = "orjson-3.10.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1670fe88b116c2745a3a30b0f099b699a02bb3482c2591514baf5433819e4f4d"}, + {file = "orjson-3.10.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:185c394ef45b18b9a7d8e8f333606e2e8194a50c6e3c664215aae8cf42c5385e"}, + {file = "orjson-3.10.5-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ca0b3a94ac8d3886c9581b9f9de3ce858263865fdaa383fbc31c310b9eac07c9"}, + {file = "orjson-3.10.5-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dfc91d4720d48e2a709e9c368d5125b4b5899dced34b5400c3837dadc7d6271b"}, + {file = "orjson-3.10.5-cp38-none-win32.whl", hash = "sha256:c05f16701ab2a4ca146d0bca950af254cb7c02f3c01fca8efbbad82d23b3d9d4"}, + {file = "orjson-3.10.5-cp38-none-win_amd64.whl", hash = "sha256:8a11d459338f96a9aa7f232ba95679fc0c7cedbd1b990d736467894210205c09"}, + {file = "orjson-3.10.5-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:85c89131d7b3218db1b24c4abecea92fd6c7f9fab87441cfc342d3acc725d807"}, + {file = "orjson-3.10.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb66215277a230c456f9038d5e2d84778141643207f85336ef8d2a9da26bd7ca"}, + {file = "orjson-3.10.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:51bbcdea96cdefa4a9b4461e690c75ad4e33796530d182bdd5c38980202c134a"}, + {file = "orjson-3.10.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbead71dbe65f959b7bd8cf91e0e11d5338033eba34c114f69078d59827ee139"}, + {file = "orjson-3.10.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5df58d206e78c40da118a8c14fc189207fffdcb1f21b3b4c9c0c18e839b5a214"}, + {file = "orjson-3.10.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c4057c3b511bb8aef605616bd3f1f002a697c7e4da6adf095ca5b84c0fd43595"}, + {file = "orjson-3.10.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b39e006b00c57125ab974362e740c14a0c6a66ff695bff44615dcf4a70ce2b86"}, + {file = "orjson-3.10.5-cp39-none-win32.whl", hash = "sha256:eded5138cc565a9d618e111c6d5c2547bbdd951114eb822f7f6309e04db0fb47"}, + {file = "orjson-3.10.5-cp39-none-win_amd64.whl", hash = "sha256:cc28e90a7cae7fcba2493953cff61da5a52950e78dc2dacfe931a317ee3d8de7"}, + {file = "orjson-3.10.5.tar.gz", hash = "sha256:7a5baef8a4284405d96c90c7c62b755e9ef1ada84c2406c24a9ebec86b89f46d"}, ] [[package]] @@ -3727,13 +3835,13 @@ files = [ [[package]] name = "packaging" -version = "24.0" +version = "24.1" description = "Core utilities for Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, - {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] [[package]] @@ -3772,9 +3880,9 @@ files = [ [package.dependencies] numpy = [ + {version = ">=1.23.2", markers = "python_version >= \"3.11\""}, {version = ">=1.20.3", markers = "python_version < \"3.10\""}, {version = ">=1.21.0", markers = "python_version >= \"3.10\" and python_version < \"3.11\""}, - {version = ">=1.23.2", markers = "python_version >= \"3.11\""}, ] python-dateutil = ">=2.8.2" pytz = ">=2020.1" @@ -4008,13 +4116,13 @@ xmp = ["defusedxml"] [[package]] name = "pkginfo" -version = "1.10.0" +version = "1.11.1" description = "Query metadata from sdists / bdists / installed packages." optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "pkginfo-1.10.0-py3-none-any.whl", hash = "sha256:889a6da2ed7ffc58ab5b900d888ddce90bce912f2d2de1dc1c26f4cb9fe65097"}, - {file = "pkginfo-1.10.0.tar.gz", hash = "sha256:5df73835398d10db79f8eecd5cd86b1f6d29317589ea70796994d49399af6297"}, + {file = "pkginfo-1.11.1-py3-none-any.whl", hash = "sha256:bfa76a714fdfc18a045fcd684dbfc3816b603d9d075febef17cb6582bea29573"}, + {file = "pkginfo-1.11.1.tar.gz", hash = "sha256:2e0dca1cf4c8e39644eed32408ea9966ee15e0d324c62ba899a393b3c6b467aa"}, ] [package.extras] @@ -4033,13 +4141,13 @@ files = [ [[package]] name = "platformdirs" -version = "4.2.1" +version = "4.2.2" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"}, - {file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"}, + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, ] [package.extras] @@ -4083,13 +4191,13 @@ poetry-plugin = ["poetry (>=1.0,<2.0)"] [[package]] name = "poetry" -version = "1.8.2" +version = "1.8.3" description = "Python dependency management and packaging made easy." optional = false -python-versions = ">=3.8,<4.0" +python-versions = "<4.0,>=3.8" files = [ - {file = "poetry-1.8.2-py3-none-any.whl", hash = "sha256:b42b400d9a803af6e788a30a6f3e9998020b77860e28df20647eb10b6f414910"}, - {file = "poetry-1.8.2.tar.gz", hash = "sha256:49cceb3838104647c3e1021f3a4f13c6053704cc18d33f849a90fe687a29cb73"}, + {file = "poetry-1.8.3-py3-none-any.whl", hash = "sha256:88191c69b08d06f9db671b793d68f40048e8904c0718404b63dcc2b5aec62d13"}, + {file = "poetry-1.8.3.tar.gz", hash = "sha256:67f4eb68288eab41e841cc71a00d26cf6bdda9533022d0189a145a34d0a35f48"}, ] [package.dependencies] @@ -4104,7 +4212,7 @@ installer = ">=0.7.0,<0.8.0" keyring = ">=24.0.0,<25.0.0" packaging = ">=23.1" pexpect = ">=4.7.0,<5.0.0" -pkginfo = ">=1.9.4,<2.0.0" +pkginfo = ">=1.10,<2.0" platformdirs = ">=3.0.0,<5" poetry-core = "1.9.0" poetry-plugin-export = ">=1.6.0,<2.0.0" @@ -4131,18 +4239,18 @@ files = [ [[package]] name = "poetry-plugin-export" -version = "1.7.1" +version = "1.8.0" description = "Poetry plugin to export the dependencies to various formats" optional = false -python-versions = ">=3.8,<4.0" +python-versions = "<4.0,>=3.8" files = [ - {file = "poetry_plugin_export-1.7.1-py3-none-any.whl", hash = "sha256:b2258e53ae0d369a73806f957ed0e726eb95c571a0ce8b1f273da686528cc1da"}, - {file = "poetry_plugin_export-1.7.1.tar.gz", hash = "sha256:cf62cfb6218a904290ba6db3bc1a24aa076d10f81c48c6e48b2ded430131e22e"}, + {file = "poetry_plugin_export-1.8.0-py3-none-any.whl", hash = "sha256:adbe232cfa0cc04991ea3680c865cf748bff27593b9abcb1f35fb50ed7ba2c22"}, + {file = "poetry_plugin_export-1.8.0.tar.gz", hash = "sha256:1fa6168a85d59395d835ca564bc19862a7c76061e60c3e7dfaec70d50937fc61"}, ] [package.dependencies] -poetry = ">=1.8.0,<2.0.0" -poetry-core = ">=1.7.0,<2.0.0" +poetry = ">=1.8.0,<3.0.0" +poetry-core = ">=1.7.0,<3.0.0" [[package]] name = "prometheus-client" @@ -4160,13 +4268,13 @@ twisted = ["twisted"] [[package]] name = "prompt-toolkit" -version = "3.0.43" +version = "3.0.47" description = "Library for building powerful interactive command lines in Python" optional = false python-versions = ">=3.7.0" files = [ - {file = "prompt_toolkit-3.0.43-py3-none-any.whl", hash = "sha256:a11a29cb3bf0a28a387fe5122cdb649816a957cd9261dcedf8c9f1fef33eacf6"}, - {file = "prompt_toolkit-3.0.43.tar.gz", hash = "sha256:3527b7af26106cbc65a040bcc84839a3566ec1b051bb0bfe953631e704b0ff7d"}, + {file = "prompt_toolkit-3.0.47-py3-none-any.whl", hash = "sha256:0d7bfa67001d5e39d02c224b663abc33687405033a8c422d0d675a5a13361d10"}, + {file = "prompt_toolkit-3.0.47.tar.gz", hash = "sha256:1e1b29cb58080b1e69f207c893a1a7bf16d127a5c30c9d17a25a5d77792e5360"}, ] [package.dependencies] @@ -4194,27 +4302,28 @@ files = [ [[package]] name = "psutil" -version = "5.9.8" +version = "6.0.0" description = "Cross-platform lib for process and system monitoring in Python." optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ - {file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"}, - {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"}, - {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"}, - {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"}, - {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"}, - {file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"}, - {file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"}, - {file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"}, - {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"}, - {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"}, - {file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"}, - {file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"}, - {file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"}, - {file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"}, - {file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"}, - {file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"}, + {file = "psutil-6.0.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a021da3e881cd935e64a3d0a20983bda0bb4cf80e4f74fa9bfcb1bc5785360c6"}, + {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1287c2b95f1c0a364d23bc6f2ea2365a8d4d9b726a3be7294296ff7ba97c17f0"}, + {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:a9a3dbfb4de4f18174528d87cc352d1f788b7496991cca33c6996f40c9e3c92c"}, + {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6ec7588fb3ddaec7344a825afe298db83fe01bfaaab39155fa84cf1c0d6b13c3"}, + {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:1e7c870afcb7d91fdea2b37c24aeb08f98b6d67257a5cb0a8bc3ac68d0f1a68c"}, + {file = "psutil-6.0.0-cp27-none-win32.whl", hash = "sha256:02b69001f44cc73c1c5279d02b30a817e339ceb258ad75997325e0e6169d8b35"}, + {file = "psutil-6.0.0-cp27-none-win_amd64.whl", hash = "sha256:21f1fb635deccd510f69f485b87433460a603919b45e2a324ad65b0cc74f8fb1"}, + {file = "psutil-6.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c588a7e9b1173b6e866756dde596fd4cad94f9399daf99ad8c3258b3cb2b47a0"}, + {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ed2440ada7ef7d0d608f20ad89a04ec47d2d3ab7190896cd62ca5fc4fe08bf0"}, + {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd9a97c8e94059b0ef54a7d4baf13b405011176c3b6ff257c247cae0d560ecd"}, + {file = "psutil-6.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e8d0054fc88153ca0544f5c4d554d42e33df2e009c4ff42284ac9ebdef4132"}, + {file = "psutil-6.0.0-cp36-cp36m-win32.whl", hash = "sha256:fc8c9510cde0146432bbdb433322861ee8c3efbf8589865c8bf8d21cb30c4d14"}, + {file = "psutil-6.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:34859b8d8f423b86e4385ff3665d3f4d94be3cdf48221fbe476e883514fdb71c"}, + {file = "psutil-6.0.0-cp37-abi3-win32.whl", hash = "sha256:a495580d6bae27291324fe60cea0b5a7c23fa36a7cd35035a16d93bdcf076b9d"}, + {file = "psutil-6.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:33ea5e1c975250a720b3a6609c490db40dae5d83a4eb315170c4fe0d8b1f34b3"}, + {file = "psutil-6.0.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:ffe7fc9b6b36beadc8c322f84e1caff51e8703b88eee1da46d1e3a6ae11b4fd0"}, + {file = "psutil-6.0.0.tar.gz", hash = "sha256:8faae4f310b6d969fa26ca0545338b21f73c6b15db7c4a8d934a5482faa818f2"}, ] [package.extras] @@ -4257,47 +4366,47 @@ files = [ [[package]] name = "pyarrow" -version = "16.0.0" +version = "16.1.0" description = "Python library for Apache Arrow" optional = false python-versions = ">=3.8" files = [ - {file = "pyarrow-16.0.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:22a1fdb1254e5095d629e29cd1ea98ed04b4bbfd8e42cc670a6b639ccc208b60"}, - {file = "pyarrow-16.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:574a00260a4ed9d118a14770edbd440b848fcae5a3024128be9d0274dbcaf858"}, - {file = "pyarrow-16.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0815d0ddb733b8c1b53a05827a91f1b8bde6240f3b20bf9ba5d650eb9b89cdf"}, - {file = "pyarrow-16.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df0080339387b5d30de31e0a149c0c11a827a10c82f0c67d9afae3981d1aabb7"}, - {file = "pyarrow-16.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:edf38cce0bf0dcf726e074159c60516447e4474904c0033f018c1f33d7dac6c5"}, - {file = "pyarrow-16.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:91d28f9a40f1264eab2af7905a4d95320ac2f287891e9c8b0035f264fe3c3a4b"}, - {file = "pyarrow-16.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:99af421ee451a78884d7faea23816c429e263bd3618b22d38e7992c9ce2a7ad9"}, - {file = "pyarrow-16.0.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:d22d0941e6c7bafddf5f4c0662e46f2075850f1c044bf1a03150dd9e189427ce"}, - {file = "pyarrow-16.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:266ddb7e823f03733c15adc8b5078db2df6980f9aa93d6bb57ece615df4e0ba7"}, - {file = "pyarrow-16.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cc23090224b6594f5a92d26ad47465af47c1d9c079dd4a0061ae39551889efe"}, - {file = "pyarrow-16.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56850a0afe9ef37249d5387355449c0f94d12ff7994af88f16803a26d38f2016"}, - {file = "pyarrow-16.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:705db70d3e2293c2f6f8e84874b5b775f690465798f66e94bb2c07bab0a6bb55"}, - {file = "pyarrow-16.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:5448564754c154997bc09e95a44b81b9e31ae918a86c0fcb35c4aa4922756f55"}, - {file = "pyarrow-16.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:729f7b262aa620c9df8b9967db96c1575e4cfc8c25d078a06968e527b8d6ec05"}, - {file = "pyarrow-16.0.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:fb8065dbc0d051bf2ae2453af0484d99a43135cadabacf0af588a3be81fbbb9b"}, - {file = "pyarrow-16.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:20ce707d9aa390593ea93218b19d0eadab56390311cb87aad32c9a869b0e958c"}, - {file = "pyarrow-16.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5823275c8addbbb50cd4e6a6839952682a33255b447277e37a6f518d6972f4e1"}, - {file = "pyarrow-16.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ab8b9050752b16a8b53fcd9853bf07d8daf19093533e990085168f40c64d978"}, - {file = "pyarrow-16.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:42e56557bc7c5c10d3e42c3b32f6cff649a29d637e8f4e8b311d334cc4326730"}, - {file = "pyarrow-16.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:2a7abdee4a4a7cfa239e2e8d721224c4b34ffe69a0ca7981354fe03c1328789b"}, - {file = "pyarrow-16.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:ef2f309b68396bcc5a354106741d333494d6a0d3e1951271849787109f0229a6"}, - {file = "pyarrow-16.0.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:ed66e5217b4526fa3585b5e39b0b82f501b88a10d36bd0d2a4d8aa7b5a48e2df"}, - {file = "pyarrow-16.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc8814310486f2a73c661ba8354540f17eef51e1b6dd090b93e3419d3a097b3a"}, - {file = "pyarrow-16.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c2f5e239db7ed43e0ad2baf46a6465f89c824cc703f38ef0fde927d8e0955f7"}, - {file = "pyarrow-16.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f293e92d1db251447cb028ae12f7bc47526e4649c3a9924c8376cab4ad6b98bd"}, - {file = "pyarrow-16.0.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:dd9334a07b6dc21afe0857aa31842365a62eca664e415a3f9536e3a8bb832c07"}, - {file = "pyarrow-16.0.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:d91073d1e2fef2c121154680e2ba7e35ecf8d4969cc0af1fa6f14a8675858159"}, - {file = "pyarrow-16.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:71d52561cd7aefd22cf52538f262850b0cc9e4ec50af2aaa601da3a16ef48877"}, - {file = "pyarrow-16.0.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:b93c9a50b965ee0bf4fef65e53b758a7e8dcc0c2d86cebcc037aaaf1b306ecc0"}, - {file = "pyarrow-16.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d831690844706e374c455fba2fb8cfcb7b797bfe53ceda4b54334316e1ac4fa4"}, - {file = "pyarrow-16.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35692ce8ad0b8c666aa60f83950957096d92f2a9d8d7deda93fb835e6053307e"}, - {file = "pyarrow-16.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9dd3151d098e56f16a8389c1247137f9e4c22720b01c6f3aa6dec29a99b74d80"}, - {file = "pyarrow-16.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:bd40467bdb3cbaf2044ed7a6f7f251c8f941c8b31275aaaf88e746c4f3ca4a7a"}, - {file = "pyarrow-16.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:00a1dcb22ad4ceb8af87f7bd30cc3354788776c417f493089e0a0af981bc8d80"}, - {file = "pyarrow-16.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:fda9a7cebd1b1d46c97b511f60f73a5b766a6de4c5236f144f41a5d5afec1f35"}, - {file = "pyarrow-16.0.0.tar.gz", hash = "sha256:59bb1f1edbbf4114c72415f039f1359f1a57d166a331c3229788ccbfbb31689a"}, + {file = "pyarrow-16.1.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:17e23b9a65a70cc733d8b738baa6ad3722298fa0c81d88f63ff94bf25eaa77b9"}, + {file = "pyarrow-16.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4740cc41e2ba5d641071d0ab5e9ef9b5e6e8c7611351a5cb7c1d175eaf43674a"}, + {file = "pyarrow-16.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98100e0268d04e0eec47b73f20b39c45b4006f3c4233719c3848aa27a03c1aef"}, + {file = "pyarrow-16.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f68f409e7b283c085f2da014f9ef81e885d90dcd733bd648cfba3ef265961848"}, + {file = "pyarrow-16.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:a8914cd176f448e09746037b0c6b3a9d7688cef451ec5735094055116857580c"}, + {file = "pyarrow-16.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:48be160782c0556156d91adbdd5a4a7e719f8d407cb46ae3bb4eaee09b3111bd"}, + {file = "pyarrow-16.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:9cf389d444b0f41d9fe1444b70650fea31e9d52cfcb5f818b7888b91b586efff"}, + {file = "pyarrow-16.1.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:d0ebea336b535b37eee9eee31761813086d33ed06de9ab6fc6aaa0bace7b250c"}, + {file = "pyarrow-16.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e73cfc4a99e796727919c5541c65bb88b973377501e39b9842ea71401ca6c1c"}, + {file = "pyarrow-16.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf9251264247ecfe93e5f5a0cd43b8ae834f1e61d1abca22da55b20c788417f6"}, + {file = "pyarrow-16.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddf5aace92d520d3d2a20031d8b0ec27b4395cab9f74e07cc95edf42a5cc0147"}, + {file = "pyarrow-16.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:25233642583bf658f629eb230b9bb79d9af4d9f9229890b3c878699c82f7d11e"}, + {file = "pyarrow-16.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a33a64576fddfbec0a44112eaf844c20853647ca833e9a647bfae0582b2ff94b"}, + {file = "pyarrow-16.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:185d121b50836379fe012753cf15c4ba9638bda9645183ab36246923875f8d1b"}, + {file = "pyarrow-16.1.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:2e51ca1d6ed7f2e9d5c3c83decf27b0d17bb207a7dea986e8dc3e24f80ff7d6f"}, + {file = "pyarrow-16.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06ebccb6f8cb7357de85f60d5da50e83507954af617d7b05f48af1621d331c9a"}, + {file = "pyarrow-16.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b04707f1979815f5e49824ce52d1dceb46e2f12909a48a6a753fe7cafbc44a0c"}, + {file = "pyarrow-16.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d32000693deff8dc5df444b032b5985a48592c0697cb6e3071a5d59888714e2"}, + {file = "pyarrow-16.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:8785bb10d5d6fd5e15d718ee1d1f914fe768bf8b4d1e5e9bf253de8a26cb1628"}, + {file = "pyarrow-16.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:e1369af39587b794873b8a307cc6623a3b1194e69399af0efd05bb202195a5a7"}, + {file = "pyarrow-16.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:febde33305f1498f6df85e8020bca496d0e9ebf2093bab9e0f65e2b4ae2b3444"}, + {file = "pyarrow-16.1.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:b5f5705ab977947a43ac83b52ade3b881eb6e95fcc02d76f501d549a210ba77f"}, + {file = "pyarrow-16.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0d27bf89dfc2576f6206e9cd6cf7a107c9c06dc13d53bbc25b0bd4556f19cf5f"}, + {file = "pyarrow-16.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d07de3ee730647a600037bc1d7b7994067ed64d0eba797ac74b2bc77384f4c2"}, + {file = "pyarrow-16.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fbef391b63f708e103df99fbaa3acf9f671d77a183a07546ba2f2c297b361e83"}, + {file = "pyarrow-16.1.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:19741c4dbbbc986d38856ee7ddfdd6a00fc3b0fc2d928795b95410d38bb97d15"}, + {file = "pyarrow-16.1.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:f2c5fb249caa17b94e2b9278b36a05ce03d3180e6da0c4c3b3ce5b2788f30eed"}, + {file = "pyarrow-16.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:e6b6d3cd35fbb93b70ade1336022cc1147b95ec6af7d36906ca7fe432eb09710"}, + {file = "pyarrow-16.1.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:18da9b76a36a954665ccca8aa6bd9f46c1145f79c0bb8f4f244f5f8e799bca55"}, + {file = "pyarrow-16.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:99f7549779b6e434467d2aa43ab2b7224dd9e41bdde486020bae198978c9e05e"}, + {file = "pyarrow-16.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f07fdffe4fd5b15f5ec15c8b64584868d063bc22b86b46c9695624ca3505b7b4"}, + {file = "pyarrow-16.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddfe389a08ea374972bd4065d5f25d14e36b43ebc22fc75f7b951f24378bf0b5"}, + {file = "pyarrow-16.1.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:3b20bd67c94b3a2ea0a749d2a5712fc845a69cb5d52e78e6449bbd295611f3aa"}, + {file = "pyarrow-16.1.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:ba8ac20693c0bb0bf4b238751d4409e62852004a8cf031c73b0e0962b03e45e3"}, + {file = "pyarrow-16.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:31a1851751433d89a986616015841977e0a188662fcffd1a5677453f1df2de0a"}, + {file = "pyarrow-16.1.0.tar.gz", hash = "sha256:15fbb22ea96d11f0b5768504a3f961edab25eaf4197c341720c4a387f6c60315"}, ] [package.dependencies] @@ -4316,13 +4425,13 @@ files = [ [[package]] name = "pycodestyle" -version = "2.11.1" +version = "2.12.0" description = "Python style guide checker" optional = false python-versions = ">=3.8" files = [ - {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, - {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, + {file = "pycodestyle-2.12.0-py2.py3-none-any.whl", hash = "sha256:949a39f6b86c3e1515ba1787c2022131d165a8ad271b11370a8819aa070269e4"}, + {file = "pycodestyle-2.12.0.tar.gz", hash = "sha256:442f950141b4f43df752dd303511ffded3a04c2b6fb7f65980574f0c31e6e79c"}, ] [[package]] @@ -4338,18 +4447,18 @@ files = [ [[package]] name = "pydantic" -version = "2.7.1" +version = "2.7.4" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.7.1-py3-none-any.whl", hash = "sha256:e029badca45266732a9a79898a15ae2e8b14840b1eabbb25844be28f0b33f3d5"}, - {file = "pydantic-2.7.1.tar.gz", hash = "sha256:e9dbb5eada8abe4d9ae5f46b9939aead650cd2b68f249bb3a8139dbe125803cc"}, + {file = "pydantic-2.7.4-py3-none-any.whl", hash = "sha256:ee8538d41ccb9c0a9ad3e0e5f07bf15ed8015b481ced539a1759d8cc89ae90d0"}, + {file = "pydantic-2.7.4.tar.gz", hash = "sha256:0c84efd9548d545f63ac0060c1e4d39bb9b14db8b3c0652338aecc07b5adec52"}, ] [package.dependencies] annotated-types = ">=0.4.0" -pydantic-core = "2.18.2" +pydantic-core = "2.18.4" typing-extensions = ">=4.6.1" [package.extras] @@ -4357,90 +4466,90 @@ email = ["email-validator (>=2.0.0)"] [[package]] name = "pydantic-core" -version = "2.18.2" +version = "2.18.4" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.18.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9e08e867b306f525802df7cd16c44ff5ebbe747ff0ca6cf3fde7f36c05a59a81"}, - {file = "pydantic_core-2.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f0a21cbaa69900cbe1a2e7cad2aa74ac3cf21b10c3efb0fa0b80305274c0e8a2"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0680b1f1f11fda801397de52c36ce38ef1c1dc841a0927a94f226dea29c3ae3d"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:95b9d5e72481d3780ba3442eac863eae92ae43a5f3adb5b4d0a1de89d42bb250"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fcf5cd9c4b655ad666ca332b9a081112cd7a58a8b5a6ca7a3104bc950f2038"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b5155ff768083cb1d62f3e143b49a8a3432e6789a3abee8acd005c3c7af1c74"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553ef617b6836fc7e4df130bb851e32fe357ce36336d897fd6646d6058d980af"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89ed9eb7d616ef5714e5590e6cf7f23b02d0d539767d33561e3675d6f9e3857"}, - {file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:75f7e9488238e920ab6204399ded280dc4c307d034f3924cd7f90a38b1829563"}, - {file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ef26c9e94a8c04a1b2924149a9cb081836913818e55681722d7f29af88fe7b38"}, - {file = "pydantic_core-2.18.2-cp310-none-win32.whl", hash = "sha256:182245ff6b0039e82b6bb585ed55a64d7c81c560715d1bad0cbad6dfa07b4027"}, - {file = "pydantic_core-2.18.2-cp310-none-win_amd64.whl", hash = "sha256:e23ec367a948b6d812301afc1b13f8094ab7b2c280af66ef450efc357d2ae543"}, - {file = "pydantic_core-2.18.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:219da3f096d50a157f33645a1cf31c0ad1fe829a92181dd1311022f986e5fbe3"}, - {file = "pydantic_core-2.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cc1cfd88a64e012b74e94cd00bbe0f9c6df57049c97f02bb07d39e9c852e19a4"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b7133a6e6aeb8df37d6f413f7705a37ab4031597f64ab56384c94d98fa0e90"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:224c421235f6102e8737032483f43c1a8cfb1d2f45740c44166219599358c2cd"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b14d82cdb934e99dda6d9d60dc84a24379820176cc4a0d123f88df319ae9c150"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2728b01246a3bba6de144f9e3115b532ee44bd6cf39795194fb75491824a1413"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:470b94480bb5ee929f5acba6995251ada5e059a5ef3e0dfc63cca287283ebfa6"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:997abc4df705d1295a42f95b4eec4950a37ad8ae46d913caeee117b6b198811c"}, - {file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75250dbc5290e3f1a0f4618db35e51a165186f9034eff158f3d490b3fed9f8a0"}, - {file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4456f2dca97c425231d7315737d45239b2b51a50dc2b6f0c2bb181fce6207664"}, - {file = "pydantic_core-2.18.2-cp311-none-win32.whl", hash = "sha256:269322dcc3d8bdb69f054681edff86276b2ff972447863cf34c8b860f5188e2e"}, - {file = "pydantic_core-2.18.2-cp311-none-win_amd64.whl", hash = "sha256:800d60565aec896f25bc3cfa56d2277d52d5182af08162f7954f938c06dc4ee3"}, - {file = "pydantic_core-2.18.2-cp311-none-win_arm64.whl", hash = "sha256:1404c69d6a676245199767ba4f633cce5f4ad4181f9d0ccb0577e1f66cf4c46d"}, - {file = "pydantic_core-2.18.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:fb2bd7be70c0fe4dfd32c951bc813d9fe6ebcbfdd15a07527796c8204bd36242"}, - {file = "pydantic_core-2.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6132dd3bd52838acddca05a72aafb6eab6536aa145e923bb50f45e78b7251043"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d904828195733c183d20a54230c0df0eb46ec746ea1a666730787353e87182"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c9bd70772c720142be1020eac55f8143a34ec9f82d75a8e7a07852023e46617f"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8ed04b3582771764538f7ee7001b02e1170223cf9b75dff0bc698fadb00cf3"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6dac87ddb34aaec85f873d737e9d06a3555a1cc1a8e0c44b7f8d5daeb89d86f"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca4ae5a27ad7a4ee5170aebce1574b375de390bc01284f87b18d43a3984df72"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:886eec03591b7cf058467a70a87733b35f44707bd86cf64a615584fd72488b7c"}, - {file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ca7b0c1f1c983e064caa85f3792dd2fe3526b3505378874afa84baf662e12241"}, - {file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b4356d3538c3649337df4074e81b85f0616b79731fe22dd11b99499b2ebbdf3"}, - {file = "pydantic_core-2.18.2-cp312-none-win32.whl", hash = "sha256:8b172601454f2d7701121bbec3425dd71efcb787a027edf49724c9cefc14c038"}, - {file = "pydantic_core-2.18.2-cp312-none-win_amd64.whl", hash = "sha256:b1bd7e47b1558ea872bd16c8502c414f9e90dcf12f1395129d7bb42a09a95438"}, - {file = "pydantic_core-2.18.2-cp312-none-win_arm64.whl", hash = "sha256:98758d627ff397e752bc339272c14c98199c613f922d4a384ddc07526c86a2ec"}, - {file = "pydantic_core-2.18.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:9fdad8e35f278b2c3eb77cbdc5c0a49dada440657bf738d6905ce106dc1de439"}, - {file = "pydantic_core-2.18.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1d90c3265ae107f91a4f279f4d6f6f1d4907ac76c6868b27dc7fb33688cfb347"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390193c770399861d8df9670fb0d1874f330c79caaca4642332df7c682bf6b91"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:82d5d4d78e4448683cb467897fe24e2b74bb7b973a541ea1dcfec1d3cbce39fb"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4774f3184d2ef3e14e8693194f661dea5a4d6ca4e3dc8e39786d33a94865cefd"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4d938ec0adf5167cb335acb25a4ee69a8107e4984f8fbd2e897021d9e4ca21b"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0e8b1be28239fc64a88a8189d1df7fad8be8c1ae47fcc33e43d4be15f99cc70"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:868649da93e5a3d5eacc2b5b3b9235c98ccdbfd443832f31e075f54419e1b96b"}, - {file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:78363590ef93d5d226ba21a90a03ea89a20738ee5b7da83d771d283fd8a56761"}, - {file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:852e966fbd035a6468fc0a3496589b45e2208ec7ca95c26470a54daed82a0788"}, - {file = "pydantic_core-2.18.2-cp38-none-win32.whl", hash = "sha256:6a46e22a707e7ad4484ac9ee9f290f9d501df45954184e23fc29408dfad61350"}, - {file = "pydantic_core-2.18.2-cp38-none-win_amd64.whl", hash = "sha256:d91cb5ea8b11607cc757675051f61b3d93f15eca3cefb3e6c704a5d6e8440f4e"}, - {file = "pydantic_core-2.18.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ae0a8a797a5e56c053610fa7be147993fe50960fa43609ff2a9552b0e07013e8"}, - {file = "pydantic_core-2.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:042473b6280246b1dbf530559246f6842b56119c2926d1e52b631bdc46075f2a"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a388a77e629b9ec814c1b1e6b3b595fe521d2cdc625fcca26fbc2d44c816804"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25add29b8f3b233ae90ccef2d902d0ae0432eb0d45370fe315d1a5cf231004b"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f459a5ce8434614dfd39bbebf1041952ae01da6bed9855008cb33b875cb024c0"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eff2de745698eb46eeb51193a9f41d67d834d50e424aef27df2fcdee1b153845"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8309f67285bdfe65c372ea3722b7a5642680f3dba538566340a9d36e920b5f0"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f93a8a2e3938ff656a7c1bc57193b1319960ac015b6e87d76c76bf14fe0244b4"}, - {file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:22057013c8c1e272eb8d0eebc796701167d8377441ec894a8fed1af64a0bf399"}, - {file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfeecd1ac6cc1fb2692c3d5110781c965aabd4ec5d32799773ca7b1456ac636b"}, - {file = "pydantic_core-2.18.2-cp39-none-win32.whl", hash = "sha256:0d69b4c2f6bb3e130dba60d34c0845ba31b69babdd3f78f7c0c8fae5021a253e"}, - {file = "pydantic_core-2.18.2-cp39-none-win_amd64.whl", hash = "sha256:d9319e499827271b09b4e411905b24a426b8fb69464dfa1696258f53a3334641"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a1874c6dd4113308bd0eb568418e6114b252afe44319ead2b4081e9b9521fe75"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ccdd111c03bfd3666bd2472b674c6899550e09e9f298954cfc896ab92b5b0e6d"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e18609ceaa6eed63753037fc06ebb16041d17d28199ae5aba0052c51449650a9"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e5c584d357c4e2baf0ff7baf44f4994be121e16a2c88918a5817331fc7599d7"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43f0f463cf89ace478de71a318b1b4f05ebc456a9b9300d027b4b57c1a2064fb"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e1b395e58b10b73b07b7cf740d728dd4ff9365ac46c18751bf8b3d8cca8f625a"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0098300eebb1c837271d3d1a2cd2911e7c11b396eac9661655ee524a7f10587b"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:36789b70d613fbac0a25bb07ab3d9dba4d2e38af609c020cf4d888d165ee0bf3"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3f9a801e7c8f1ef8718da265bba008fa121243dfe37c1cea17840b0944dfd72c"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:3a6515ebc6e69d85502b4951d89131ca4e036078ea35533bb76327f8424531ce"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20aca1e2298c56ececfd8ed159ae4dde2df0781988c97ef77d5c16ff4bd5b400"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:223ee893d77a310a0391dca6df00f70bbc2f36a71a895cecd9a0e762dc37b349"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2334ce8c673ee93a1d6a65bd90327588387ba073c17e61bf19b4fd97d688d63c"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cbca948f2d14b09d20268cda7b0367723d79063f26c4ffc523af9042cad95592"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b3ef08e20ec49e02d5c6717a91bb5af9b20f1805583cb0adfe9ba2c6b505b5ae"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6fdc8627910eed0c01aed6a390a252fe3ea6d472ee70fdde56273f198938374"}, - {file = "pydantic_core-2.18.2.tar.gz", hash = "sha256:2e29d20810dfc3043ee13ac7d9e25105799817683348823f305ab3f349b9386e"}, + {file = "pydantic_core-2.18.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f76d0ad001edd426b92233d45c746fd08f467d56100fd8f30e9ace4b005266e4"}, + {file = "pydantic_core-2.18.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:59ff3e89f4eaf14050c8022011862df275b552caef8082e37b542b066ce1ff26"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a55b5b16c839df1070bc113c1f7f94a0af4433fcfa1b41799ce7606e5c79ce0a"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4d0dcc59664fcb8974b356fe0a18a672d6d7cf9f54746c05f43275fc48636851"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8951eee36c57cd128f779e641e21eb40bc5073eb28b2d23f33eb0ef14ffb3f5d"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4701b19f7e3a06ea655513f7938de6f108123bf7c86bbebb1196eb9bd35cf724"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e00a3f196329e08e43d99b79b286d60ce46bed10f2280d25a1718399457e06be"}, + {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:97736815b9cc893b2b7f663628e63f436018b75f44854c8027040e05230eeddb"}, + {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6891a2ae0e8692679c07728819b6e2b822fb30ca7445f67bbf6509b25a96332c"}, + {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bc4ff9805858bd54d1a20efff925ccd89c9d2e7cf4986144b30802bf78091c3e"}, + {file = "pydantic_core-2.18.4-cp310-none-win32.whl", hash = "sha256:1b4de2e51bbcb61fdebd0ab86ef28062704f62c82bbf4addc4e37fa4b00b7cbc"}, + {file = "pydantic_core-2.18.4-cp310-none-win_amd64.whl", hash = "sha256:6a750aec7bf431517a9fd78cb93c97b9b0c496090fee84a47a0d23668976b4b0"}, + {file = "pydantic_core-2.18.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:942ba11e7dfb66dc70f9ae66b33452f51ac7bb90676da39a7345e99ffb55402d"}, + {file = "pydantic_core-2.18.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b2ebef0e0b4454320274f5e83a41844c63438fdc874ea40a8b5b4ecb7693f1c4"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a642295cd0c8df1b86fc3dced1d067874c353a188dc8e0f744626d49e9aa51c4"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f09baa656c904807e832cf9cce799c6460c450c4ad80803517032da0cd062e2"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98906207f29bc2c459ff64fa007afd10a8c8ac080f7e4d5beff4c97086a3dabd"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19894b95aacfa98e7cb093cd7881a0c76f55731efad31073db4521e2b6ff5b7d"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fbbdc827fe5e42e4d196c746b890b3d72876bdbf160b0eafe9f0334525119c8"}, + {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f85d05aa0918283cf29a30b547b4df2fbb56b45b135f9e35b6807cb28bc47951"}, + {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e85637bc8fe81ddb73fda9e56bab24560bdddfa98aa64f87aaa4e4b6730c23d2"}, + {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2f5966897e5461f818e136b8451d0551a2e77259eb0f73a837027b47dc95dab9"}, + {file = "pydantic_core-2.18.4-cp311-none-win32.whl", hash = "sha256:44c7486a4228413c317952e9d89598bcdfb06399735e49e0f8df643e1ccd0558"}, + {file = "pydantic_core-2.18.4-cp311-none-win_amd64.whl", hash = "sha256:8a7164fe2005d03c64fd3b85649891cd4953a8de53107940bf272500ba8a788b"}, + {file = "pydantic_core-2.18.4-cp311-none-win_arm64.whl", hash = "sha256:4e99bc050fe65c450344421017f98298a97cefc18c53bb2f7b3531eb39bc7805"}, + {file = "pydantic_core-2.18.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6f5c4d41b2771c730ea1c34e458e781b18cc668d194958e0112455fff4e402b2"}, + {file = "pydantic_core-2.18.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2fdf2156aa3d017fddf8aea5adfba9f777db1d6022d392b682d2a8329e087cef"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4748321b5078216070b151d5271ef3e7cc905ab170bbfd27d5c83ee3ec436695"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847a35c4d58721c5dc3dba599878ebbdfd96784f3fb8bb2c356e123bdcd73f34"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c40d4eaad41f78e3bbda31b89edc46a3f3dc6e171bf0ecf097ff7a0ffff7cb1"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:21a5e440dbe315ab9825fcd459b8814bb92b27c974cbc23c3e8baa2b76890077"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01dd777215e2aa86dfd664daed5957704b769e726626393438f9c87690ce78c3"}, + {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4b06beb3b3f1479d32befd1f3079cc47b34fa2da62457cdf6c963393340b56e9"}, + {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:564d7922e4b13a16b98772441879fcdcbe82ff50daa622d681dd682175ea918c"}, + {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0eb2a4f660fcd8e2b1c90ad566db2b98d7f3f4717c64fe0a83e0adb39766d5b8"}, + {file = "pydantic_core-2.18.4-cp312-none-win32.whl", hash = "sha256:8b8bab4c97248095ae0c4455b5a1cd1cdd96e4e4769306ab19dda135ea4cdb07"}, + {file = "pydantic_core-2.18.4-cp312-none-win_amd64.whl", hash = "sha256:14601cdb733d741b8958224030e2bfe21a4a881fb3dd6fbb21f071cabd48fa0a"}, + {file = "pydantic_core-2.18.4-cp312-none-win_arm64.whl", hash = "sha256:c1322d7dd74713dcc157a2b7898a564ab091ca6c58302d5c7b4c07296e3fd00f"}, + {file = "pydantic_core-2.18.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:823be1deb01793da05ecb0484d6c9e20baebb39bd42b5d72636ae9cf8350dbd2"}, + {file = "pydantic_core-2.18.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ebef0dd9bf9b812bf75bda96743f2a6c5734a02092ae7f721c048d156d5fabae"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae1d6df168efb88d7d522664693607b80b4080be6750c913eefb77e34c12c71a"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f9899c94762343f2cc2fc64c13e7cae4c3cc65cdfc87dd810a31654c9b7358cc"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99457f184ad90235cfe8461c4d70ab7dd2680e28821c29eca00252ba90308c78"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18f469a3d2a2fdafe99296a87e8a4c37748b5080a26b806a707f25a902c040a8"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7cdf28938ac6b8b49ae5e92f2735056a7ba99c9b110a474473fd71185c1af5d"}, + {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:938cb21650855054dc54dfd9120a851c974f95450f00683399006aa6e8abb057"}, + {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:44cd83ab6a51da80fb5adbd9560e26018e2ac7826f9626bc06ca3dc074cd198b"}, + {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:972658f4a72d02b8abfa2581d92d59f59897d2e9f7e708fdabe922f9087773af"}, + {file = "pydantic_core-2.18.4-cp38-none-win32.whl", hash = "sha256:1d886dc848e60cb7666f771e406acae54ab279b9f1e4143babc9c2258213daa2"}, + {file = "pydantic_core-2.18.4-cp38-none-win_amd64.whl", hash = "sha256:bb4462bd43c2460774914b8525f79b00f8f407c945d50881568f294c1d9b4443"}, + {file = "pydantic_core-2.18.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:44a688331d4a4e2129140a8118479443bd6f1905231138971372fcde37e43528"}, + {file = "pydantic_core-2.18.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a2fdd81edd64342c85ac7cf2753ccae0b79bf2dfa063785503cb85a7d3593223"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86110d7e1907ab36691f80b33eb2da87d780f4739ae773e5fc83fb272f88825f"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46387e38bd641b3ee5ce247563b60c5ca098da9c56c75c157a05eaa0933ed154"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:123c3cec203e3f5ac7b000bd82235f1a3eced8665b63d18be751f115588fea30"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc1803ac5c32ec324c5261c7209e8f8ce88e83254c4e1aebdc8b0a39f9ddb443"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53db086f9f6ab2b4061958d9c276d1dbe3690e8dd727d6abf2321d6cce37fa94"}, + {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:abc267fa9837245cc28ea6929f19fa335f3dc330a35d2e45509b6566dc18be23"}, + {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a0d829524aaefdebccb869eed855e2d04c21d2d7479b6cada7ace5448416597b"}, + {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:509daade3b8649f80d4e5ff21aa5673e4ebe58590b25fe42fac5f0f52c6f034a"}, + {file = "pydantic_core-2.18.4-cp39-none-win32.whl", hash = "sha256:ca26a1e73c48cfc54c4a76ff78df3727b9d9f4ccc8dbee4ae3f73306a591676d"}, + {file = "pydantic_core-2.18.4-cp39-none-win_amd64.whl", hash = "sha256:c67598100338d5d985db1b3d21f3619ef392e185e71b8d52bceacc4a7771ea7e"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:574d92eac874f7f4db0ca653514d823a0d22e2354359d0759e3f6a406db5d55d"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1f4d26ceb5eb9eed4af91bebeae4b06c3fb28966ca3a8fb765208cf6b51102ab"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77450e6d20016ec41f43ca4a6c63e9fdde03f0ae3fe90e7c27bdbeaece8b1ed4"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d323a01da91851a4f17bf592faf46149c9169d68430b3146dcba2bb5e5719abc"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43d447dd2ae072a0065389092a231283f62d960030ecd27565672bd40746c507"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:578e24f761f3b425834f297b9935e1ce2e30f51400964ce4801002435a1b41ef"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:81b5efb2f126454586d0f40c4d834010979cb80785173d1586df845a632e4e6d"}, + {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ab86ce7c8f9bea87b9d12c7f0af71102acbf5ecbc66c17796cff45dae54ef9a5"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:90afc12421df2b1b4dcc975f814e21bc1754640d502a2fbcc6d41e77af5ec312"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:51991a89639a912c17bef4b45c87bd83593aee0437d8102556af4885811d59f5"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:293afe532740370aba8c060882f7d26cfd00c94cae32fd2e212a3a6e3b7bc15e"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b48ece5bde2e768197a2d0f6e925f9d7e3e826f0ad2271120f8144a9db18d5c8"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eae237477a873ab46e8dd748e515c72c0c804fb380fbe6c85533c7de51f23a8f"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:834b5230b5dfc0c1ec37b2fda433b271cbbc0e507560b5d1588e2cc1148cf1ce"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e858ac0a25074ba4bce653f9b5d0a85b7456eaddadc0ce82d3878c22489fa4ee"}, + {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2fd41f6eff4c20778d717af1cc50eca52f5afe7805ee530a4fbd0bae284f16e9"}, + {file = "pydantic_core-2.18.4.tar.gz", hash = "sha256:ec3beeada09ff865c344ff3bc2f427f5e6c26401cc6113d77e372c3fdac73864"}, ] [package.dependencies] @@ -4475,13 +4584,13 @@ test = ["pytest", "pytest-cov", "pytest-regressions"] [[package]] name = "pydeck" -version = "0.9.0" +version = "0.9.1" description = "Widget for deck.gl maps" optional = false python-versions = ">=3.8" files = [ - {file = "pydeck-0.9.0-py2.py3-none-any.whl", hash = "sha256:0ab6e657210919fed2992cfad20afb4135d67ab6d8374f0b06fcc82b4e77b055"}, - {file = "pydeck-0.9.0.tar.gz", hash = "sha256:fd8b75f61ca0d4712d32b4e073440f1cb74f2e0c16a0b57e57a00e65c563cc54"}, + {file = "pydeck-0.9.1-py2.py3-none-any.whl", hash = "sha256:b3f75ba0d273fc917094fa61224f3f6076ca8752b93d46faf3bcfd9f9d59b038"}, + {file = "pydeck-0.9.1.tar.gz", hash = "sha256:f74475ae637951d63f2ee58326757f8d4f9cd9f2a457cf42950715003e2cb605"}, ] [package.dependencies] @@ -4519,71 +4628,71 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pymongo" -version = "4.7.2" +version = "4.7.3" description = "Python driver for MongoDB " optional = true python-versions = ">=3.7" files = [ - {file = "pymongo-4.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:268d8578c0500012140c5460755ea405cbfe541ef47c81efa9d6744f0f99aeca"}, - {file = "pymongo-4.7.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:827611beb6c483260d520cfa6a49662d980dfa5368a04296f65fa39e78fccea7"}, - {file = "pymongo-4.7.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a754e366c404d19ff3f077ddeed64be31e0bb515e04f502bf11987f1baa55a16"}, - {file = "pymongo-4.7.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c44efab10d9a3db920530f7bcb26af8f408b7273d2f0214081d3891979726328"}, - {file = "pymongo-4.7.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35b3f0c7d49724859d4df5f0445818d525824a6cd55074c42573d9b50764df67"}, - {file = "pymongo-4.7.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e37faf298a37ffb3e0809e77fbbb0a32b6a2d18a83c59cfc2a7b794ea1136b0"}, - {file = "pymongo-4.7.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1bcd58669e56c08f1e72c5758868b5df169fe267501c949ee83c418e9df9155"}, - {file = "pymongo-4.7.2-cp310-cp310-win32.whl", hash = "sha256:c72d16fede22efe7cdd1f422e8da15760e9498024040429362886f946c10fe95"}, - {file = "pymongo-4.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:12d1fef77d25640cb78893d07ff7d2fac4c4461d8eec45bd3b9ad491a1115d6e"}, - {file = "pymongo-4.7.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fc5af24fcf5fc6f7f40d65446400d45dd12bea933d0299dc9e90c5b22197f1e9"}, - {file = "pymongo-4.7.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:730778b6f0964b164c187289f906bbc84cb0524df285b7a85aa355bbec43eb21"}, - {file = "pymongo-4.7.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47a1a4832ef2f4346dcd1a10a36ade7367ad6905929ddb476459abb4fd1b98cb"}, - {file = "pymongo-4.7.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6eab12c6385526d386543d6823b07187fefba028f0da216506e00f0e1855119"}, - {file = "pymongo-4.7.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37e9ea81fa59ee9274457ed7d59b6c27f6f2a5fe8e26f184ecf58ea52a019cb8"}, - {file = "pymongo-4.7.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e9d9d2c0aae73aa4369bd373ac2ac59f02c46d4e56c4b6d6e250cfe85f76802"}, - {file = "pymongo-4.7.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb6e00a79dff22c9a72212ad82021b54bdb3b85f38a85f4fc466bde581d7d17a"}, - {file = "pymongo-4.7.2-cp311-cp311-win32.whl", hash = "sha256:02efd1bb3397e24ef2af45923888b41a378ce00cb3a4259c5f4fc3c70497a22f"}, - {file = "pymongo-4.7.2-cp311-cp311-win_amd64.whl", hash = "sha256:87bb453ac3eb44db95cb6d5a616fbc906c1c00661eec7f55696253a6245beb8a"}, - {file = "pymongo-4.7.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:12c466e02133b7f8f4ff1045c6b5916215c5f7923bc83fd6e28e290cba18f9f6"}, - {file = "pymongo-4.7.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f91073049c43d14e66696970dd708d319b86ee57ef9af359294eee072abaac79"}, - {file = "pymongo-4.7.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87032f818bf5052ab742812c715eff896621385c43f8f97cdd37d15b5d394e95"}, - {file = "pymongo-4.7.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6a87eef394039765679f75c6a47455a4030870341cb76eafc349c5944408c882"}, - {file = "pymongo-4.7.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d275596f840018858757561840767b39272ac96436fcb54f5cac6d245393fd97"}, - {file = "pymongo-4.7.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82102e353be13f1a6769660dd88115b1da382447672ba1c2662a0fbe3df1d861"}, - {file = "pymongo-4.7.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:194065c9d445017b3c82fb85f89aa2055464a080bde604010dc8eb932a6b3c95"}, - {file = "pymongo-4.7.2-cp312-cp312-win32.whl", hash = "sha256:db4380d1e69fdad1044a4b8f3bb105200542c49a0dde93452d938ff9db1d6d29"}, - {file = "pymongo-4.7.2-cp312-cp312-win_amd64.whl", hash = "sha256:fadc6e8db7707c861ebe25b13ad6aca19ea4d2c56bf04a26691f46c23dadf6e4"}, - {file = "pymongo-4.7.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2cb77d09bd012cb4b30636e7e38d00b5f9be5eb521c364bde66490c45ee6c4b4"}, - {file = "pymongo-4.7.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56bf8b706946952acdea0fe478f8e44f1ed101c4b87f046859e6c3abe6c0a9f4"}, - {file = "pymongo-4.7.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bcf337d1b252405779d9c79978d6ca15eab3cdaa2f44c100a79221bddad97c8a"}, - {file = "pymongo-4.7.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ffd1519edbe311df73c74ec338de7d294af535b2748191c866ea3a7c484cd15"}, - {file = "pymongo-4.7.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d59776f435564159196d971aa89422ead878174aff8fe18e06d9a0bc6d648c"}, - {file = "pymongo-4.7.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:347c49cf7f0ba49ea87c1a5a1984187ecc5516b7c753f31938bf7b37462824fd"}, - {file = "pymongo-4.7.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:84bc00200c3cbb6c98a2bb964c9e8284b641e4a33cf10c802390552575ee21de"}, - {file = "pymongo-4.7.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:fcaf8c911cb29316a02356f89dbc0e0dfcc6a712ace217b6b543805690d2aefd"}, - {file = "pymongo-4.7.2-cp37-cp37m-win32.whl", hash = "sha256:b48a5650ee5320d59f6d570bd99a8d5c58ac6f297a4e9090535f6561469ac32e"}, - {file = "pymongo-4.7.2-cp37-cp37m-win_amd64.whl", hash = "sha256:5239ef7e749f1326ea7564428bf861d5250aa39d7f26d612741b1b1273227062"}, - {file = "pymongo-4.7.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2dcf608d35644e8d276d61bf40a93339d8d66a0e5f3e3f75b2c155a421a1b71"}, - {file = "pymongo-4.7.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:25eeb2c18ede63891cbd617943dd9e6b9cbccc54f276e0b2e693a0cc40f243c5"}, - {file = "pymongo-4.7.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9349f0bb17a31371d4cacb64b306e4ca90413a3ad1fffe73ac7cd495570d94b5"}, - {file = "pymongo-4.7.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ffd4d7cb2e6c6e100e2b39606d38a9ffc934e18593dc9bb326196afc7d93ce3d"}, - {file = "pymongo-4.7.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9a8bd37f5dabc86efceb8d8cbff5969256523d42d08088f098753dba15f3b37a"}, - {file = "pymongo-4.7.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c78f156edc59b905c80c9003e022e1a764c54fd40ac4fea05b0764f829790e2"}, - {file = "pymongo-4.7.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9d892fb91e81cccb83f507cdb2ea0aa026ec3ced7f12a1d60f6a5bf0f20f9c1f"}, - {file = "pymongo-4.7.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:87832d6076c2c82f42870157414fd876facbb6554d2faf271ffe7f8f30ce7bed"}, - {file = "pymongo-4.7.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ce1a374ea0e49808e0380ffc64284c0ce0f12bd21042b4bef1af3eb7bdf49054"}, - {file = "pymongo-4.7.2-cp38-cp38-win32.whl", hash = "sha256:eb0642e5f0dd7e86bb358749cc278e70b911e617f519989d346f742dc9520dfb"}, - {file = "pymongo-4.7.2-cp38-cp38-win_amd64.whl", hash = "sha256:4bdb5ffe1cd3728c9479671a067ef44dacafc3743741d4dc700c377c4231356f"}, - {file = "pymongo-4.7.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:743552033c63f0afdb56b9189ab04b5c1dbffd7310cf7156ab98eebcecf24621"}, - {file = "pymongo-4.7.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5239776633f7578b81207e5646245415a5a95f6ae5ef5dff8e7c2357e6264bfc"}, - {file = "pymongo-4.7.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:727ad07952c155cd20045f2ce91143c7dc4fb01a5b4e8012905a89a7da554b0c"}, - {file = "pymongo-4.7.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9385654f01a90f73827af4db90c290a1519f7d9102ba43286e187b373e9a78e9"}, - {file = "pymongo-4.7.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d833651f1ba938bb7501f13e326b96cfbb7d98867b2d545ca6d69c7664903e0"}, - {file = "pymongo-4.7.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf17ea9cea14d59b0527403dd7106362917ced7c4ec936c4ba22bd36c912c8e0"}, - {file = "pymongo-4.7.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cecd2df037249d1c74f0af86fb5b766104a5012becac6ff63d85d1de53ba8b98"}, - {file = "pymongo-4.7.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65b4c00dedbd333698b83cd2095a639a6f0d7c4e2a617988f6c65fb46711f028"}, - {file = "pymongo-4.7.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d9b6cbc037108ff1a0a867e7670d8513c37f9bcd9ee3d2464411bfabf70ca002"}, - {file = "pymongo-4.7.2-cp39-cp39-win32.whl", hash = "sha256:cf28430ec1924af1bffed37b69a812339084697fd3f3e781074a0148e6475803"}, - {file = "pymongo-4.7.2-cp39-cp39-win_amd64.whl", hash = "sha256:e004527ea42a6b99a8b8d5b42b42762c3bdf80f88fbdb5c3a9d47f3808495b86"}, - {file = "pymongo-4.7.2.tar.gz", hash = "sha256:9024e1661c6e40acf468177bf90ce924d1bc681d2b244adda3ed7b2f4c4d17d7"}, + {file = "pymongo-4.7.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e9580b4537b3cc5d412070caabd1dabdf73fdce249793598792bac5782ecf2eb"}, + {file = "pymongo-4.7.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:517243b2b189c98004570dd8fc0e89b1a48363d5578b3b99212fa2098b2ea4b8"}, + {file = "pymongo-4.7.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23b1e9dabd61da1c7deb54d888f952f030e9e35046cebe89309b28223345b3d9"}, + {file = "pymongo-4.7.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03e0f9901ad66c6fb7da0d303461377524d61dab93a4e4e5af44164c5bb4db76"}, + {file = "pymongo-4.7.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9a870824aa54453aee030bac08c77ebcf2fe8999400f0c2a065bebcbcd46b7f8"}, + {file = "pymongo-4.7.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfd7b3d3f4261bddbb74a332d87581bc523353e62bb9da4027cc7340f6fcbebc"}, + {file = "pymongo-4.7.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4d719a643ea6da46d215a3ba51dac805a773b611c641319558d8576cbe31cef8"}, + {file = "pymongo-4.7.3-cp310-cp310-win32.whl", hash = "sha256:d8b1e06f361f3c66ee694cb44326e1a2e4f93bc9c3a4849ae8547889fca71154"}, + {file = "pymongo-4.7.3-cp310-cp310-win_amd64.whl", hash = "sha256:c450ab2f9397e2d5caa7fddeb4feb30bf719c47c13ae02c0bbb3b71bf4099c1c"}, + {file = "pymongo-4.7.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79cc6459209e885ba097779eaa0fe7f2fa049db39ab43b1731cf8d065a4650e8"}, + {file = "pymongo-4.7.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6e2287f1e2cc35e73cd74a4867e398a97962c5578a3991c730ef78d276ca8e46"}, + {file = "pymongo-4.7.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:413506bd48d8c31ee100645192171e4773550d7cb940b594d5175ac29e329ea1"}, + {file = "pymongo-4.7.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cc1febf17646d52b7561caa762f60bdfe2cbdf3f3e70772f62eb624269f9c05"}, + {file = "pymongo-4.7.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8dfcf18a49955d50a16c92b39230bd0668ffc9c164ccdfe9d28805182b48fa72"}, + {file = "pymongo-4.7.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89872041196c008caddf905eb59d3dc2d292ae6b0282f1138418e76f3abd3ad6"}, + {file = "pymongo-4.7.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3ed97b89de62ea927b672ad524de0d23f3a6b4a01c8d10e3d224abec973fbc3"}, + {file = "pymongo-4.7.3-cp311-cp311-win32.whl", hash = "sha256:d2f52b38151e946011d888a8441d3d75715c663fc5b41a7ade595e924e12a90a"}, + {file = "pymongo-4.7.3-cp311-cp311-win_amd64.whl", hash = "sha256:4a4cc91c28e81c0ce03d3c278e399311b0af44665668a91828aec16527082676"}, + {file = "pymongo-4.7.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cb30c8a78f5ebaca98640943447b6a0afcb146f40b415757c9047bf4a40d07b4"}, + {file = "pymongo-4.7.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9cf2069f5d37c398186453589486ea98bb0312214c439f7d320593b61880dc05"}, + {file = "pymongo-4.7.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3564f423958fced8a8c90940fd2f543c27adbcd6c7c6ed6715d847053f6200a0"}, + {file = "pymongo-4.7.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7a8af8a38fa6951fff73e6ff955a6188f829b29fed7c5a1b739a306b4aa56fe8"}, + {file = "pymongo-4.7.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a0e81c8dba6d825272867d487f18764cfed3c736d71d7d4ff5b79642acbed42"}, + {file = "pymongo-4.7.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88fc1d146feabac4385ea8ddb1323e584922922641303c8bf392fe1c36803463"}, + {file = "pymongo-4.7.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4225100b2c5d1f7393d7c5d256ceb8b20766830eecf869f8ae232776347625a6"}, + {file = "pymongo-4.7.3-cp312-cp312-win32.whl", hash = "sha256:5f3569ed119bf99c0f39ac9962fb5591eff02ca210fe80bb5178d7a1171c1b1e"}, + {file = "pymongo-4.7.3-cp312-cp312-win_amd64.whl", hash = "sha256:eb383c54c0c8ba27e7712b954fcf2a0905fee82a929d277e2e94ad3a5ba3c7db"}, + {file = "pymongo-4.7.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a46cffe91912570151617d866a25d07b9539433a32231ca7e7cf809b6ba1745f"}, + {file = "pymongo-4.7.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c3cba427dac50944c050c96d958c5e643c33a457acee03bae27c8990c5b9c16"}, + {file = "pymongo-4.7.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7a5fd893edbeb7fa982f8d44b6dd0186b6cd86c89e23f6ef95049ff72bffe46"}, + {file = "pymongo-4.7.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c168a2fadc8b19071d0a9a4f85fe38f3029fe22163db04b4d5c046041c0b14bd"}, + {file = "pymongo-4.7.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c59c2c9e70f63a7f18a31e367898248c39c068c639b0579623776f637e8f482"}, + {file = "pymongo-4.7.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d08165fd82c89d372e82904c3268bd8fe5de44f92a00e97bb1db1785154397d9"}, + {file = "pymongo-4.7.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:397fed21afec4fdaecf72f9c4344b692e489756030a9c6d864393e00c7e80491"}, + {file = "pymongo-4.7.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:f903075f8625e2d228f1b9b9a0cf1385f1c41e93c03fd7536c91780a0fb2e98f"}, + {file = "pymongo-4.7.3-cp37-cp37m-win32.whl", hash = "sha256:8ed1132f58c38add6b6138b771d0477a3833023c015c455d9a6e26f367f9eb5c"}, + {file = "pymongo-4.7.3-cp37-cp37m-win_amd64.whl", hash = "sha256:8d00a5d8fc1043a4f641cbb321da766699393f1b6f87c70fae8089d61c9c9c54"}, + {file = "pymongo-4.7.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9377b868c38700c7557aac1bc4baae29f47f1d279cc76b60436e547fd643318c"}, + {file = "pymongo-4.7.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:da4a6a7b4f45329bb135aa5096823637bd5f760b44d6224f98190ee367b6b5dd"}, + {file = "pymongo-4.7.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:487e2f9277f8a63ac89335ec4f1699ae0d96ebd06d239480d69ed25473a71b2c"}, + {file = "pymongo-4.7.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db3d608d541a444c84f0bfc7bad80b0b897e0f4afa580a53f9a944065d9b633"}, + {file = "pymongo-4.7.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e90af2ad3a8a7c295f4d09a2fbcb9a350c76d6865f787c07fe843b79c6e821d1"}, + {file = "pymongo-4.7.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e28feb18dc559d50ededba27f9054c79f80c4edd70a826cecfe68f3266807b3"}, + {file = "pymongo-4.7.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f21ecddcba2d9132d5aebd8e959de8d318c29892d0718420447baf2b9bccbb19"}, + {file = "pymongo-4.7.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:26140fbb3f6a9a74bd73ed46d0b1f43d5702e87a6e453a31b24fad9c19df9358"}, + {file = "pymongo-4.7.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:94baa5fc7f7d22c3ce2ac7bd92f7e03ba7a6875f2480e3b97a400163d6eaafc9"}, + {file = "pymongo-4.7.3-cp38-cp38-win32.whl", hash = "sha256:92dd247727dd83d1903e495acc743ebd757f030177df289e3ba4ef8a8c561fad"}, + {file = "pymongo-4.7.3-cp38-cp38-win_amd64.whl", hash = "sha256:1c90c848a5e45475731c35097f43026b88ef14a771dfd08f20b67adc160a3f79"}, + {file = "pymongo-4.7.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f598be401b416319a535c386ac84f51df38663f7a9d1071922bda4d491564422"}, + {file = "pymongo-4.7.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:35ba90477fae61c65def6e7d09e8040edfdd3b7fd47c3c258b4edded60c4d625"}, + {file = "pymongo-4.7.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9aa8735955c70892634d7e61b0ede9b1eefffd3cd09ccabee0ffcf1bdfe62254"}, + {file = "pymongo-4.7.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:82a97d8f7f138586d9d0a0cff804a045cdbbfcfc1cd6bba542b151e284fbbec5"}, + {file = "pymongo-4.7.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de3b9db558930efab5eaef4db46dcad8bf61ac3ddfd5751b3e5ac6084a25e366"}, + {file = "pymongo-4.7.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0e149217ef62812d3c2401cf0e2852b0c57fd155297ecc4dcd67172c4eca402"}, + {file = "pymongo-4.7.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3a8a1ef4a824f5feb793b3231526d0045eadb5eb01080e38435dfc40a26c3e5"}, + {file = "pymongo-4.7.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d14e5e89a4be1f10efc3d9dcb13eb7a3b2334599cb6bb5d06c6a9281b79c8e22"}, + {file = "pymongo-4.7.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c6bfa29f032fd4fd7b129520f8cdb51ab71d88c2ba0567cccd05d325f963acb5"}, + {file = "pymongo-4.7.3-cp39-cp39-win32.whl", hash = "sha256:1421d0bd2ce629405f5157bd1aaa9b83f12d53a207cf68a43334f4e4ee312b66"}, + {file = "pymongo-4.7.3-cp39-cp39-win_amd64.whl", hash = "sha256:f7ee974f8b9370a998919c55b1050889f43815ab588890212023fecbc0402a6d"}, + {file = "pymongo-4.7.3.tar.gz", hash = "sha256:6354a66b228f2cd399be7429685fb68e07f19110a3679782ecb4fdb68da03831"}, ] [package.dependencies] @@ -4622,13 +4731,13 @@ files = [ [[package]] name = "pytest" -version = "8.2.0" +version = "8.2.2" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.2.0-py3-none-any.whl", hash = "sha256:1733f0620f6cda4095bbf0d9ff8022486e91892245bb9e7d5542c018f612f233"}, - {file = "pytest-8.2.0.tar.gz", hash = "sha256:d507d4482197eac0ba2bae2e9babf0672eb333017bcedaa5fb1a3d42c1174b3f"}, + {file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"}, + {file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"}, ] [package.dependencies] @@ -4644,13 +4753,13 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments [[package]] name = "pytest-asyncio" -version = "0.23.6" +version = "0.23.7" description = "Pytest support for asyncio" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-asyncio-0.23.6.tar.gz", hash = "sha256:ffe523a89c1c222598c76856e76852b787504ddb72dd5d9b6617ffa8aa2cde5f"}, - {file = "pytest_asyncio-0.23.6-py3-none-any.whl", hash = "sha256:68516fdd1018ac57b846c9846b954f0393b26f094764a28c955eabb0536a4e8a"}, + {file = "pytest_asyncio-0.23.7-py3-none-any.whl", hash = "sha256:009b48127fbe44518a547bddd25611551b0e43ccdbf1e67d12479f569832c20b"}, + {file = "pytest_asyncio-0.23.7.tar.gz", hash = "sha256:5f5c72948f4c49e7db4f29f2521d4031f1c27f86e57b046126654083d4770268"}, ] [package.dependencies] @@ -4814,17 +4923,27 @@ test = ["pytest"] [[package]] name = "python-telegram-bot" -version = "21.1.1" +version = "21.3" description = "We have made you a wrapper you can't refuse" optional = true python-versions = ">=3.8" files = [ - {file = "python-telegram-bot-21.1.1.tar.gz", hash = "sha256:5fd21710902270f5946c6a484918227b361b8119e1dadae6b3dd7fcee3b1a74e"}, - {file = "python_telegram_bot-21.1.1-py3-none-any.whl", hash = "sha256:dded93d733585a37b382fd5e088faffc37c3ae6d2bd4052d105d9a40f8a0152a"}, + {file = "python-telegram-bot-21.3.tar.gz", hash = "sha256:1be3c8b6f2b7354418109daa3f23c522e82ed22e7fc904346bee0c7b4aab52ae"}, + {file = "python_telegram_bot-21.3-py3-none-any.whl", hash = "sha256:8f575e6da903edd1e78967b5b481455ee6b27f2804d2384029177eab165f2e93"}, ] [package.dependencies] -httpx = ">=0.27,<1.0" +aiolimiter = {version = ">=1.1.0,<1.2.0", optional = true, markers = "extra == \"all\""} +APScheduler = {version = ">=3.10.4,<3.11.0", optional = true, markers = "extra == \"all\""} +cachetools = {version = ">=5.3.3,<5.4.0", optional = true, markers = "extra == \"all\""} +cryptography = {version = ">=39.0.1", optional = true, markers = "extra == \"all\""} +httpx = [ + {version = ">=0.27,<1.0"}, + {version = "*", extras = ["socks"], optional = true, markers = "extra == \"all\""}, + {version = "*", extras = ["http2"], optional = true, markers = "extra == \"all\""}, +] +pytz = {version = ">=2018.6", optional = true, markers = "extra == \"all\""} +tornado = {version = ">=6.4,<7.0", optional = true, markers = "extra == \"all\""} [package.extras] all = ["APScheduler (>=3.10.4,<3.11.0)", "aiolimiter (>=1.1.0,<1.2.0)", "cachetools (>=5.3.3,<5.4.0)", "cryptography (>=39.0.1)", "httpx[http2]", "httpx[socks]", "pytz (>=2018.6)", "tornado (>=6.4,<7.0)"] @@ -5100,101 +5219,104 @@ test = ["pytest (>=6,!=7.0.0,!=7.0.1)", "pytest-cov (>=3.0.0)", "pytest-qt"] [[package]] name = "rapidfuzz" -version = "3.9.0" +version = "3.9.3" description = "rapid fuzzy string matching" optional = false python-versions = ">=3.8" files = [ - {file = "rapidfuzz-3.9.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:bd375c4830fee11d502dd93ecadef63c137ae88e1aaa29cc15031fa66d1e0abb"}, - {file = "rapidfuzz-3.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:55e2c5076f38fc1dbaacb95fa026a3e409eee6ea5ac4016d44fb30e4cad42b20"}, - {file = "rapidfuzz-3.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:488f74126904db6b1bea545c2f3567ea882099f4c13f46012fe8f4b990c683df"}, - {file = "rapidfuzz-3.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3f2d1ea7cd57dfcd34821e38b4924c80a31bcf8067201b1ab07386996a9faee"}, - {file = "rapidfuzz-3.9.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b11e602987bcb4ea22b44178851f27406fca59b0836298d0beb009b504dba266"}, - {file = "rapidfuzz-3.9.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3083512e9bf6ed2bb3d25883922974f55e21ae7f8e9f4e298634691ae1aee583"}, - {file = "rapidfuzz-3.9.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b33c6d4b3a1190bc0b6c158c3981535f9434e8ed9ffa40cf5586d66c1819fb4b"}, - {file = "rapidfuzz-3.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dcb95fde22f98e6d0480db8d6038c45fe2d18a338690e6f9bba9b82323f3469"}, - {file = "rapidfuzz-3.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:08d8b49b3a4fb8572e480e73fcddc750da9cbb8696752ee12cca4bf8c8220d52"}, - {file = "rapidfuzz-3.9.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e721842e6b601ebbeb8cc5e12c75bbdd1d9e9561ea932f2f844c418c31256e82"}, - {file = "rapidfuzz-3.9.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7988363b3a415c5194ce1a68d380629247f8713e669ad81db7548eb156c4f365"}, - {file = "rapidfuzz-3.9.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:2d267d4c982ab7d177e994ab1f31b98ff3814f6791b90d35dda38307b9e7c989"}, - {file = "rapidfuzz-3.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0bb28ab5300cf974c7eb68ea21125c493e74b35b1129e629533468b2064ae0a2"}, - {file = "rapidfuzz-3.9.0-cp310-cp310-win32.whl", hash = "sha256:1b1f74997b6d94d66375479fa55f70b1c18e4d865d7afcd13f0785bfd40a9d3c"}, - {file = "rapidfuzz-3.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:c56d2efdfaa1c642029f3a7a5bb76085c5531f7a530777be98232d2ce142553c"}, - {file = "rapidfuzz-3.9.0-cp310-cp310-win_arm64.whl", hash = "sha256:6a83128d505cac76ea560bb9afcb3f6986e14e50a6f467db9a31faef4bd9b347"}, - {file = "rapidfuzz-3.9.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e2218d62ab63f3c5ad48eced898854d0c2c327a48f0fb02e2288d7e5332a22c8"}, - {file = "rapidfuzz-3.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:36bf35df2d6c7d5820da20a6720aee34f67c15cd2daf8cf92e8141995c640c25"}, - {file = "rapidfuzz-3.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:905b01a9b633394ff6bb5ebb1c5fd660e0e180c03fcf9d90199cc6ed74b87cf7"}, - {file = "rapidfuzz-3.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33cfabcb7fd994938a6a08e641613ce5fe46757832edc789c6a5602e7933d6fa"}, - {file = "rapidfuzz-3.9.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1179dcd3d150a67b8a678cd9c84f3baff7413ff13c9e8fe85e52a16c97e24c9b"}, - {file = "rapidfuzz-3.9.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:47d97e28c42f1efb7781993b67c749223f198f6653ef177a0c8f2b1c516efcaf"}, - {file = "rapidfuzz-3.9.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28da953eb2ef9ad527e536022da7afff6ace7126cdd6f3e21ac20f8762e76d2c"}, - {file = "rapidfuzz-3.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:182b4e11de928fb4834e8f8b5ecd971b5b10a86fabe8636ab65d3a9b7e0e9ca7"}, - {file = "rapidfuzz-3.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c74f2da334ce597f31670db574766ddeaee5d9430c2c00e28d0fbb7f76172036"}, - {file = "rapidfuzz-3.9.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:014ac55b03f4074f903248ded181f3000f4cdbd134e6155cbf643f0eceb4f70f"}, - {file = "rapidfuzz-3.9.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c4ef34b2ddbf448f1d644b4ec6475df8bbe5b9d0fee173ff2e87322a151663bd"}, - {file = "rapidfuzz-3.9.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fc02157f521af15143fae88f92ef3ddcc4e0cff05c40153a9549dc0fbdb9adb3"}, - {file = "rapidfuzz-3.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ff08081c49b18ba253a99e6a47f492e6ee8019e19bbb6ddc3ed360cd3ecb2f62"}, - {file = "rapidfuzz-3.9.0-cp311-cp311-win32.whl", hash = "sha256:b9bf90b3d96925cbf8ef44e5ee3cf39ef0c422f12d40f7a497e91febec546650"}, - {file = "rapidfuzz-3.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:d5d5684f54d82d9b0cf0b2701e55a630527a9c3dd5ddcf7a2e726a475ac238f2"}, - {file = "rapidfuzz-3.9.0-cp311-cp311-win_arm64.whl", hash = "sha256:a2de844e0e971d7bd8aa41284627dbeacc90e750b90acfb016836553c7a63192"}, - {file = "rapidfuzz-3.9.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f81fe99a69ac8ee3fd905e70c62f3af033901aeb60b69317d1d43d547b46e510"}, - {file = "rapidfuzz-3.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:633b9d03fc04abc585c197104b1d0af04b1f1db1abc99f674d871224cd15557a"}, - {file = "rapidfuzz-3.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab872cb57ae97c54ba7c71a9e3c9552beb57cb907c789b726895576d1ea9af6f"}, - {file = "rapidfuzz-3.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bdd8c15c3a14e409507fdf0c0434ec481d85c6cbeec8bdcd342a8cd1eda03825"}, - {file = "rapidfuzz-3.9.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2444d8155d9846f206e2079bb355b85f365d9457480b0d71677a112d0a7f7128"}, - {file = "rapidfuzz-3.9.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f83bd3d01f04061c3660742dc85143a89d49fd23eb31eccbf60ad56c4b955617"}, - {file = "rapidfuzz-3.9.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ca799f882364e69d0872619afb19efa3652b7133c18352e4a3d86a324fb2bb1"}, - {file = "rapidfuzz-3.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6993d361f28b9ef5f0fa4e79b8541c2f3507be7471b9f9cb403a255e123b31e1"}, - {file = "rapidfuzz-3.9.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:170822a1b1719f02b58e3dce194c8ad7d4c5b39be38c0fdec603bd19c6f9cf81"}, - {file = "rapidfuzz-3.9.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:0e86e39c1c1a0816ceda836e6f7bd3743b930cbc51a43a81bb433b552f203f25"}, - {file = "rapidfuzz-3.9.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:731269812ea837e0b93d913648e404736407408e33a00b75741e8f27c590caa2"}, - {file = "rapidfuzz-3.9.0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:8e5ff882d3a3d081157ceba7e0ebc7fac775f95b08cbb143accd4cece6043819"}, - {file = "rapidfuzz-3.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2003071aa633477a01509890c895f9ef56cf3f2eaa72c7ec0b567f743c1abcba"}, - {file = "rapidfuzz-3.9.0-cp312-cp312-win32.whl", hash = "sha256:13857f9070600ea1f940749f123b02d0b027afbaa45e72186df0f278915761d0"}, - {file = "rapidfuzz-3.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:134b7098ac109834eeea81424b6822f33c4c52bf80b81508295611e7a21be12a"}, - {file = "rapidfuzz-3.9.0-cp312-cp312-win_arm64.whl", hash = "sha256:2a96209f046fe328be30fc43f06e3d4b91f0d5b74e9dcd627dbfd65890fa4a5e"}, - {file = "rapidfuzz-3.9.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:544b0bf9d17170720809918e9ccd0d482d4a3a6eca35630d8e1459f737f71755"}, - {file = "rapidfuzz-3.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d536f8beb8dd82d6efb20fe9f82c2cfab9ffa0384b5d184327e393a4edde91d"}, - {file = "rapidfuzz-3.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:30f7609da871510583f87484a10820b26555a473a90ab356cdda2f3b4456256c"}, - {file = "rapidfuzz-3.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f4a2468432a1db491af6f547fad8f6d55fa03e57265c2f20e5eaceb68c7907e"}, - {file = "rapidfuzz-3.9.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11a7ec4676242c8a430509cff42ce98bca2fbe30188a63d0f60fdcbfd7e84970"}, - {file = "rapidfuzz-3.9.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dcb523243e988c849cf81220164ec3bbed378a699e595a8914fffe80596dc49f"}, - {file = "rapidfuzz-3.9.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4eea3bf72c4fe68e957526ffd6bcbb403a21baa6b3344aaae2d3252313df6199"}, - {file = "rapidfuzz-3.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4514980a5d204c076dd5b756960f6b1b7598f030009456e6109d76c4c331d03c"}, - {file = "rapidfuzz-3.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9a06a99f1335fe43464d7121bc6540de7cd9c9475ac2025babb373fe7f27846b"}, - {file = "rapidfuzz-3.9.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6c1ed63345d1581c39d4446b1a8c8f550709656ce2a3c88c47850b258167f3c2"}, - {file = "rapidfuzz-3.9.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:cd2e6e97daf17ebb3254285cf8dd86c60d56d6cf35c67f0f9a557ef26bd66290"}, - {file = "rapidfuzz-3.9.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:9bc0f7e6256a9c668482c41c8a3de5d0aa12e8ca346dcc427b97c7edb82cba48"}, - {file = "rapidfuzz-3.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7c09f4e87e82a164c9db769474bc61f8c8b677f2aeb0234b8abac73d2ecf9799"}, - {file = "rapidfuzz-3.9.0-cp38-cp38-win32.whl", hash = "sha256:e65b8f7921bf60cbb207c132842a6b45eefef48c4c3b510eb16087d6c08c70af"}, - {file = "rapidfuzz-3.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:9d6478957fb35c7844ad08f2442b62ba76c1857a56370781a707eefa4f4981e1"}, - {file = "rapidfuzz-3.9.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:65d9250a4b0bf86320097306084bc3ca479c8f5491927c170d018787793ebe95"}, - {file = "rapidfuzz-3.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:47b7c0840afa724db3b1a070bc6ed5beab73b4e659b1d395023617fc51bf68a2"}, - {file = "rapidfuzz-3.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3a16c48c6df8fb633efbbdea744361025d01d79bca988f884a620e63e782fe5b"}, - {file = "rapidfuzz-3.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48105991ff6e4a51c7f754df500baa070270ed3d41784ee0d097549bc9fcb16d"}, - {file = "rapidfuzz-3.9.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a7f273906b3c7cc6d63a76e088200805947aa0bc1ada42c6a0e582e19c390d7"}, - {file = "rapidfuzz-3.9.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c396562d304e974b4b0d5cd3afc4f92c113ea46a36e6bc62e45333d6aa8837e"}, - {file = "rapidfuzz-3.9.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:68da1b70458fea5290ec9a169fcffe0c17ff7e5bb3c3257e63d7021a50601a8e"}, - {file = "rapidfuzz-3.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c5b8f9a7b177af6ce7c6ad5b95588b4b73e37917711aafa33b2e79ee80fe709"}, - {file = "rapidfuzz-3.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3c42a238bf9dd48f4ccec4c6934ac718225b00bb3a438a008c219e7ccb3894c7"}, - {file = "rapidfuzz-3.9.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a365886c42177b2beab475a50ba311b59b04f233ceaebc4c341f6f91a86a78e2"}, - {file = "rapidfuzz-3.9.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:ce897b5dafb7fb7587a95fe4d449c1ea0b6d9ac4462fbafefdbbeef6eee4cf6a"}, - {file = "rapidfuzz-3.9.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:413ac49bae291d7e226a5c9be65c71b2630b3346bce39268d02cb3290232e4b7"}, - {file = "rapidfuzz-3.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8982fc3bd49d55a91569fc8a3feba0de4cef0b391ff9091be546e9df075b81"}, - {file = "rapidfuzz-3.9.0-cp39-cp39-win32.whl", hash = "sha256:3904d0084ab51f82e9f353031554965524f535522a48ec75c30b223eb5a0a488"}, - {file = "rapidfuzz-3.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:3733aede16ea112728ffeafeb29ccc62e095ed8ec816822fa2a82e92e2c08696"}, - {file = "rapidfuzz-3.9.0-cp39-cp39-win_arm64.whl", hash = "sha256:fc4e26f592b51f97acf0a3f8dfed95e4d830c6a8fbf359361035df836381ab81"}, - {file = "rapidfuzz-3.9.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e33362e98c7899b5f60dcb06ada00acd8673ce0d59aefe9a542701251fd00423"}, - {file = "rapidfuzz-3.9.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb67cf43ad83cb886cbbbff4df7dcaad7aedf94d64fca31aea0da7d26684283c"}, - {file = "rapidfuzz-3.9.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e2e106cc66453bb80d2ad9c0044f8287415676df5c8036d737d05d4b9cdbf8e"}, - {file = "rapidfuzz-3.9.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1256915f7e7a5cf2c151c9ac44834b37f9bd1c97e8dec6f936884f01b9dfc7d"}, - {file = "rapidfuzz-3.9.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:ae643220584518cbff8bf2974a0494d3e250763af816b73326a512c86ae782ce"}, - {file = "rapidfuzz-3.9.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:491274080742110427f38a6085bb12dffcaff1eef12dccf9e8758398c7e3957e"}, - {file = "rapidfuzz-3.9.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bc5559b9b94326922c096b30ae2d8e5b40b2e9c2c100c2cc396ad91bcb84d30"}, - {file = "rapidfuzz-3.9.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:849160dc0f128acb343af514ca827278005c1d00148d025e4035e034fc2d8c7f"}, - {file = "rapidfuzz-3.9.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:623883fb78e692d54ed7c43b09beec52c6685f10a45a7518128e25746667403b"}, - {file = "rapidfuzz-3.9.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d20ab9abc7e19767f1951772a6ab14cb4eddd886493c2da5ee12014596ad253f"}, - {file = "rapidfuzz-3.9.0.tar.gz", hash = "sha256:b182f0fb61f6ac435e416eb7ab330d62efdbf9b63cf0c7fa12d1f57c2eaaf6f3"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bdb8c5b8e29238ec80727c2ba3b301efd45aa30c6a7001123a6647b8e6f77ea4"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b3bd0d9632088c63a241f217742b1cf86e2e8ae573e01354775bd5016d12138c"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:153f23c03d4917f6a1fc2fb56d279cc6537d1929237ff08ee7429d0e40464a18"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a96c5225e840f1587f1bac8fa6f67562b38e095341576e82b728a82021f26d62"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b777cd910ceecd738adc58593d6ed42e73f60ad04ecdb4a841ae410b51c92e0e"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:53e06e4b81f552da04940aa41fc556ba39dee5513d1861144300c36c33265b76"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c7ca5b6050f18fdcacdada2dc5fb7619ff998cd9aba82aed2414eee74ebe6cd"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:87bb8d84cb41446a808c4b5f746e29d8a53499381ed72f6c4e456fe0f81c80a8"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:959a15186d18425d19811bea86a8ffbe19fd48644004d29008e636631420a9b7"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a24603dd05fb4e3c09d636b881ce347e5f55f925a6b1b4115527308a323b9f8e"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0d055da0e801c71dd74ba81d72d41b2fa32afa182b9fea6b4b199d2ce937450d"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:875b581afb29a7213cf9d98cb0f98df862f1020bce9d9b2e6199b60e78a41d14"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-win32.whl", hash = "sha256:6073a46f61479a89802e3f04655267caa6c14eb8ac9d81a635a13805f735ebc1"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:119c010e20e561249b99ca2627f769fdc8305b07193f63dbc07bca0a6c27e892"}, + {file = "rapidfuzz-3.9.3-cp310-cp310-win_arm64.whl", hash = "sha256:790b0b244f3213581d42baa2fed8875f9ee2b2f9b91f94f100ec80d15b140ba9"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f57e8305c281e8c8bc720515540e0580355100c0a7a541105c6cafc5de71daae"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a4fc7b784cf987dbddc300cef70e09a92ed1bce136f7bb723ea79d7e297fe76d"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b422c0a6fe139d5447a0766268e68e6a2a8c2611519f894b1f31f0a392b9167"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f50fed4a9b0c9825ff37cf0bccafd51ff5792090618f7846a7650f21f85579c9"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b80eb7cbe62348c61d3e67e17057cddfd6defab168863028146e07d5a8b24a89"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f45be77ec82da32ce5709a362e236ccf801615cc7163b136d1778cf9e31b14"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd84b7f652a5610733400307dc732f57c4a907080bef9520412e6d9b55bc9adc"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3e6d27dad8c990218b8cd4a5c99cbc8834f82bb46ab965a7265d5aa69fc7ced7"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:05ee0696ebf0dfe8f7c17f364d70617616afc7dafe366532730ca34056065b8a"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2bc8391749e5022cd9e514ede5316f86e332ffd3cfceeabdc0b17b7e45198a8c"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:93981895602cf5944d89d317ae3b1b4cc684d175a8ae2a80ce5b65615e72ddd0"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:754b719a4990735f66653c9e9261dcf52fd4d925597e43d6b9069afcae700d21"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-win32.whl", hash = "sha256:14c9f268ade4c88cf77ab007ad0fdf63699af071ee69378de89fff7aa3cae134"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:bc1991b4cde6c9d3c0bbcb83d5581dc7621bec8c666c095c65b4277233265a82"}, + {file = "rapidfuzz-3.9.3-cp311-cp311-win_arm64.whl", hash = "sha256:0c34139df09a61b1b557ab65782ada971b4a3bce7081d1b2bee45b0a52231adb"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5d6a210347d6e71234af5c76d55eeb0348b026c9bb98fe7c1cca89bac50fb734"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b300708c917ce52f6075bdc6e05b07c51a085733650f14b732c087dc26e0aaad"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83ea7ca577d76778250421de61fb55a719e45b841deb769351fc2b1740763050"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8319838fb5b7b5f088d12187d91d152b9386ce3979ed7660daa0ed1bff953791"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:505d99131afd21529293a9a7b91dfc661b7e889680b95534756134dc1cc2cd86"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c52970f7784518d7c82b07a62a26e345d2de8c2bd8ed4774e13342e4b3ff4200"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:143caf7247449055ecc3c1e874b69e42f403dfc049fc2f3d5f70e1daf21c1318"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b8ab0fa653d9225195a8ff924f992f4249c1e6fa0aea563f685e71b81b9fcccf"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:57e7c5bf7b61c7320cfa5dde1e60e678d954ede9bb7da8e763959b2138391401"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:51fa1ba84653ab480a2e2044e2277bd7f0123d6693051729755addc0d015c44f"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:17ff7f7eecdb169f9236e3b872c96dbbaf116f7787f4d490abd34b0116e3e9c8"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:afe7c72d3f917b066257f7ff48562e5d462d865a25fbcabf40fca303a9fa8d35"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-win32.whl", hash = "sha256:e53ed2e9b32674ce96eed80b3b572db9fd87aae6742941fb8e4705e541d861ce"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:35b7286f177e4d8ba1e48b03612f928a3c4bdac78e5651379cec59f95d8651e6"}, + {file = "rapidfuzz-3.9.3-cp312-cp312-win_arm64.whl", hash = "sha256:e6e4b9380ed4758d0cb578b0d1970c3f32dd9e87119378729a5340cb3169f879"}, + {file = "rapidfuzz-3.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a39890013f6d5b056cc4bfdedc093e322462ece1027a57ef0c636537bdde7531"}, + {file = "rapidfuzz-3.9.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b5bc0fdbf419493163c5c9cb147c5fbe95b8e25844a74a8807dcb1a125e630cf"}, + {file = "rapidfuzz-3.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efe6e200a75a792d37b960457904c4fce7c928a96ae9e5d21d2bd382fe39066e"}, + {file = "rapidfuzz-3.9.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de077c468c225d4c18f7188c47d955a16d65f21aab121cbdd98e3e2011002c37"}, + {file = "rapidfuzz-3.9.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f917eaadf5388466a95f6a236f678a1588d231e52eda85374077101842e794e"}, + {file = "rapidfuzz-3.9.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:858ba57c05afd720db8088a8707079e8d024afe4644001fe0dbd26ef7ca74a65"}, + {file = "rapidfuzz-3.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d36447d21b05f90282a6f98c5a33771805f9222e5d0441d03eb8824e33e5bbb4"}, + {file = "rapidfuzz-3.9.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:acbe4b6f1ccd5b90c29d428e849aa4242e51bb6cab0448d5f3c022eb9a25f7b1"}, + {file = "rapidfuzz-3.9.3-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:53c7f27cdf899e94712972237bda48cfd427646aa6f5d939bf45d084780e4c16"}, + {file = "rapidfuzz-3.9.3-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:6175682a829c6dea4d35ed707f1dadc16513270ef64436568d03b81ccb6bdb74"}, + {file = "rapidfuzz-3.9.3-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:5276df395bd8497397197fca2b5c85f052d2e6a66ffc3eb0544dd9664d661f95"}, + {file = "rapidfuzz-3.9.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:77b5c4f3e72924d7845f0e189c304270066d0f49635cf8a3938e122c437e58de"}, + {file = "rapidfuzz-3.9.3-cp38-cp38-win32.whl", hash = "sha256:8add34061e5cd561c72ed4febb5c15969e7b25bda2bb5102d02afc3abc1f52d0"}, + {file = "rapidfuzz-3.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:604e0502a39cf8e67fa9ad239394dddad4cdef6d7008fdb037553817d420e108"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:21047f55d674614eb4b0ab34e35c3dc66f36403b9fbfae645199c4a19d4ed447"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a56da3aff97cb56fe85d9ca957d1f55dbac7c27da927a86a2a86d8a7e17f80aa"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:964c08481aec2fe574f0062e342924db2c6b321391aeb73d68853ed42420fd6d"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5e2b827258beefbe5d3f958243caa5a44cf46187eff0c20e0b2ab62d1550327a"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c6e65a301fcd19fbfbee3a514cc0014ff3f3b254b9fd65886e8a9d6957fb7bca"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cbe93ba1725a8d47d2b9dca6c1f435174859427fbc054d83de52aea5adc65729"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aca21c0a34adee582775da997a600283e012a608a107398d80a42f9a57ad323d"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:256e07d3465173b2a91c35715a2277b1ee3ae0b9bbab4e519df6af78570741d0"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:802ca2cc8aa6b8b34c6fdafb9e32540c1ba05fca7ad60b3bbd7ec89ed1797a87"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:dd789100fc852cffac1449f82af0da139d36d84fd9faa4f79fc4140a88778343"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:5d0abbacdb06e27ff803d7ae0bd0624020096802758068ebdcab9bd49cf53115"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:378d1744828e27490a823fc6fe6ebfb98c15228d54826bf4e49e4b76eb5f5579"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-win32.whl", hash = "sha256:5d0cb272d43e6d3c0dedefdcd9d00007471f77b52d2787a4695e9dd319bb39d2"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:15e4158ac4b3fb58108072ec35b8a69165f651ba1c8f43559a36d518dbf9fb3f"}, + {file = "rapidfuzz-3.9.3-cp39-cp39-win_arm64.whl", hash = "sha256:58c6a4936190c558d5626b79fc9e16497e5df7098589a7e80d8bff68148ff096"}, + {file = "rapidfuzz-3.9.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5410dc848c947a603792f4f51b904a3331cf1dc60621586bfbe7a6de72da1091"}, + {file = "rapidfuzz-3.9.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:282d55700a1a3d3a7980746eb2fcd48c9bbc1572ebe0840d0340d548a54d01fe"}, + {file = "rapidfuzz-3.9.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc1037507810833646481f5729901a154523f98cbebb1157ba3a821012e16402"}, + {file = "rapidfuzz-3.9.3-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5e33f779391caedcba2ba3089fb6e8e557feab540e9149a5c3f7fea7a3a7df37"}, + {file = "rapidfuzz-3.9.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41a81a9f311dc83d22661f9b1a1de983b201322df0c4554042ffffd0f2040c37"}, + {file = "rapidfuzz-3.9.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a93250bd8fae996350c251e1752f2c03335bb8a0a5b0c7e910a593849121a435"}, + {file = "rapidfuzz-3.9.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3617d1aa7716c57d120b6adc8f7c989f2d65bc2b0cbd5f9288f1fc7bf469da11"}, + {file = "rapidfuzz-3.9.3-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:ad04a3f5384b82933213bba2459f6424decc2823df40098920856bdee5fd6e88"}, + {file = "rapidfuzz-3.9.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8709918da8a88ad73c9d4dd0ecf24179a4f0ceba0bee21efc6ea21a8b5290349"}, + {file = "rapidfuzz-3.9.3-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b770f85eab24034e6ef7df04b2bfd9a45048e24f8a808e903441aa5abde8ecdd"}, + {file = "rapidfuzz-3.9.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:930b4e6fdb4d914390141a2b99a6f77a52beacf1d06aa4e170cba3a98e24c1bc"}, + {file = "rapidfuzz-3.9.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:c8444e921bfc3757c475c4f4d7416a7aa69b2d992d5114fe55af21411187ab0d"}, + {file = "rapidfuzz-3.9.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2c1d3ef3878f871abe6826e386c3d61b5292ef5f7946fe646f4206b85836b5da"}, + {file = "rapidfuzz-3.9.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:d861bf326ee7dabc35c532a40384541578cd1ec1e1b7db9f9ecbba56eb76ca22"}, + {file = "rapidfuzz-3.9.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cde6b9d9ba5007077ee321ec722fa714ebc0cbd9a32ccf0f4dd3cc3f20952d71"}, + {file = "rapidfuzz-3.9.3-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bb6546e7b6bed1aefbe24f68a5fb9b891cc5aef61bca6c1a7b1054b7f0359bb"}, + {file = "rapidfuzz-3.9.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d8a57261ef7996d5ced7c8cba9189ada3fbeffd1815f70f635e4558d93766cb"}, + {file = "rapidfuzz-3.9.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:67201c02efc596923ad950519e0b75ceb78d524177ea557134d6567b9ac2c283"}, + {file = "rapidfuzz-3.9.3.tar.gz", hash = "sha256:b398ea66e8ed50451bce5997c430197d5e4b06ac4aa74602717f792d8d8d06e2"}, ] [package.extras] @@ -5202,13 +5324,13 @@ full = ["numpy"] [[package]] name = "redis" -version = "5.0.4" +version = "5.0.6" description = "Python client for Redis database and key-value store" optional = true python-versions = ">=3.7" files = [ - {file = "redis-5.0.4-py3-none-any.whl", hash = "sha256:7adc2835c7a9b5033b7ad8f8918d09b7344188228809c98df07af226d39dec91"}, - {file = "redis-5.0.4.tar.gz", hash = "sha256:ec31f2ed9675cc54c21ba854cfe0462e6faf1d83c8ce5944709db8a4700b9c61"}, + {file = "redis-5.0.6-py3-none-any.whl", hash = "sha256:c0d6d990850c627bbf7be01c5c4cbaadf67b48593e913bb71c9819c30df37eee"}, + {file = "redis-5.0.6.tar.gz", hash = "sha256:38473cd7c6389ad3e44a91f4c3eaf6bcb8a9f746007f29bf4fb20824ff0b2197"}, ] [package.dependencies] @@ -5235,13 +5357,13 @@ rpds-py = ">=0.7.0" [[package]] name = "requests" -version = "2.31.0" +version = "2.32.3" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] [package.dependencies] @@ -5477,19 +5599,18 @@ win32 = ["pywin32"] [[package]] name = "setuptools" -version = "69.5.1" +version = "70.1.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"}, - {file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"}, + {file = "setuptools-70.1.0-py3-none-any.whl", hash = "sha256:d9b8b771455a97c8a9f3ab3448ebe0b29b5e105f1228bba41028be116985a267"}, + {file = "setuptools-70.1.0.tar.gz", hash = "sha256:01a1e793faa5bd89abc851fa15d0a0db26f160890c7102cd8dce643e886b47f5"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.10.0)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "shellingham" @@ -5546,6 +5667,17 @@ files = [ {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, ] +[[package]] +name = "socksio" +version = "1.0.0" +description = "Sans-I/O implementation of SOCKS4, SOCKS4A, and SOCKS5." +optional = true +python-versions = ">=3.6" +files = [ + {file = "socksio-1.0.0-py3-none-any.whl", hash = "sha256:95dc1f15f9b34e8d7b16f06d74b8ccf48f609af32ab33c608d08761c5dcbb1f3"}, + {file = "socksio-1.0.0.tar.gz", hash = "sha256:f88beb3da5b5c38b9890469de67d0cb0f9d494b78b106ca1845f96c10b91c4ac"}, +] + [[package]] name = "soupsieve" version = "2.5" @@ -5760,13 +5892,13 @@ test = ["flake8", "mypy", "pytest"] [[package]] name = "sphinxcontrib-katex" -version = "0.9.9" +version = "0.9.10" description = "A Sphinx extension for rendering math in HTML pages" optional = false python-versions = ">=3.7" files = [ - {file = "sphinxcontrib-katex-0.9.9.tar.gz", hash = "sha256:d594c82df54b048d59d48e46b109f62217b311ab9b95208cab96d902f9a3fe29"}, - {file = "sphinxcontrib_katex-0.9.9-py3-none-any.whl", hash = "sha256:9b88f2b815ec04025ff0f96b50a22678bb9a186710aa34f20a308dd58fbb7f08"}, + {file = "sphinxcontrib_katex-0.9.10-py3-none-any.whl", hash = "sha256:4e5f0b18761cd2cd058a1b2392f42a7edea4cc5beaa504a44aaee07d17ace9b7"}, + {file = "sphinxcontrib_katex-0.9.10.tar.gz", hash = "sha256:309a92dae245dbc584ff7ea5fb6549727bae95e4e52008b74d259d2fd1ad0dec"}, ] [package.dependencies] @@ -5804,64 +5936,64 @@ test = ["pytest"] [[package]] name = "sqlalchemy" -version = "2.0.30" +version = "2.0.31" description = "Database Abstraction Library" optional = true python-versions = ">=3.7" files = [ - {file = "SQLAlchemy-2.0.30-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3b48154678e76445c7ded1896715ce05319f74b1e73cf82d4f8b59b46e9c0ddc"}, - {file = "SQLAlchemy-2.0.30-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2753743c2afd061bb95a61a51bbb6a1a11ac1c44292fad898f10c9839a7f75b2"}, - {file = "SQLAlchemy-2.0.30-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7bfc726d167f425d4c16269a9a10fe8630ff6d14b683d588044dcef2d0f6be7"}, - {file = "SQLAlchemy-2.0.30-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4f61ada6979223013d9ab83a3ed003ded6959eae37d0d685db2c147e9143797"}, - {file = "SQLAlchemy-2.0.30-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a365eda439b7a00732638f11072907c1bc8e351c7665e7e5da91b169af794af"}, - {file = "SQLAlchemy-2.0.30-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bba002a9447b291548e8d66fd8c96a6a7ed4f2def0bb155f4f0a1309fd2735d5"}, - {file = "SQLAlchemy-2.0.30-cp310-cp310-win32.whl", hash = "sha256:0138c5c16be3600923fa2169532205d18891b28afa817cb49b50e08f62198bb8"}, - {file = "SQLAlchemy-2.0.30-cp310-cp310-win_amd64.whl", hash = "sha256:99650e9f4cf3ad0d409fed3eec4f071fadd032e9a5edc7270cd646a26446feeb"}, - {file = "SQLAlchemy-2.0.30-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:955991a09f0992c68a499791a753523f50f71a6885531568404fa0f231832aa0"}, - {file = "SQLAlchemy-2.0.30-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f69e4c756ee2686767eb80f94c0125c8b0a0b87ede03eacc5c8ae3b54b99dc46"}, - {file = "SQLAlchemy-2.0.30-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69c9db1ce00e59e8dd09d7bae852a9add716efdc070a3e2068377e6ff0d6fdaa"}, - {file = "SQLAlchemy-2.0.30-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1429a4b0f709f19ff3b0cf13675b2b9bfa8a7e79990003207a011c0db880a13"}, - {file = "SQLAlchemy-2.0.30-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:efedba7e13aa9a6c8407c48facfdfa108a5a4128e35f4c68f20c3407e4376aa9"}, - {file = "SQLAlchemy-2.0.30-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:16863e2b132b761891d6c49f0a0f70030e0bcac4fd208117f6b7e053e68668d0"}, - {file = "SQLAlchemy-2.0.30-cp311-cp311-win32.whl", hash = "sha256:2ecabd9ccaa6e914e3dbb2aa46b76dede7eadc8cbf1b8083c94d936bcd5ffb49"}, - {file = "SQLAlchemy-2.0.30-cp311-cp311-win_amd64.whl", hash = "sha256:0b3f4c438e37d22b83e640f825ef0f37b95db9aa2d68203f2c9549375d0b2260"}, - {file = "SQLAlchemy-2.0.30-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5a79d65395ac5e6b0c2890935bad892eabb911c4aa8e8015067ddb37eea3d56c"}, - {file = "SQLAlchemy-2.0.30-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9a5baf9267b752390252889f0c802ea13b52dfee5e369527da229189b8bd592e"}, - {file = "SQLAlchemy-2.0.30-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cb5a646930c5123f8461f6468901573f334c2c63c795b9af350063a736d0134"}, - {file = "SQLAlchemy-2.0.30-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:296230899df0b77dec4eb799bcea6fbe39a43707ce7bb166519c97b583cfcab3"}, - {file = "SQLAlchemy-2.0.30-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c62d401223f468eb4da32627bffc0c78ed516b03bb8a34a58be54d618b74d472"}, - {file = "SQLAlchemy-2.0.30-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3b69e934f0f2b677ec111b4d83f92dc1a3210a779f69bf905273192cf4ed433e"}, - {file = "SQLAlchemy-2.0.30-cp312-cp312-win32.whl", hash = "sha256:77d2edb1f54aff37e3318f611637171e8ec71472f1fdc7348b41dcb226f93d90"}, - {file = "SQLAlchemy-2.0.30-cp312-cp312-win_amd64.whl", hash = "sha256:b6c7ec2b1f4969fc19b65b7059ed00497e25f54069407a8701091beb69e591a5"}, - {file = "SQLAlchemy-2.0.30-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5a8e3b0a7e09e94be7510d1661339d6b52daf202ed2f5b1f9f48ea34ee6f2d57"}, - {file = "SQLAlchemy-2.0.30-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b60203c63e8f984df92035610c5fb76d941254cf5d19751faab7d33b21e5ddc0"}, - {file = "SQLAlchemy-2.0.30-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1dc3eabd8c0232ee8387fbe03e0a62220a6f089e278b1f0aaf5e2d6210741ad"}, - {file = "SQLAlchemy-2.0.30-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:40ad017c672c00b9b663fcfcd5f0864a0a97828e2ee7ab0c140dc84058d194cf"}, - {file = "SQLAlchemy-2.0.30-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e42203d8d20dc704604862977b1470a122e4892791fe3ed165f041e4bf447a1b"}, - {file = "SQLAlchemy-2.0.30-cp37-cp37m-win32.whl", hash = "sha256:2a4f4da89c74435f2bc61878cd08f3646b699e7d2eba97144030d1be44e27584"}, - {file = "SQLAlchemy-2.0.30-cp37-cp37m-win_amd64.whl", hash = "sha256:b6bf767d14b77f6a18b6982cbbf29d71bede087edae495d11ab358280f304d8e"}, - {file = "SQLAlchemy-2.0.30-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bc0c53579650a891f9b83fa3cecd4e00218e071d0ba00c4890f5be0c34887ed3"}, - {file = "SQLAlchemy-2.0.30-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:311710f9a2ee235f1403537b10c7687214bb1f2b9ebb52702c5aa4a77f0b3af7"}, - {file = "SQLAlchemy-2.0.30-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:408f8b0e2c04677e9c93f40eef3ab22f550fecb3011b187f66a096395ff3d9fd"}, - {file = "SQLAlchemy-2.0.30-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37a4b4fb0dd4d2669070fb05b8b8824afd0af57587393015baee1cf9890242d9"}, - {file = "SQLAlchemy-2.0.30-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a943d297126c9230719c27fcbbeab57ecd5d15b0bd6bfd26e91bfcfe64220621"}, - {file = "SQLAlchemy-2.0.30-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0a089e218654e740a41388893e090d2e2c22c29028c9d1353feb38638820bbeb"}, - {file = "SQLAlchemy-2.0.30-cp38-cp38-win32.whl", hash = "sha256:fa561138a64f949f3e889eb9ab8c58e1504ab351d6cf55259dc4c248eaa19da6"}, - {file = "SQLAlchemy-2.0.30-cp38-cp38-win_amd64.whl", hash = "sha256:7d74336c65705b986d12a7e337ba27ab2b9d819993851b140efdf029248e818e"}, - {file = "SQLAlchemy-2.0.30-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ae8c62fe2480dd61c532ccafdbce9b29dacc126fe8be0d9a927ca3e699b9491a"}, - {file = "SQLAlchemy-2.0.30-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2383146973a15435e4717f94c7509982770e3e54974c71f76500a0136f22810b"}, - {file = "SQLAlchemy-2.0.30-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8409de825f2c3b62ab15788635ccaec0c881c3f12a8af2b12ae4910a0a9aeef6"}, - {file = "SQLAlchemy-2.0.30-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0094c5dc698a5f78d3d1539853e8ecec02516b62b8223c970c86d44e7a80f6c7"}, - {file = "SQLAlchemy-2.0.30-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:edc16a50f5e1b7a06a2dcc1f2205b0b961074c123ed17ebda726f376a5ab0953"}, - {file = "SQLAlchemy-2.0.30-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f7703c2010355dd28f53deb644a05fc30f796bd8598b43f0ba678878780b6e4c"}, - {file = "SQLAlchemy-2.0.30-cp39-cp39-win32.whl", hash = "sha256:1f9a727312ff6ad5248a4367358e2cf7e625e98b1028b1d7ab7b806b7d757513"}, - {file = "SQLAlchemy-2.0.30-cp39-cp39-win_amd64.whl", hash = "sha256:a0ef36b28534f2a5771191be6edb44cc2673c7b2edf6deac6562400288664221"}, - {file = "SQLAlchemy-2.0.30-py3-none-any.whl", hash = "sha256:7108d569d3990c71e26a42f60474b4c02c8586c4681af5fd67e51a044fdea86a"}, - {file = "SQLAlchemy-2.0.30.tar.gz", hash = "sha256:2b1708916730f4830bc69d6f49d37f7698b5bd7530aca7f04f785f8849e95255"}, -] - -[package.dependencies] -greenlet = {version = "!=0.4.17", optional = true, markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\" or extra == \"asyncio\""} + {file = "SQLAlchemy-2.0.31-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f2a213c1b699d3f5768a7272de720387ae0122f1becf0901ed6eaa1abd1baf6c"}, + {file = "SQLAlchemy-2.0.31-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9fea3d0884e82d1e33226935dac990b967bef21315cbcc894605db3441347443"}, + {file = "SQLAlchemy-2.0.31-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3ad7f221d8a69d32d197e5968d798217a4feebe30144986af71ada8c548e9fa"}, + {file = "SQLAlchemy-2.0.31-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f2bee229715b6366f86a95d497c347c22ddffa2c7c96143b59a2aa5cc9eebbc"}, + {file = "SQLAlchemy-2.0.31-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cd5b94d4819c0c89280b7c6109c7b788a576084bf0a480ae17c227b0bc41e109"}, + {file = "SQLAlchemy-2.0.31-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:750900a471d39a7eeba57580b11983030517a1f512c2cb287d5ad0fcf3aebd58"}, + {file = "SQLAlchemy-2.0.31-cp310-cp310-win32.whl", hash = "sha256:7bd112be780928c7f493c1a192cd8c5fc2a2a7b52b790bc5a84203fb4381c6be"}, + {file = "SQLAlchemy-2.0.31-cp310-cp310-win_amd64.whl", hash = "sha256:5a48ac4d359f058474fadc2115f78a5cdac9988d4f99eae44917f36aa1476327"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f68470edd70c3ac3b6cd5c2a22a8daf18415203ca1b036aaeb9b0fb6f54e8298"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e2c38c2a4c5c634fe6c3c58a789712719fa1bf9b9d6ff5ebfce9a9e5b89c1ca"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd15026f77420eb2b324dcb93551ad9c5f22fab2c150c286ef1dc1160f110203"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2196208432deebdfe3b22185d46b08f00ac9d7b01284e168c212919891289396"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:352b2770097f41bff6029b280c0e03b217c2dcaddc40726f8f53ed58d8a85da4"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:56d51ae825d20d604583f82c9527d285e9e6d14f9a5516463d9705dab20c3740"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-win32.whl", hash = "sha256:6e2622844551945db81c26a02f27d94145b561f9d4b0c39ce7bfd2fda5776dac"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-win_amd64.whl", hash = "sha256:ccaf1b0c90435b6e430f5dd30a5aede4764942a695552eb3a4ab74ed63c5b8d3"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3b74570d99126992d4b0f91fb87c586a574a5872651185de8297c6f90055ae42"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f77c4f042ad493cb8595e2f503c7a4fe44cd7bd59c7582fd6d78d7e7b8ec52c"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd1591329333daf94467e699e11015d9c944f44c94d2091f4ac493ced0119449"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74afabeeff415e35525bf7a4ecdab015f00e06456166a2eba7590e49f8db940e"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b9c01990d9015df2c6f818aa8f4297d42ee71c9502026bb074e713d496e26b67"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:66f63278db425838b3c2b1c596654b31939427016ba030e951b292e32b99553e"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-win32.whl", hash = "sha256:0b0f658414ee4e4b8cbcd4a9bb0fd743c5eeb81fc858ca517217a8013d282c96"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-win_amd64.whl", hash = "sha256:fa4b1af3e619b5b0b435e333f3967612db06351217c58bfb50cee5f003db2a5a"}, + {file = "SQLAlchemy-2.0.31-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:f43e93057cf52a227eda401251c72b6fbe4756f35fa6bfebb5d73b86881e59b0"}, + {file = "SQLAlchemy-2.0.31-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d337bf94052856d1b330d5fcad44582a30c532a2463776e1651bd3294ee7e58b"}, + {file = "SQLAlchemy-2.0.31-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c06fb43a51ccdff3b4006aafee9fcf15f63f23c580675f7734245ceb6b6a9e05"}, + {file = "SQLAlchemy-2.0.31-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:b6e22630e89f0e8c12332b2b4c282cb01cf4da0d26795b7eae16702a608e7ca1"}, + {file = "SQLAlchemy-2.0.31-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:79a40771363c5e9f3a77f0e28b3302801db08040928146e6808b5b7a40749c88"}, + {file = "SQLAlchemy-2.0.31-cp37-cp37m-win32.whl", hash = "sha256:501ff052229cb79dd4c49c402f6cb03b5a40ae4771efc8bb2bfac9f6c3d3508f"}, + {file = "SQLAlchemy-2.0.31-cp37-cp37m-win_amd64.whl", hash = "sha256:597fec37c382a5442ffd471f66ce12d07d91b281fd474289356b1a0041bdf31d"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:dc6d69f8829712a4fd799d2ac8d79bdeff651c2301b081fd5d3fe697bd5b4ab9"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:23b9fbb2f5dd9e630db70fbe47d963c7779e9c81830869bd7d137c2dc1ad05fb"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a21c97efcbb9f255d5c12a96ae14da873233597dfd00a3a0c4ce5b3e5e79704"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26a6a9837589c42b16693cf7bf836f5d42218f44d198f9343dd71d3164ceeeac"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc251477eae03c20fae8db9c1c23ea2ebc47331bcd73927cdcaecd02af98d3c3"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:2fd17e3bb8058359fa61248c52c7b09a97cf3c820e54207a50af529876451808"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-win32.whl", hash = "sha256:c76c81c52e1e08f12f4b6a07af2b96b9b15ea67ccdd40ae17019f1c373faa227"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-win_amd64.whl", hash = "sha256:4b600e9a212ed59355813becbcf282cfda5c93678e15c25a0ef896b354423238"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b6cf796d9fcc9b37011d3f9936189b3c8074a02a4ed0c0fbbc126772c31a6d4"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:78fe11dbe37d92667c2c6e74379f75746dc947ee505555a0197cfba9a6d4f1a4"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fc47dc6185a83c8100b37acda27658fe4dbd33b7d5e7324111f6521008ab4fe"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a41514c1a779e2aa9a19f67aaadeb5cbddf0b2b508843fcd7bafdf4c6864005"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:afb6dde6c11ea4525318e279cd93c8734b795ac8bb5dda0eedd9ebaca7fa23f1"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3f9faef422cfbb8fd53716cd14ba95e2ef655400235c3dfad1b5f467ba179c8c"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-win32.whl", hash = "sha256:fc6b14e8602f59c6ba893980bea96571dd0ed83d8ebb9c4479d9ed5425d562e9"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-win_amd64.whl", hash = "sha256:3cb8a66b167b033ec72c3812ffc8441d4e9f5f78f5e31e54dcd4c90a4ca5bebc"}, + {file = "SQLAlchemy-2.0.31-py3-none-any.whl", hash = "sha256:69f3e3c08867a8e4856e92d7afb618b95cdee18e0bc1647b77599722c9a28911"}, + {file = "SQLAlchemy-2.0.31.tar.gz", hash = "sha256:b607489dd4a54de56984a0c7656247504bd5523d9d0ba799aef59d4add009484"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", optional = true, markers = "python_version < \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\") or extra == \"asyncio\""} typing-extensions = ">=4.6.0" [package.extras] @@ -5943,13 +6075,13 @@ full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7 [[package]] name = "streamlit" -version = "1.34.0" +version = "1.35.0" description = "A faster way to build and share data apps" optional = false python-versions = "!=3.9.7,>=3.8" files = [ - {file = "streamlit-1.34.0-py2.py3-none-any.whl", hash = "sha256:411183cf7f525e468eb256b343c914782d11cd894b5e30a4ab3bb1d54e3ae339"}, - {file = "streamlit-1.34.0.tar.gz", hash = "sha256:135a3b79a686b3132b73f204450ad6e889de04f3349d692925e09f0e21e74b52"}, + {file = "streamlit-1.35.0-py2.py3-none-any.whl", hash = "sha256:e17d1d86830a0d7687c37faf2fe47bffa752d0c95a306e96d7749bd3faa72a5b"}, + {file = "streamlit-1.35.0.tar.gz", hash = "sha256:679d55bb6189743f606abf0696623df0bfd223a6d0c8d96b8d60678d4891d2d6"}, ] [package.dependencies] @@ -5992,12 +6124,12 @@ streamlit = ">=0.63" [[package]] name = "telethon" -version = "1.35.0" +version = "1.36.0" description = "Full-featured Telegram client library for Python 3" optional = false python-versions = ">=3.5" files = [ - {file = "Telethon-1.35.0.tar.gz", hash = "sha256:99d7a2e161e9af1cdf03feef7a3fea6eef304a9caf620fe13aefc53099845555"}, + {file = "Telethon-1.36.0.tar.gz", hash = "sha256:11db5c7ed7e37f1272d443fb7eea0f1db580d56c6949165233946fb323aaf3a7"}, ] [package.dependencies] @@ -6009,13 +6141,13 @@ cryptg = ["cryptg"] [[package]] name = "tenacity" -version = "8.3.0" +version = "8.4.1" description = "Retry code until it succeeds" optional = false python-versions = ">=3.8" files = [ - {file = "tenacity-8.3.0-py3-none-any.whl", hash = "sha256:3649f6443dbc0d9b01b9d8020a9c4ec7a1ff5f6f3c6c8a036ef371f573fe9185"}, - {file = "tenacity-8.3.0.tar.gz", hash = "sha256:953d4e6ad24357bceffbc9707bc74349aca9d245f68eb65419cf0c249a1949a2"}, + {file = "tenacity-8.4.1-py3-none-any.whl", hash = "sha256:28522e692eda3e1b8f5e99c51464efcc0b9fc86933da92415168bc1c4e2308fa"}, + {file = "tenacity-8.4.1.tar.gz", hash = "sha256:54b1412b878ddf7e1f1577cd49527bad8cdef32421bd599beac0c6c3f10582fd"}, ] [package.extras] @@ -6099,13 +6231,13 @@ files = [ [[package]] name = "tomlkit" -version = "0.12.4" +version = "0.12.5" description = "Style preserving TOML library" optional = false python-versions = ">=3.7" files = [ - {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, - {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, + {file = "tomlkit-0.12.5-py3-none-any.whl", hash = "sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f"}, + {file = "tomlkit-0.12.5.tar.gz", hash = "sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c"}, ] [[package]] @@ -6121,22 +6253,22 @@ files = [ [[package]] name = "tornado" -version = "6.4" +version = "6.4.1" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." optional = false -python-versions = ">= 3.8" +python-versions = ">=3.8" files = [ - {file = "tornado-6.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:02ccefc7d8211e5a7f9e8bc3f9e5b0ad6262ba2fbb683a6443ecc804e5224ce0"}, - {file = "tornado-6.4-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:27787de946a9cffd63ce5814c33f734c627a87072ec7eed71f7fc4417bb16263"}, - {file = "tornado-6.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7894c581ecdcf91666a0912f18ce5e757213999e183ebfc2c3fdbf4d5bd764e"}, - {file = "tornado-6.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43bc2e5370a6a8e413e1e1cd0c91bedc5bd62a74a532371042a18ef19e10579"}, - {file = "tornado-6.4-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0251554cdd50b4b44362f73ad5ba7126fc5b2c2895cc62b14a1c2d7ea32f212"}, - {file = "tornado-6.4-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fd03192e287fbd0899dd8f81c6fb9cbbc69194d2074b38f384cb6fa72b80e9c2"}, - {file = "tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:88b84956273fbd73420e6d4b8d5ccbe913c65d31351b4c004ae362eba06e1f78"}, - {file = "tornado-6.4-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:71ddfc23a0e03ef2df1c1397d859868d158c8276a0603b96cf86892bff58149f"}, - {file = "tornado-6.4-cp38-abi3-win32.whl", hash = "sha256:6f8a6c77900f5ae93d8b4ae1196472d0ccc2775cc1dfdc9e7727889145c45052"}, - {file = "tornado-6.4-cp38-abi3-win_amd64.whl", hash = "sha256:10aeaa8006333433da48dec9fe417877f8bcc21f48dda8d661ae79da357b2a63"}, - {file = "tornado-6.4.tar.gz", hash = "sha256:72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee"}, + {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:163b0aafc8e23d8cdc3c9dfb24c5368af84a81e3364745ccb4427669bf84aec8"}, + {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6d5ce3437e18a2b66fbadb183c1d3364fb03f2be71299e7d10dbeeb69f4b2a14"}, + {file = "tornado-6.4.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e20b9113cd7293f164dc46fffb13535266e713cdb87bd2d15ddb336e96cfc4"}, + {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ae50a504a740365267b2a8d1a90c9fbc86b780a39170feca9bcc1787ff80842"}, + {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:613bf4ddf5c7a95509218b149b555621497a6cc0d46ac341b30bd9ec19eac7f3"}, + {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25486eb223babe3eed4b8aecbac33b37e3dd6d776bc730ca14e1bf93888b979f"}, + {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:454db8a7ecfcf2ff6042dde58404164d969b6f5d58b926da15e6b23817950fc4"}, + {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a02a08cc7a9314b006f653ce40483b9b3c12cda222d6a46d4ac63bb6c9057698"}, + {file = "tornado-6.4.1-cp38-abi3-win32.whl", hash = "sha256:d9a566c40b89757c9aa8e6f032bcdb8ca8795d7c1a9762910c722b1635c9de4d"}, + {file = "tornado-6.4.1-cp38-abi3-win_amd64.whl", hash = "sha256:b24b8982ed444378d7f21d563f4180a2de31ced9d8d84443907a0a64da2072e7"}, + {file = "tornado-6.4.1.tar.gz", hash = "sha256:92d3ab53183d8c50f8204a51e6f91d18a15d5ef261e84d452800d4ff6fc504e9"}, ] [[package]] @@ -6176,13 +6308,13 @@ test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0, [[package]] name = "trove-classifiers" -version = "2024.4.10" +version = "2024.5.22" description = "Canonical source for classifiers on PyPI (pypi.org)." optional = false python-versions = "*" files = [ - {file = "trove-classifiers-2024.4.10.tar.gz", hash = "sha256:49f40bb6a746b72a1cba4f8d55ee8252169cda0f70802e3fd24f04b7fb25a492"}, - {file = "trove_classifiers-2024.4.10-py3-none-any.whl", hash = "sha256:678bd6fcc5218d72e3304e27a608acc9b91e17bd00c3f3d8c968497c843ad98b"}, + {file = "trove_classifiers-2024.5.22-py3-none-any.whl", hash = "sha256:c43ade18704823e4afa3d9db7083294bc4708a5e02afbcefacd0e9d03a7a24ef"}, + {file = "trove_classifiers-2024.5.22.tar.gz", hash = "sha256:8a6242bbb5c9ae88d34cf665e816b287d2212973c8777dfaef5ec18d72ac1d03"}, ] [[package]] @@ -6215,13 +6347,13 @@ files = [ [[package]] name = "typing-extensions" -version = "4.11.0" +version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, - {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [[package]] @@ -6235,78 +6367,109 @@ files = [ {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, ] +[[package]] +name = "tzlocal" +version = "5.2" +description = "tzinfo object for the local timezone" +optional = true +python-versions = ">=3.8" +files = [ + {file = "tzlocal-5.2-py3-none-any.whl", hash = "sha256:49816ef2fe65ea8ac19d19aa7a1ae0551c834303d5014c6d5a62e4cbda8047b8"}, + {file = "tzlocal-5.2.tar.gz", hash = "sha256:8d399205578f1a9342816409cc1e46a93ebd5755e39ea2d85334bea911bf0e6e"}, +] + +[package.dependencies] +"backports.zoneinfo" = {version = "*", markers = "python_version < \"3.9\""} +tzdata = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +devenv = ["check-manifest", "pytest (>=4.3)", "pytest-cov", "pytest-mock (>=3.3)", "zest.releaser"] + [[package]] name = "ujson" -version = "5.9.0" +version = "5.10.0" description = "Ultra fast JSON encoder and decoder for Python" optional = false python-versions = ">=3.8" files = [ - {file = "ujson-5.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ab71bf27b002eaf7d047c54a68e60230fbd5cd9da60de7ca0aa87d0bccead8fa"}, - {file = "ujson-5.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a365eac66f5aa7a7fdf57e5066ada6226700884fc7dce2ba5483538bc16c8c5"}, - {file = "ujson-5.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e015122b337858dba5a3dc3533af2a8fc0410ee9e2374092f6a5b88b182e9fcc"}, - {file = "ujson-5.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:779a2a88c53039bebfbccca934430dabb5c62cc179e09a9c27a322023f363e0d"}, - {file = "ujson-5.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10ca3c41e80509fd9805f7c149068fa8dbee18872bbdc03d7cca928926a358d5"}, - {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a566e465cb2fcfdf040c2447b7dd9718799d0d90134b37a20dff1e27c0e9096"}, - {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f833c529e922577226a05bc25b6a8b3eb6c4fb155b72dd88d33de99d53113124"}, - {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b68a0caab33f359b4cbbc10065c88e3758c9f73a11a65a91f024b2e7a1257106"}, - {file = "ujson-5.9.0-cp310-cp310-win32.whl", hash = "sha256:7cc7e605d2aa6ae6b7321c3ae250d2e050f06082e71ab1a4200b4ae64d25863c"}, - {file = "ujson-5.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:a6d3f10eb8ccba4316a6b5465b705ed70a06011c6f82418b59278fbc919bef6f"}, - {file = "ujson-5.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b23bbb46334ce51ddb5dded60c662fbf7bb74a37b8f87221c5b0fec1ec6454b"}, - {file = "ujson-5.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6974b3a7c17bbf829e6c3bfdc5823c67922e44ff169851a755eab79a3dd31ec0"}, - {file = "ujson-5.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5964ea916edfe24af1f4cc68488448fbb1ec27a3ddcddc2b236da575c12c8ae"}, - {file = "ujson-5.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ba7cac47dd65ff88571eceeff48bf30ed5eb9c67b34b88cb22869b7aa19600d"}, - {file = "ujson-5.9.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6bbd91a151a8f3358c29355a491e915eb203f607267a25e6ab10531b3b157c5e"}, - {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:829a69d451a49c0de14a9fecb2a2d544a9b2c884c2b542adb243b683a6f15908"}, - {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:a807ae73c46ad5db161a7e883eec0fbe1bebc6a54890152ccc63072c4884823b"}, - {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8fc2aa18b13d97b3c8ccecdf1a3c405f411a6e96adeee94233058c44ff92617d"}, - {file = "ujson-5.9.0-cp311-cp311-win32.whl", hash = "sha256:70e06849dfeb2548be48fdd3ceb53300640bc8100c379d6e19d78045e9c26120"}, - {file = "ujson-5.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:7309d063cd392811acc49b5016728a5e1b46ab9907d321ebbe1c2156bc3c0b99"}, - {file = "ujson-5.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:20509a8c9f775b3a511e308bbe0b72897ba6b800767a7c90c5cca59d20d7c42c"}, - {file = "ujson-5.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b28407cfe315bd1b34f1ebe65d3bd735d6b36d409b334100be8cdffae2177b2f"}, - {file = "ujson-5.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d302bd17989b6bd90d49bade66943c78f9e3670407dbc53ebcf61271cadc399"}, - {file = "ujson-5.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f21315f51e0db8ee245e33a649dd2d9dce0594522de6f278d62f15f998e050e"}, - {file = "ujson-5.9.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5635b78b636a54a86fdbf6f027e461aa6c6b948363bdf8d4fbb56a42b7388320"}, - {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:82b5a56609f1235d72835ee109163c7041b30920d70fe7dac9176c64df87c164"}, - {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5ca35f484622fd208f55041b042d9d94f3b2c9c5add4e9af5ee9946d2d30db01"}, - {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:829b824953ebad76d46e4ae709e940bb229e8999e40881338b3cc94c771b876c"}, - {file = "ujson-5.9.0-cp312-cp312-win32.whl", hash = "sha256:25fa46e4ff0a2deecbcf7100af3a5d70090b461906f2299506485ff31d9ec437"}, - {file = "ujson-5.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:60718f1720a61560618eff3b56fd517d107518d3c0160ca7a5a66ac949c6cf1c"}, - {file = "ujson-5.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d581db9db9e41d8ea0b2705c90518ba623cbdc74f8d644d7eb0d107be0d85d9c"}, - {file = "ujson-5.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ff741a5b4be2d08fceaab681c9d4bc89abf3c9db600ab435e20b9b6d4dfef12e"}, - {file = "ujson-5.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdcb02cabcb1e44381221840a7af04433c1dc3297af76fde924a50c3054c708c"}, - {file = "ujson-5.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e208d3bf02c6963e6ef7324dadf1d73239fb7008491fdf523208f60be6437402"}, - {file = "ujson-5.9.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4b3917296630a075e04d3d07601ce2a176479c23af838b6cf90a2d6b39b0d95"}, - {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0c4d6adb2c7bb9eb7c71ad6f6f612e13b264942e841f8cc3314a21a289a76c4e"}, - {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0b159efece9ab5c01f70b9d10bbb77241ce111a45bc8d21a44c219a2aec8ddfd"}, - {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0cb4a7814940ddd6619bdce6be637a4b37a8c4760de9373bac54bb7b229698b"}, - {file = "ujson-5.9.0-cp38-cp38-win32.whl", hash = "sha256:dc80f0f5abf33bd7099f7ac94ab1206730a3c0a2d17549911ed2cb6b7aa36d2d"}, - {file = "ujson-5.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:506a45e5fcbb2d46f1a51fead991c39529fc3737c0f5d47c9b4a1d762578fc30"}, - {file = "ujson-5.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0fd2eba664a22447102062814bd13e63c6130540222c0aa620701dd01f4be81"}, - {file = "ujson-5.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bdf7fc21a03bafe4ba208dafa84ae38e04e5d36c0e1c746726edf5392e9f9f36"}, - {file = "ujson-5.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2f909bc08ce01f122fd9c24bc6f9876aa087188dfaf3c4116fe6e4daf7e194f"}, - {file = "ujson-5.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd4ea86c2afd41429751d22a3ccd03311c067bd6aeee2d054f83f97e41e11d8f"}, - {file = "ujson-5.9.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:63fb2e6599d96fdffdb553af0ed3f76b85fda63281063f1cb5b1141a6fcd0617"}, - {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:32bba5870c8fa2a97f4a68f6401038d3f1922e66c34280d710af00b14a3ca562"}, - {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:37ef92e42535a81bf72179d0e252c9af42a4ed966dc6be6967ebfb929a87bc60"}, - {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f69f16b8f1c69da00e38dc5f2d08a86b0e781d0ad3e4cc6a13ea033a439c4844"}, - {file = "ujson-5.9.0-cp39-cp39-win32.whl", hash = "sha256:3382a3ce0ccc0558b1c1668950008cece9bf463ebb17463ebf6a8bfc060dae34"}, - {file = "ujson-5.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:6adef377ed583477cf005b58c3025051b5faa6b8cc25876e594afbb772578f21"}, - {file = "ujson-5.9.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ffdfebd819f492e48e4f31c97cb593b9c1a8251933d8f8972e81697f00326ff1"}, - {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4eec2ddc046360d087cf35659c7ba0cbd101f32035e19047013162274e71fcf"}, - {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbb90aa5c23cb3d4b803c12aa220d26778c31b6e4b7a13a1f49971f6c7d088e"}, - {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba0823cb70866f0d6a4ad48d998dd338dce7314598721bc1b7986d054d782dfd"}, - {file = "ujson-5.9.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4e35d7885ed612feb6b3dd1b7de28e89baaba4011ecdf995e88be9ac614765e9"}, - {file = "ujson-5.9.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b048aa93eace8571eedbd67b3766623e7f0acbf08ee291bef7d8106210432427"}, - {file = "ujson-5.9.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:323279e68c195110ef85cbe5edce885219e3d4a48705448720ad925d88c9f851"}, - {file = "ujson-5.9.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ac92d86ff34296f881e12aa955f7014d276895e0e4e868ba7fddebbde38e378"}, - {file = "ujson-5.9.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:6eecbd09b316cea1fd929b1e25f70382917542ab11b692cb46ec9b0a26c7427f"}, - {file = "ujson-5.9.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:473fb8dff1d58f49912323d7cb0859df5585cfc932e4b9c053bf8cf7f2d7c5c4"}, - {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f91719c6abafe429c1a144cfe27883eace9fb1c09a9c5ef1bcb3ae80a3076a4e"}, - {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b1c0991c4fe256f5fdb19758f7eac7f47caac29a6c57d0de16a19048eb86bad"}, - {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a8ea0f55a1396708e564595aaa6696c0d8af532340f477162ff6927ecc46e21"}, - {file = "ujson-5.9.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:07e0cfdde5fd91f54cd2d7ffb3482c8ff1bf558abf32a8b953a5d169575ae1cd"}, - {file = "ujson-5.9.0.tar.gz", hash = "sha256:89cc92e73d5501b8a7f48575eeb14ad27156ad092c2e9fc7e3cf949f07e75532"}, + {file = "ujson-5.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2601aa9ecdbee1118a1c2065323bda35e2c5a2cf0797ef4522d485f9d3ef65bd"}, + {file = "ujson-5.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:348898dd702fc1c4f1051bc3aacbf894caa0927fe2c53e68679c073375f732cf"}, + {file = "ujson-5.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22cffecf73391e8abd65ef5f4e4dd523162a3399d5e84faa6aebbf9583df86d6"}, + {file = "ujson-5.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26b0e2d2366543c1bb4fbd457446f00b0187a2bddf93148ac2da07a53fe51569"}, + {file = "ujson-5.10.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:caf270c6dba1be7a41125cd1e4fc7ba384bf564650beef0df2dd21a00b7f5770"}, + {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a245d59f2ffe750446292b0094244df163c3dc96b3ce152a2c837a44e7cda9d1"}, + {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:94a87f6e151c5f483d7d54ceef83b45d3a9cca7a9cb453dbdbb3f5a6f64033f5"}, + {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:29b443c4c0a113bcbb792c88bea67b675c7ca3ca80c3474784e08bba01c18d51"}, + {file = "ujson-5.10.0-cp310-cp310-win32.whl", hash = "sha256:c18610b9ccd2874950faf474692deee4223a994251bc0a083c114671b64e6518"}, + {file = "ujson-5.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:924f7318c31874d6bb44d9ee1900167ca32aa9b69389b98ecbde34c1698a250f"}, + {file = "ujson-5.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a5b366812c90e69d0f379a53648be10a5db38f9d4ad212b60af00bd4048d0f00"}, + {file = "ujson-5.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:502bf475781e8167f0f9d0e41cd32879d120a524b22358e7f205294224c71126"}, + {file = "ujson-5.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b91b5d0d9d283e085e821651184a647699430705b15bf274c7896f23fe9c9d8"}, + {file = "ujson-5.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:129e39af3a6d85b9c26d5577169c21d53821d8cf68e079060602e861c6e5da1b"}, + {file = "ujson-5.10.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f77b74475c462cb8b88680471193064d3e715c7c6074b1c8c412cb526466efe9"}, + {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7ec0ca8c415e81aa4123501fee7f761abf4b7f386aad348501a26940beb1860f"}, + {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab13a2a9e0b2865a6c6db9271f4b46af1c7476bfd51af1f64585e919b7c07fd4"}, + {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:57aaf98b92d72fc70886b5a0e1a1ca52c2320377360341715dd3933a18e827b1"}, + {file = "ujson-5.10.0-cp311-cp311-win32.whl", hash = "sha256:2987713a490ceb27edff77fb184ed09acdc565db700ee852823c3dc3cffe455f"}, + {file = "ujson-5.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:f00ea7e00447918ee0eff2422c4add4c5752b1b60e88fcb3c067d4a21049a720"}, + {file = "ujson-5.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98ba15d8cbc481ce55695beee9f063189dce91a4b08bc1d03e7f0152cd4bbdd5"}, + {file = "ujson-5.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9d2edbf1556e4f56e50fab7d8ff993dbad7f54bac68eacdd27a8f55f433578e"}, + {file = "ujson-5.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6627029ae4f52d0e1a2451768c2c37c0c814ffc04f796eb36244cf16b8e57043"}, + {file = "ujson-5.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8ccb77b3e40b151e20519c6ae6d89bfe3f4c14e8e210d910287f778368bb3d1"}, + {file = "ujson-5.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3caf9cd64abfeb11a3b661329085c5e167abbe15256b3b68cb5d914ba7396f3"}, + {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6e32abdce572e3a8c3d02c886c704a38a1b015a1fb858004e03d20ca7cecbb21"}, + {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a65b6af4d903103ee7b6f4f5b85f1bfd0c90ba4eeac6421aae436c9988aa64a2"}, + {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:604a046d966457b6cdcacc5aa2ec5314f0e8c42bae52842c1e6fa02ea4bda42e"}, + {file = "ujson-5.10.0-cp312-cp312-win32.whl", hash = "sha256:6dea1c8b4fc921bf78a8ff00bbd2bfe166345f5536c510671bccececb187c80e"}, + {file = "ujson-5.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:38665e7d8290188b1e0d57d584eb8110951a9591363316dd41cf8686ab1d0abc"}, + {file = "ujson-5.10.0-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:618efd84dc1acbd6bff8eaa736bb6c074bfa8b8a98f55b61c38d4ca2c1f7f287"}, + {file = "ujson-5.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38d5d36b4aedfe81dfe251f76c0467399d575d1395a1755de391e58985ab1c2e"}, + {file = "ujson-5.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67079b1f9fb29ed9a2914acf4ef6c02844b3153913eb735d4bf287ee1db6e557"}, + {file = "ujson-5.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7d0e0ceeb8fe2468c70ec0c37b439dd554e2aa539a8a56365fd761edb418988"}, + {file = "ujson-5.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:59e02cd37bc7c44d587a0ba45347cc815fb7a5fe48de16bf05caa5f7d0d2e816"}, + {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2a890b706b64e0065f02577bf6d8ca3b66c11a5e81fb75d757233a38c07a1f20"}, + {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:621e34b4632c740ecb491efc7f1fcb4f74b48ddb55e65221995e74e2d00bbff0"}, + {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b9500e61fce0cfc86168b248104e954fead61f9be213087153d272e817ec7b4f"}, + {file = "ujson-5.10.0-cp313-cp313-win32.whl", hash = "sha256:4c4fc16f11ac1612f05b6f5781b384716719547e142cfd67b65d035bd85af165"}, + {file = "ujson-5.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:4573fd1695932d4f619928fd09d5d03d917274381649ade4328091ceca175539"}, + {file = "ujson-5.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a984a3131da7f07563057db1c3020b1350a3e27a8ec46ccbfbf21e5928a43050"}, + {file = "ujson-5.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:73814cd1b9db6fc3270e9d8fe3b19f9f89e78ee9d71e8bd6c9a626aeaeaf16bd"}, + {file = "ujson-5.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61e1591ed9376e5eddda202ec229eddc56c612b61ac6ad07f96b91460bb6c2fb"}, + {file = "ujson-5.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2c75269f8205b2690db4572a4a36fe47cd1338e4368bc73a7a0e48789e2e35a"}, + {file = "ujson-5.10.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7223f41e5bf1f919cd8d073e35b229295aa8e0f7b5de07ed1c8fddac63a6bc5d"}, + {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc2fd6b3067c0782e7002ac3b38cf48608ee6366ff176bbd02cf969c9c20fe"}, + {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:232cc85f8ee3c454c115455195a205074a56ff42608fd6b942aa4c378ac14dd7"}, + {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:cc6139531f13148055d691e442e4bc6601f6dba1e6d521b1585d4788ab0bfad4"}, + {file = "ujson-5.10.0-cp38-cp38-win32.whl", hash = "sha256:e7ce306a42b6b93ca47ac4a3b96683ca554f6d35dd8adc5acfcd55096c8dfcb8"}, + {file = "ujson-5.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:e82d4bb2138ab05e18f089a83b6564fee28048771eb63cdecf4b9b549de8a2cc"}, + {file = "ujson-5.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dfef2814c6b3291c3c5f10065f745a1307d86019dbd7ea50e83504950136ed5b"}, + {file = "ujson-5.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4734ee0745d5928d0ba3a213647f1c4a74a2a28edc6d27b2d6d5bd9fa4319e27"}, + {file = "ujson-5.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d47ebb01bd865fdea43da56254a3930a413f0c5590372a1241514abae8aa7c76"}, + {file = "ujson-5.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dee5e97c2496874acbf1d3e37b521dd1f307349ed955e62d1d2f05382bc36dd5"}, + {file = "ujson-5.10.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7490655a2272a2d0b072ef16b0b58ee462f4973a8f6bbe64917ce5e0a256f9c0"}, + {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ba17799fcddaddf5c1f75a4ba3fd6441f6a4f1e9173f8a786b42450851bd74f1"}, + {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2aff2985cef314f21d0fecc56027505804bc78802c0121343874741650a4d3d1"}, + {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ad88ac75c432674d05b61184178635d44901eb749786c8eb08c102330e6e8996"}, + {file = "ujson-5.10.0-cp39-cp39-win32.whl", hash = "sha256:2544912a71da4ff8c4f7ab5606f947d7299971bdd25a45e008e467ca638d13c9"}, + {file = "ujson-5.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:3ff201d62b1b177a46f113bb43ad300b424b7847f9c5d38b1b4ad8f75d4a282a"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5b6fee72fa77dc172a28f21693f64d93166534c263adb3f96c413ccc85ef6e64"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:61d0af13a9af01d9f26d2331ce49bb5ac1fb9c814964018ac8df605b5422dcb3"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecb24f0bdd899d368b715c9e6664166cf694d1e57be73f17759573a6986dd95a"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fbd8fd427f57a03cff3ad6574b5e299131585d9727c8c366da4624a9069ed746"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beeaf1c48e32f07d8820c705ff8e645f8afa690cca1544adba4ebfa067efdc88"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:baed37ea46d756aca2955e99525cc02d9181de67f25515c468856c38d52b5f3b"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7663960f08cd5a2bb152f5ee3992e1af7690a64c0e26d31ba7b3ff5b2ee66337"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:d8640fb4072d36b08e95a3a380ba65779d356b2fee8696afeb7794cf0902d0a1"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78778a3aa7aafb11e7ddca4e29f46bc5139131037ad628cc10936764282d6753"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0111b27f2d5c820e7f2dbad7d48e3338c824e7ac4d2a12da3dc6061cc39c8e6"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:c66962ca7565605b355a9ed478292da628b8f18c0f2793021ca4425abf8b01e5"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ba43cc34cce49cf2d4bc76401a754a81202d8aa926d0e2b79f0ee258cb15d3a4"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:ac56eb983edce27e7f51d05bc8dd820586c6e6be1c5216a6809b0c668bb312b8"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44bd4b23a0e723bf8b10628288c2c7c335161d6840013d4d5de20e48551773b"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c10f4654e5326ec14a46bcdeb2b685d4ada6911050aa8baaf3501e57024b804"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0de4971a89a762398006e844ae394bd46991f7c385d7a6a3b93ba229e6dac17e"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e1402f0564a97d2a52310ae10a64d25bcef94f8dd643fcf5d310219d915484f7"}, + {file = "ujson-5.10.0.tar.gz", hash = "sha256:b3cd8f3c5d8c7738257f1018880444f7b7d9b66232c64649f562d7ba86ad4bc1"}, ] [[package]] @@ -6325,13 +6488,13 @@ dev = ["flake8", "flake8-annotations", "flake8-bandit", "flake8-bugbear", "flake [[package]] name = "urllib3" -version = "1.26.18" +version = "1.26.19" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ - {file = "urllib3-1.26.18-py2.py3-none-any.whl", hash = "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07"}, - {file = "urllib3-1.26.18.tar.gz", hash = "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0"}, + {file = "urllib3-1.26.19-py2.py3-none-any.whl", hash = "sha256:37a0344459b199fce0e80b0d3569837ec6b6937435c5244e7fd73fa6006830f3"}, + {file = "urllib3-1.26.19.tar.gz", hash = "sha256:3e3d753a8618b86d7de333b4223005f68720bcd6a7d2bcb9fbd2229ec7c1e429"}, ] [package.extras] @@ -6341,13 +6504,13 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "uvicorn" -version = "0.29.0" +version = "0.30.1" description = "The lightning-fast ASGI server." optional = false python-versions = ">=3.8" files = [ - {file = "uvicorn-0.29.0-py3-none-any.whl", hash = "sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de"}, - {file = "uvicorn-0.29.0.tar.gz", hash = "sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0"}, + {file = "uvicorn-0.30.1-py3-none-any.whl", hash = "sha256:cd17daa7f3b9d7a24de3617820e634d0933b69eed8e33a516071174427238c81"}, + {file = "uvicorn-0.30.1.tar.gz", hash = "sha256:d46cd8e0fd80240baffbcd9ec1012a712938754afcf81bce56c024c1656aece8"}, ] [package.dependencies] @@ -6411,13 +6574,13 @@ test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)" [[package]] name = "virtualenv" -version = "20.26.1" +version = "20.26.2" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.26.1-py3-none-any.whl", hash = "sha256:7aa9982a728ae5892558bff6a2839c00b9ed145523ece2274fad6f414690ae75"}, - {file = "virtualenv-20.26.1.tar.gz", hash = "sha256:604bfdceaeece392802e6ae48e69cec49168b9c5f4a44e483963f9242eb0e78b"}, + {file = "virtualenv-20.26.2-py3-none-any.whl", hash = "sha256:a624db5e94f01ad993d476b9ee5346fdf7b9de43ccaee0e0197012dc838a0e9b"}, + {file = "virtualenv-20.26.2.tar.gz", hash = "sha256:82bf0f4eebbb78d36ddaee0283d43fe5736b53880b8a8cdcd37390a07ac3741c"}, ] [package.dependencies] @@ -6431,40 +6594,43 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess [[package]] name = "watchdog" -version = "4.0.0" +version = "4.0.1" description = "Filesystem events monitoring" optional = false python-versions = ">=3.8" files = [ - {file = "watchdog-4.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:39cb34b1f1afbf23e9562501673e7146777efe95da24fab5707b88f7fb11649b"}, - {file = "watchdog-4.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c522392acc5e962bcac3b22b9592493ffd06d1fc5d755954e6be9f4990de932b"}, - {file = "watchdog-4.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6c47bdd680009b11c9ac382163e05ca43baf4127954c5f6d0250e7d772d2b80c"}, - {file = "watchdog-4.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8350d4055505412a426b6ad8c521bc7d367d1637a762c70fdd93a3a0d595990b"}, - {file = "watchdog-4.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c17d98799f32e3f55f181f19dd2021d762eb38fdd381b4a748b9f5a36738e935"}, - {file = "watchdog-4.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4986db5e8880b0e6b7cd52ba36255d4793bf5cdc95bd6264806c233173b1ec0b"}, - {file = "watchdog-4.0.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:11e12fafb13372e18ca1bbf12d50f593e7280646687463dd47730fd4f4d5d257"}, - {file = "watchdog-4.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5369136a6474678e02426bd984466343924d1df8e2fd94a9b443cb7e3aa20d19"}, - {file = "watchdog-4.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76ad8484379695f3fe46228962017a7e1337e9acadafed67eb20aabb175df98b"}, - {file = "watchdog-4.0.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:45cc09cc4c3b43fb10b59ef4d07318d9a3ecdbff03abd2e36e77b6dd9f9a5c85"}, - {file = "watchdog-4.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:eed82cdf79cd7f0232e2fdc1ad05b06a5e102a43e331f7d041e5f0e0a34a51c4"}, - {file = "watchdog-4.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba30a896166f0fee83183cec913298151b73164160d965af2e93a20bbd2ab605"}, - {file = "watchdog-4.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d18d7f18a47de6863cd480734613502904611730f8def45fc52a5d97503e5101"}, - {file = "watchdog-4.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2895bf0518361a9728773083908801a376743bcc37dfa252b801af8fd281b1ca"}, - {file = "watchdog-4.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87e9df830022488e235dd601478c15ad73a0389628588ba0b028cb74eb72fed8"}, - {file = "watchdog-4.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6e949a8a94186bced05b6508faa61b7adacc911115664ccb1923b9ad1f1ccf7b"}, - {file = "watchdog-4.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6a4db54edea37d1058b08947c789a2354ee02972ed5d1e0dca9b0b820f4c7f92"}, - {file = "watchdog-4.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d31481ccf4694a8416b681544c23bd271f5a123162ab603c7d7d2dd7dd901a07"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8fec441f5adcf81dd240a5fe78e3d83767999771630b5ddfc5867827a34fa3d3"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:6a9c71a0b02985b4b0b6d14b875a6c86ddea2fdbebd0c9a720a806a8bbffc69f"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:557ba04c816d23ce98a06e70af6abaa0485f6d94994ec78a42b05d1c03dcbd50"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:d0f9bd1fd919134d459d8abf954f63886745f4660ef66480b9d753a7c9d40927"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:f9b2fdca47dc855516b2d66eef3c39f2672cbf7e7a42e7e67ad2cbfcd6ba107d"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:73c7a935e62033bd5e8f0da33a4dcb763da2361921a69a5a95aaf6c93aa03a87"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6a80d5cae8c265842c7419c560b9961561556c4361b297b4c431903f8c33b269"}, - {file = "watchdog-4.0.0-py3-none-win32.whl", hash = "sha256:8f9a542c979df62098ae9c58b19e03ad3df1c9d8c6895d96c0d51da17b243b1c"}, - {file = "watchdog-4.0.0-py3-none-win_amd64.whl", hash = "sha256:f970663fa4f7e80401a7b0cbeec00fa801bf0287d93d48368fc3e6fa32716245"}, - {file = "watchdog-4.0.0-py3-none-win_ia64.whl", hash = "sha256:9a03e16e55465177d416699331b0f3564138f1807ecc5f2de9d55d8f188d08c7"}, - {file = "watchdog-4.0.0.tar.gz", hash = "sha256:e3e7065cbdabe6183ab82199d7a4f6b3ba0a438c5a512a68559846ccb76a78ec"}, + {file = "watchdog-4.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:da2dfdaa8006eb6a71051795856bedd97e5b03e57da96f98e375682c48850645"}, + {file = "watchdog-4.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e93f451f2dfa433d97765ca2634628b789b49ba8b504fdde5837cdcf25fdb53b"}, + {file = "watchdog-4.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ef0107bbb6a55f5be727cfc2ef945d5676b97bffb8425650dadbb184be9f9a2b"}, + {file = "watchdog-4.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:17e32f147d8bf9657e0922c0940bcde863b894cd871dbb694beb6704cfbd2fb5"}, + {file = "watchdog-4.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:03e70d2df2258fb6cb0e95bbdbe06c16e608af94a3ffbd2b90c3f1e83eb10767"}, + {file = "watchdog-4.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:123587af84260c991dc5f62a6e7ef3d1c57dfddc99faacee508c71d287248459"}, + {file = "watchdog-4.0.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:093b23e6906a8b97051191a4a0c73a77ecc958121d42346274c6af6520dec175"}, + {file = "watchdog-4.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:611be3904f9843f0529c35a3ff3fd617449463cb4b73b1633950b3d97fa4bfb7"}, + {file = "watchdog-4.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:62c613ad689ddcb11707f030e722fa929f322ef7e4f18f5335d2b73c61a85c28"}, + {file = "watchdog-4.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d4925e4bf7b9bddd1c3de13c9b8a2cdb89a468f640e66fbfabaf735bd85b3e35"}, + {file = "watchdog-4.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cad0bbd66cd59fc474b4a4376bc5ac3fc698723510cbb64091c2a793b18654db"}, + {file = "watchdog-4.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a3c2c317a8fb53e5b3d25790553796105501a235343f5d2bf23bb8649c2c8709"}, + {file = "watchdog-4.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c9904904b6564d4ee8a1ed820db76185a3c96e05560c776c79a6ce5ab71888ba"}, + {file = "watchdog-4.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:667f3c579e813fcbad1b784db7a1aaa96524bed53437e119f6a2f5de4db04235"}, + {file = "watchdog-4.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d10a681c9a1d5a77e75c48a3b8e1a9f2ae2928eda463e8d33660437705659682"}, + {file = "watchdog-4.0.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0144c0ea9997b92615af1d94afc0c217e07ce2c14912c7b1a5731776329fcfc7"}, + {file = "watchdog-4.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:998d2be6976a0ee3a81fb8e2777900c28641fb5bfbd0c84717d89bca0addcdc5"}, + {file = "watchdog-4.0.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e7921319fe4430b11278d924ef66d4daa469fafb1da679a2e48c935fa27af193"}, + {file = "watchdog-4.0.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:f0de0f284248ab40188f23380b03b59126d1479cd59940f2a34f8852db710625"}, + {file = "watchdog-4.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bca36be5707e81b9e6ce3208d92d95540d4ca244c006b61511753583c81c70dd"}, + {file = "watchdog-4.0.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:ab998f567ebdf6b1da7dc1e5accfaa7c6992244629c0fdaef062f43249bd8dee"}, + {file = "watchdog-4.0.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:dddba7ca1c807045323b6af4ff80f5ddc4d654c8bce8317dde1bd96b128ed253"}, + {file = "watchdog-4.0.1-py3-none-manylinux2014_armv7l.whl", hash = "sha256:4513ec234c68b14d4161440e07f995f231be21a09329051e67a2118a7a612d2d"}, + {file = "watchdog-4.0.1-py3-none-manylinux2014_i686.whl", hash = "sha256:4107ac5ab936a63952dea2a46a734a23230aa2f6f9db1291bf171dac3ebd53c6"}, + {file = "watchdog-4.0.1-py3-none-manylinux2014_ppc64.whl", hash = "sha256:6e8c70d2cd745daec2a08734d9f63092b793ad97612470a0ee4cbb8f5f705c57"}, + {file = "watchdog-4.0.1-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:f27279d060e2ab24c0aa98363ff906d2386aa6c4dc2f1a374655d4e02a6c5e5e"}, + {file = "watchdog-4.0.1-py3-none-manylinux2014_s390x.whl", hash = "sha256:f8affdf3c0f0466e69f5b3917cdd042f89c8c63aebdb9f7c078996f607cdb0f5"}, + {file = "watchdog-4.0.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ac7041b385f04c047fcc2951dc001671dee1b7e0615cde772e84b01fbf68ee84"}, + {file = "watchdog-4.0.1-py3-none-win32.whl", hash = "sha256:206afc3d964f9a233e6ad34618ec60b9837d0582b500b63687e34011e15bb429"}, + {file = "watchdog-4.0.1-py3-none-win_amd64.whl", hash = "sha256:7577b3c43e5909623149f76b099ac49a1a01ca4e167d1785c76eb52fa585745a"}, + {file = "watchdog-4.0.1-py3-none-win_ia64.whl", hash = "sha256:d7b9f5f3299e8dd230880b6c55504a1f69cf1e4316275d1b215ebdd8187ec88d"}, + {file = "watchdog-4.0.1.tar.gz", hash = "sha256:eebaacf674fa25511e8867028d281e602ee6500045b57f43b08778082f7f8b44"}, ] [package.extras] @@ -6472,86 +6638,86 @@ watchmedo = ["PyYAML (>=3.10)"] [[package]] name = "watchfiles" -version = "0.21.0" +version = "0.22.0" description = "Simple, modern and high performance file watching and code reload in python." optional = false python-versions = ">=3.8" files = [ - {file = "watchfiles-0.21.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:27b4035013f1ea49c6c0b42d983133b136637a527e48c132d368eb19bf1ac6aa"}, - {file = "watchfiles-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c81818595eff6e92535ff32825f31c116f867f64ff8cdf6562cd1d6b2e1e8f3e"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6c107ea3cf2bd07199d66f156e3ea756d1b84dfd43b542b2d870b77868c98c03"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d9ac347653ebd95839a7c607608703b20bc07e577e870d824fa4801bc1cb124"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5eb86c6acb498208e7663ca22dbe68ca2cf42ab5bf1c776670a50919a56e64ab"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f564bf68404144ea6b87a78a3f910cc8de216c6b12a4cf0b27718bf4ec38d303"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d0f32ebfaa9c6011f8454994f86108c2eb9c79b8b7de00b36d558cadcedaa3d"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d45d9b699ecbac6c7bd8e0a2609767491540403610962968d258fd6405c17c"}, - {file = "watchfiles-0.21.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:aff06b2cac3ef4616e26ba17a9c250c1fe9dd8a5d907d0193f84c499b1b6e6a9"}, - {file = "watchfiles-0.21.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d9792dff410f266051025ecfaa927078b94cc7478954b06796a9756ccc7e14a9"}, - {file = "watchfiles-0.21.0-cp310-none-win32.whl", hash = "sha256:214cee7f9e09150d4fb42e24919a1e74d8c9b8a9306ed1474ecaddcd5479c293"}, - {file = "watchfiles-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:1ad7247d79f9f55bb25ab1778fd47f32d70cf36053941f07de0b7c4e96b5d235"}, - {file = "watchfiles-0.21.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:668c265d90de8ae914f860d3eeb164534ba2e836811f91fecc7050416ee70aa7"}, - {file = "watchfiles-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a23092a992e61c3a6a70f350a56db7197242f3490da9c87b500f389b2d01eef"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e7941bbcfdded9c26b0bf720cb7e6fd803d95a55d2c14b4bd1f6a2772230c586"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11cd0c3100e2233e9c53106265da31d574355c288e15259c0d40a4405cbae317"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78f30cbe8b2ce770160d3c08cff01b2ae9306fe66ce899b73f0409dc1846c1b"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6674b00b9756b0af620aa2a3346b01f8e2a3dc729d25617e1b89cf6af4a54eb1"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd7ac678b92b29ba630d8c842d8ad6c555abda1b9ef044d6cc092dacbfc9719d"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c873345680c1b87f1e09e0eaf8cf6c891b9851d8b4d3645e7efe2ec20a20cc7"}, - {file = "watchfiles-0.21.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49f56e6ecc2503e7dbe233fa328b2be1a7797d31548e7a193237dcdf1ad0eee0"}, - {file = "watchfiles-0.21.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:02d91cbac553a3ad141db016e3350b03184deaafeba09b9d6439826ee594b365"}, - {file = "watchfiles-0.21.0-cp311-none-win32.whl", hash = "sha256:ebe684d7d26239e23d102a2bad2a358dedf18e462e8808778703427d1f584400"}, - {file = "watchfiles-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:4566006aa44cb0d21b8ab53baf4b9c667a0ed23efe4aaad8c227bfba0bf15cbe"}, - {file = "watchfiles-0.21.0-cp311-none-win_arm64.whl", hash = "sha256:c550a56bf209a3d987d5a975cdf2063b3389a5d16caf29db4bdddeae49f22078"}, - {file = "watchfiles-0.21.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:51ddac60b96a42c15d24fbdc7a4bfcd02b5a29c047b7f8bf63d3f6f5a860949a"}, - {file = "watchfiles-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:511f0b034120cd1989932bf1e9081aa9fb00f1f949fbd2d9cab6264916ae89b1"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cfb92d49dbb95ec7a07511bc9efb0faff8fe24ef3805662b8d6808ba8409a71a"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f92944efc564867bbf841c823c8b71bb0be75e06b8ce45c084b46411475a915"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:642d66b75eda909fd1112d35c53816d59789a4b38c141a96d62f50a3ef9b3360"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d23bcd6c8eaa6324fe109d8cac01b41fe9a54b8c498af9ce464c1aeeb99903d6"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18d5b4da8cf3e41895b34e8c37d13c9ed294954907929aacd95153508d5d89d7"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b8d1eae0f65441963d805f766c7e9cd092f91e0c600c820c764a4ff71a0764c"}, - {file = "watchfiles-0.21.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1fd9a5205139f3c6bb60d11f6072e0552f0a20b712c85f43d42342d162be1235"}, - {file = "watchfiles-0.21.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a1e3014a625bcf107fbf38eece0e47fa0190e52e45dc6eee5a8265ddc6dc5ea7"}, - {file = "watchfiles-0.21.0-cp312-none-win32.whl", hash = "sha256:9d09869f2c5a6f2d9df50ce3064b3391d3ecb6dced708ad64467b9e4f2c9bef3"}, - {file = "watchfiles-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:18722b50783b5e30a18a8a5db3006bab146d2b705c92eb9a94f78c72beb94094"}, - {file = "watchfiles-0.21.0-cp312-none-win_arm64.whl", hash = "sha256:a3b9bec9579a15fb3ca2d9878deae789df72f2b0fdaf90ad49ee389cad5edab6"}, - {file = "watchfiles-0.21.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:4ea10a29aa5de67de02256a28d1bf53d21322295cb00bd2d57fcd19b850ebd99"}, - {file = "watchfiles-0.21.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:40bca549fdc929b470dd1dbfcb47b3295cb46a6d2c90e50588b0a1b3bd98f429"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9b37a7ba223b2f26122c148bb8d09a9ff312afca998c48c725ff5a0a632145f7"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec8c8900dc5c83650a63dd48c4d1d245343f904c4b64b48798c67a3767d7e165"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ad3fe0a3567c2f0f629d800409cd528cb6251da12e81a1f765e5c5345fd0137"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d353c4cfda586db2a176ce42c88f2fc31ec25e50212650c89fdd0f560ee507b"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:83a696da8922314ff2aec02987eefb03784f473281d740bf9170181829133765"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a03651352fc20975ee2a707cd2d74a386cd303cc688f407296064ad1e6d1562"}, - {file = "watchfiles-0.21.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3ad692bc7792be8c32918c699638b660c0de078a6cbe464c46e1340dadb94c19"}, - {file = "watchfiles-0.21.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06247538e8253975bdb328e7683f8515ff5ff041f43be6c40bff62d989b7d0b0"}, - {file = "watchfiles-0.21.0-cp38-none-win32.whl", hash = "sha256:9a0aa47f94ea9a0b39dd30850b0adf2e1cd32a8b4f9c7aa443d852aacf9ca214"}, - {file = "watchfiles-0.21.0-cp38-none-win_amd64.whl", hash = "sha256:8d5f400326840934e3507701f9f7269247f7c026d1b6cfd49477d2be0933cfca"}, - {file = "watchfiles-0.21.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:7f762a1a85a12cc3484f77eee7be87b10f8c50b0b787bb02f4e357403cad0c0e"}, - {file = "watchfiles-0.21.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6e9be3ef84e2bb9710f3f777accce25556f4a71e15d2b73223788d528fcc2052"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4c48a10d17571d1275701e14a601e36959ffada3add8cdbc9e5061a6e3579a5d"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c889025f59884423428c261f212e04d438de865beda0b1e1babab85ef4c0f01"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:66fac0c238ab9a2e72d026b5fb91cb902c146202bbd29a9a1a44e8db7b710b6f"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4a21f71885aa2744719459951819e7bf5a906a6448a6b2bbce8e9cc9f2c8128"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c9198c989f47898b2c22201756f73249de3748e0fc9de44adaf54a8b259cc0c"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f57c4461cd24fda22493109c45b3980863c58a25b8bec885ca8bea6b8d4b28"}, - {file = "watchfiles-0.21.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:853853cbf7bf9408b404754b92512ebe3e3a83587503d766d23e6bf83d092ee6"}, - {file = "watchfiles-0.21.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d5b1dc0e708fad9f92c296ab2f948af403bf201db8fb2eb4c8179db143732e49"}, - {file = "watchfiles-0.21.0-cp39-none-win32.whl", hash = "sha256:59137c0c6826bd56c710d1d2bda81553b5e6b7c84d5a676747d80caf0409ad94"}, - {file = "watchfiles-0.21.0-cp39-none-win_amd64.whl", hash = "sha256:6cb8fdc044909e2078c248986f2fc76f911f72b51ea4a4fbbf472e01d14faa58"}, - {file = "watchfiles-0.21.0-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab03a90b305d2588e8352168e8c5a1520b721d2d367f31e9332c4235b30b8994"}, - {file = "watchfiles-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:927c589500f9f41e370b0125c12ac9e7d3a2fd166b89e9ee2828b3dda20bfe6f"}, - {file = "watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bd467213195e76f838caf2c28cd65e58302d0254e636e7c0fca81efa4a2e62c"}, - {file = "watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02b73130687bc3f6bb79d8a170959042eb56eb3a42df3671c79b428cd73f17cc"}, - {file = "watchfiles-0.21.0-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:08dca260e85ffae975448e344834d765983237ad6dc308231aa16e7933db763e"}, - {file = "watchfiles-0.21.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:3ccceb50c611c433145502735e0370877cced72a6c70fd2410238bcbc7fe51d8"}, - {file = "watchfiles-0.21.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57d430f5fb63fea141ab71ca9c064e80de3a20b427ca2febcbfcef70ff0ce895"}, - {file = "watchfiles-0.21.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dd5fad9b9c0dd89904bbdea978ce89a2b692a7ee8a0ce19b940e538c88a809c"}, - {file = "watchfiles-0.21.0-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:be6dd5d52b73018b21adc1c5d28ac0c68184a64769052dfeb0c5d9998e7f56a2"}, - {file = "watchfiles-0.21.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b3cab0e06143768499384a8a5efb9c4dc53e19382952859e4802f294214f36ec"}, - {file = "watchfiles-0.21.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c6ed10c2497e5fedadf61e465b3ca12a19f96004c15dcffe4bd442ebadc2d85"}, - {file = "watchfiles-0.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43babacef21c519bc6631c5fce2a61eccdfc011b4bcb9047255e9620732c8097"}, - {file = "watchfiles-0.21.0.tar.gz", hash = "sha256:c76c635fabf542bb78524905718c39f736a98e5ab25b23ec6d4abede1a85a6a3"}, + {file = "watchfiles-0.22.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:da1e0a8caebf17976e2ffd00fa15f258e14749db5e014660f53114b676e68538"}, + {file = "watchfiles-0.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:61af9efa0733dc4ca462347becb82e8ef4945aba5135b1638bfc20fad64d4f0e"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d9188979a58a096b6f8090e816ccc3f255f137a009dd4bbec628e27696d67c1"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2bdadf6b90c099ca079d468f976fd50062905d61fae183f769637cb0f68ba59a"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:067dea90c43bf837d41e72e546196e674f68c23702d3ef80e4e816937b0a3ffd"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbf8a20266136507abf88b0df2328e6a9a7c7309e8daff124dda3803306a9fdb"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1235c11510ea557fe21be5d0e354bae2c655a8ee6519c94617fe63e05bca4171"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2444dc7cb9d8cc5ab88ebe792a8d75709d96eeef47f4c8fccb6df7c7bc5be71"}, + {file = "watchfiles-0.22.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c5af2347d17ab0bd59366db8752d9e037982e259cacb2ba06f2c41c08af02c39"}, + {file = "watchfiles-0.22.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9624a68b96c878c10437199d9a8b7d7e542feddda8d5ecff58fdc8e67b460848"}, + {file = "watchfiles-0.22.0-cp310-none-win32.whl", hash = "sha256:4b9f2a128a32a2c273d63eb1fdbf49ad64852fc38d15b34eaa3f7ca2f0d2b797"}, + {file = "watchfiles-0.22.0-cp310-none-win_amd64.whl", hash = "sha256:2627a91e8110b8de2406d8b2474427c86f5a62bf7d9ab3654f541f319ef22bcb"}, + {file = "watchfiles-0.22.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8c39987a1397a877217be1ac0fb1d8b9f662c6077b90ff3de2c05f235e6a8f96"}, + {file = "watchfiles-0.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a927b3034d0672f62fb2ef7ea3c9fc76d063c4b15ea852d1db2dc75fe2c09696"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:052d668a167e9fc345c24203b104c313c86654dd6c0feb4b8a6dfc2462239249"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e45fb0d70dda1623a7045bd00c9e036e6f1f6a85e4ef2c8ae602b1dfadf7550"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c49b76a78c156979759d759339fb62eb0549515acfe4fd18bb151cc07366629c"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4a65474fd2b4c63e2c18ac67a0c6c66b82f4e73e2e4d940f837ed3d2fd9d4da"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc0cba54f47c660d9fa3218158b8963c517ed23bd9f45fe463f08262a4adae1"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94ebe84a035993bb7668f58a0ebf998174fb723a39e4ef9fce95baabb42b787f"}, + {file = "watchfiles-0.22.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e0f0a874231e2839abbf473256efffe577d6ee2e3bfa5b540479e892e47c172d"}, + {file = "watchfiles-0.22.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:213792c2cd3150b903e6e7884d40660e0bcec4465e00563a5fc03f30ea9c166c"}, + {file = "watchfiles-0.22.0-cp311-none-win32.whl", hash = "sha256:b44b70850f0073b5fcc0b31ede8b4e736860d70e2dbf55701e05d3227a154a67"}, + {file = "watchfiles-0.22.0-cp311-none-win_amd64.whl", hash = "sha256:00f39592cdd124b4ec5ed0b1edfae091567c72c7da1487ae645426d1b0ffcad1"}, + {file = "watchfiles-0.22.0-cp311-none-win_arm64.whl", hash = "sha256:3218a6f908f6a276941422b035b511b6d0d8328edd89a53ae8c65be139073f84"}, + {file = "watchfiles-0.22.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c7b978c384e29d6c7372209cbf421d82286a807bbcdeb315427687f8371c340a"}, + {file = "watchfiles-0.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd4c06100bce70a20c4b81e599e5886cf504c9532951df65ad1133e508bf20be"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:425440e55cd735386ec7925f64d5dde392e69979d4c8459f6bb4e920210407f2"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:68fe0c4d22332d7ce53ad094622b27e67440dacefbaedd29e0794d26e247280c"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8a31bfd98f846c3c284ba694c6365620b637debdd36e46e1859c897123aa232"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc2e8fe41f3cac0660197d95216c42910c2b7e9c70d48e6d84e22f577d106fc1"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b7cc10261c2786c41d9207193a85c1db1b725cf87936df40972aab466179b6"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28585744c931576e535860eaf3f2c0ec7deb68e3b9c5a85ca566d69d36d8dd27"}, + {file = "watchfiles-0.22.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00095dd368f73f8f1c3a7982a9801190cc88a2f3582dd395b289294f8975172b"}, + {file = "watchfiles-0.22.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:52fc9b0dbf54d43301a19b236b4a4614e610605f95e8c3f0f65c3a456ffd7d35"}, + {file = "watchfiles-0.22.0-cp312-none-win32.whl", hash = "sha256:581f0a051ba7bafd03e17127735d92f4d286af941dacf94bcf823b101366249e"}, + {file = "watchfiles-0.22.0-cp312-none-win_amd64.whl", hash = "sha256:aec83c3ba24c723eac14225194b862af176d52292d271c98820199110e31141e"}, + {file = "watchfiles-0.22.0-cp312-none-win_arm64.whl", hash = "sha256:c668228833c5619f6618699a2c12be057711b0ea6396aeaece4ded94184304ea"}, + {file = "watchfiles-0.22.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d47e9ef1a94cc7a536039e46738e17cce058ac1593b2eccdede8bf72e45f372a"}, + {file = "watchfiles-0.22.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:28f393c1194b6eaadcdd8f941307fc9bbd7eb567995232c830f6aef38e8a6e88"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd64f3a4db121bc161644c9e10a9acdb836853155a108c2446db2f5ae1778c3d"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2abeb79209630da981f8ebca30a2c84b4c3516a214451bfc5f106723c5f45843"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cc382083afba7918e32d5ef12321421ef43d685b9a67cc452a6e6e18920890e"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d048ad5d25b363ba1d19f92dcf29023988524bee6f9d952130b316c5802069cb"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:103622865599f8082f03af4214eaff90e2426edff5e8522c8f9e93dc17caee13"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3e1f3cf81f1f823e7874ae563457828e940d75573c8fbf0ee66818c8b6a9099"}, + {file = "watchfiles-0.22.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8597b6f9dc410bdafc8bb362dac1cbc9b4684a8310e16b1ff5eee8725d13dcd6"}, + {file = "watchfiles-0.22.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0b04a2cbc30e110303baa6d3ddce8ca3664bc3403be0f0ad513d1843a41c97d1"}, + {file = "watchfiles-0.22.0-cp38-none-win32.whl", hash = "sha256:b610fb5e27825b570554d01cec427b6620ce9bd21ff8ab775fc3a32f28bba63e"}, + {file = "watchfiles-0.22.0-cp38-none-win_amd64.whl", hash = "sha256:fe82d13461418ca5e5a808a9e40f79c1879351fcaeddbede094028e74d836e86"}, + {file = "watchfiles-0.22.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3973145235a38f73c61474d56ad6199124e7488822f3a4fc97c72009751ae3b0"}, + {file = "watchfiles-0.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:280a4afbc607cdfc9571b9904b03a478fc9f08bbeec382d648181c695648202f"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a0d883351a34c01bd53cfa75cd0292e3f7e268bacf2f9e33af4ecede7e21d1d"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9165bcab15f2b6d90eedc5c20a7f8a03156b3773e5fb06a790b54ccecdb73385"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc1b9b56f051209be458b87edb6856a449ad3f803315d87b2da4c93b43a6fe72"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dc1fc25a1dedf2dd952909c8e5cb210791e5f2d9bc5e0e8ebc28dd42fed7562"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc92d2d2706d2b862ce0568b24987eba51e17e14b79a1abcd2edc39e48e743c8"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97b94e14b88409c58cdf4a8eaf0e67dfd3ece7e9ce7140ea6ff48b0407a593ec"}, + {file = "watchfiles-0.22.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:96eec15e5ea7c0b6eb5bfffe990fc7c6bd833acf7e26704eb18387fb2f5fd087"}, + {file = "watchfiles-0.22.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:28324d6b28bcb8d7c1041648d7b63be07a16db5510bea923fc80b91a2a6cbed6"}, + {file = "watchfiles-0.22.0-cp39-none-win32.whl", hash = "sha256:8c3e3675e6e39dc59b8fe5c914a19d30029e36e9f99468dddffd432d8a7b1c93"}, + {file = "watchfiles-0.22.0-cp39-none-win_amd64.whl", hash = "sha256:25c817ff2a86bc3de3ed2df1703e3d24ce03479b27bb4527c57e722f8554d971"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b810a2c7878cbdecca12feae2c2ae8af59bea016a78bc353c184fa1e09f76b68"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f7e1f9c5d1160d03b93fc4b68a0aeb82fe25563e12fbcdc8507f8434ab6f823c"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:030bc4e68d14bcad2294ff68c1ed87215fbd9a10d9dea74e7cfe8a17869785ab"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ace7d060432acde5532e26863e897ee684780337afb775107c0a90ae8dbccfd2"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5834e1f8b71476a26df97d121c0c0ed3549d869124ed2433e02491553cb468c2"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:0bc3b2f93a140df6806c8467c7f51ed5e55a931b031b5c2d7ff6132292e803d6"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fdebb655bb1ba0122402352b0a4254812717a017d2dc49372a1d47e24073795"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c8e0aa0e8cc2a43561e0184c0513e291ca891db13a269d8d47cb9841ced7c71"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2f350cbaa4bb812314af5dab0eb8d538481e2e2279472890864547f3fe2281ed"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7a74436c415843af2a769b36bf043b6ccbc0f8d784814ba3d42fc961cdb0a9dc"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00ad0bcd399503a84cc688590cdffbe7a991691314dde5b57b3ed50a41319a31"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72a44e9481afc7a5ee3291b09c419abab93b7e9c306c9ef9108cb76728ca58d2"}, + {file = "watchfiles-0.22.0.tar.gz", hash = "sha256:988e981aaab4f3955209e7e28c7794acdb690be1efa7f16f8ea5aba7ffdadacb"}, ] [package.dependencies] @@ -6570,18 +6736,18 @@ files = [ [[package]] name = "webcolors" -version = "1.13" +version = "24.6.0" description = "A library for working with the color formats defined by HTML and CSS." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "webcolors-1.13-py3-none-any.whl", hash = "sha256:29bc7e8752c0a1bd4a1f03c14d6e6a72e93d82193738fa860cbff59d0fcc11bf"}, - {file = "webcolors-1.13.tar.gz", hash = "sha256:c225b674c83fa923be93d235330ce0300373d02885cef23238813b0d5668304a"}, + {file = "webcolors-24.6.0-py3-none-any.whl", hash = "sha256:8cf5bc7e28defd1d48b9e83d5fc30741328305a8195c29a8e668fa45586568a1"}, + {file = "webcolors-24.6.0.tar.gz", hash = "sha256:1d160d1de46b3e81e58d0a280d0c78b467dc80f47294b91b1ad8029d2cedb55b"}, ] [package.extras] docs = ["furo", "sphinx", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-notfound-page", "sphinxext-opengraph"] -tests = ["pytest", "pytest-cov"] +tests = ["coverage[toml]"] [[package]] name = "webencodings" @@ -6710,13 +6876,13 @@ watchdog = ["watchdog (>=2.3)"] [[package]] name = "widgetsnbextension" -version = "4.0.10" +version = "4.0.11" description = "Jupyter interactive widgets for Jupyter Notebook" optional = false python-versions = ">=3.7" files = [ - {file = "widgetsnbextension-4.0.10-py3-none-any.whl", hash = "sha256:d37c3724ec32d8c48400a435ecfa7d3e259995201fbefa37163124a9fcb393cc"}, - {file = "widgetsnbextension-4.0.10.tar.gz", hash = "sha256:64196c5ff3b9a9183a8e699a4227fb0b7002f252c814098e66c4d1cd0644688f"}, + {file = "widgetsnbextension-4.0.11-py3-none-any.whl", hash = "sha256:55d4d6949d100e0d08b94948a42efc3ed6dfdc0e9468b2c4b128c9a2ce3a7a36"}, + {file = "widgetsnbextension-4.0.11.tar.gz", hash = "sha256:8b22a8f1910bfd188e596fe7fc05dcbd87e810c8a4ba010bdb3da86637398474"}, ] [[package]] @@ -6976,13 +7142,13 @@ multidict = ">=4.0" [[package]] name = "ydb" -version = "3.11.3" +version = "3.12.2" description = "YDB Python SDK" optional = true python-versions = "*" files = [ - {file = "ydb-3.11.3-py2.py3-none-any.whl", hash = "sha256:7f9b944ad6274b97a9cbd86b41983750cd8f66f8fb31183c66467a1fba13a7c9"}, - {file = "ydb-3.11.3.tar.gz", hash = "sha256:a4f752d2734b1eec2fb8b6172117165280b25c838573a6a6e0964baa7b4c2235"}, + {file = "ydb-3.12.2-py2.py3-none-any.whl", hash = "sha256:41256a3221e5659cb4bcbbc89a92057f0c08d05a777a15fbd3837ed0251be3fb"}, + {file = "ydb-3.12.2.tar.gz", hash = "sha256:2e4b51c1a0293adaf8b21af91348fe3bdcd3504b4ba54c89f1d95f2e6c15321f"}, ] [package.dependencies] @@ -6996,18 +7162,18 @@ yc = ["yandexcloud"] [[package]] name = "zipp" -version = "3.18.1" +version = "3.19.2" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ - {file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"}, - {file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"}, + {file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"}, + {file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] [[package]] name = "zope-event" @@ -7029,47 +7195,47 @@ test = ["zope.testrunner"] [[package]] name = "zope-interface" -version = "6.3" +version = "6.4.post2" description = "Interfaces for Python" optional = false python-versions = ">=3.7" files = [ - {file = "zope.interface-6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f32010ffb87759c6a3ad1c65ed4d2e38e51f6b430a1ca11cee901ec2b42e021"}, - {file = "zope.interface-6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e78a183a3c2f555c2ad6aaa1ab572d1c435ba42f1dc3a7e8c82982306a19b785"}, - {file = "zope.interface-6.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa0491a9f154cf8519a02026dc85a416192f4cb1efbbf32db4a173ba28b289a"}, - {file = "zope.interface-6.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62e32f02b3f26204d9c02c3539c802afc3eefb19d601a0987836ed126efb1f21"}, - {file = "zope.interface-6.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c40df4aea777be321b7e68facb901bc67317e94b65d9ab20fb96e0eb3c0b60a1"}, - {file = "zope.interface-6.3-cp310-cp310-win_amd64.whl", hash = "sha256:46034be614d1f75f06e7dcfefba21d609b16b38c21fc912b01a99cb29e58febb"}, - {file = "zope.interface-6.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:600101f43a7582d5b9504a7c629a1185a849ce65e60fca0f6968dfc4b76b6d39"}, - {file = "zope.interface-6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4d6b229f5e1a6375f206455cc0a63a8e502ed190fe7eb15e94a312dc69d40299"}, - {file = "zope.interface-6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10cde8dc6b2fd6a1d0b5ca4be820063e46ddba417ab82bcf55afe2227337b130"}, - {file = "zope.interface-6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40aa8c8e964d47d713b226c5baf5f13cdf3a3169c7a2653163b17ff2e2334d10"}, - {file = "zope.interface-6.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d165d7774d558ea971cb867739fb334faf68fc4756a784e689e11efa3becd59e"}, - {file = "zope.interface-6.3-cp311-cp311-win_amd64.whl", hash = "sha256:69dedb790530c7ca5345899a1b4cb837cc53ba669051ea51e8c18f82f9389061"}, - {file = "zope.interface-6.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8d407e0fd8015f6d5dfad481309638e1968d70e6644e0753f229154667dd6cd5"}, - {file = "zope.interface-6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:72d5efecad16c619a97744a4f0b67ce1bcc88115aa82fcf1dc5be9bb403bcc0b"}, - {file = "zope.interface-6.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:567d54c06306f9c5b6826190628d66753b9f2b0422f4c02d7c6d2b97ebf0a24e"}, - {file = "zope.interface-6.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483e118b1e075f1819b3c6ace082b9d7d3a6a5eb14b2b375f1b80a0868117920"}, - {file = "zope.interface-6.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb78c12c1ad3a20c0d981a043d133299117b6854f2e14893b156979ed4e1d2c"}, - {file = "zope.interface-6.3-cp312-cp312-win_amd64.whl", hash = "sha256:ad4524289d8dbd6fb5aa17aedb18f5643e7d48358f42c007a5ee51a2afc2a7c5"}, - {file = "zope.interface-6.3-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:a56fe1261230093bfeedc1c1a6cd6f3ec568f9b07f031c9a09f46b201f793a85"}, - {file = "zope.interface-6.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:014bb94fe6bf1786da1aa044eadf65bc6437bcb81c451592987e5be91e70a91e"}, - {file = "zope.interface-6.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22e8a218e8e2d87d4d9342aa973b7915297a08efbebea5b25900c73e78ed468e"}, - {file = "zope.interface-6.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f95bebd0afe86b2adc074df29edb6848fc4d474ff24075e2c263d698774e108d"}, - {file = "zope.interface-6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:d0e7321557c702bd92dac3c66a2f22b963155fdb4600133b6b29597f62b71b12"}, - {file = "zope.interface-6.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:187f7900b63845dcdef1be320a523dbbdba94d89cae570edc2781eb55f8c2f86"}, - {file = "zope.interface-6.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a058e6cf8d68a5a19cb5449f42a404f0d6c2778b897e6ce8fadda9cea308b1b0"}, - {file = "zope.interface-6.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8fa0fb05083a1a4216b4b881fdefa71c5d9a106e9b094cd4399af6b52873e91"}, - {file = "zope.interface-6.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:26c9a37fb395a703e39b11b00b9e921c48f82b6e32cc5851ad5d0618cd8876b5"}, - {file = "zope.interface-6.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b0c4c90e5eefca2c3e045d9f9ed9f1e2cdbe70eb906bff6b247e17119ad89a1"}, - {file = "zope.interface-6.3-cp38-cp38-win_amd64.whl", hash = "sha256:5683aa8f2639016fd2b421df44301f10820e28a9b96382a6e438e5c6427253af"}, - {file = "zope.interface-6.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2c3cfb272bcb83650e6695d49ae0d14dd06dc694789a3d929f23758557a23d92"}, - {file = "zope.interface-6.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:01a0b3dd012f584afcf03ed814bce0fc40ed10e47396578621509ac031be98bf"}, - {file = "zope.interface-6.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4137025731e824eee8d263b20682b28a0bdc0508de9c11d6c6be54163e5b7c83"}, - {file = "zope.interface-6.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c8731596198198746f7ce2a4487a0edcbc9ea5e5918f0ab23c4859bce56055c"}, - {file = "zope.interface-6.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf34840e102d1d0b2d39b1465918d90b312b1119552cebb61a242c42079817b9"}, - {file = "zope.interface-6.3-cp39-cp39-win_amd64.whl", hash = "sha256:a1adc14a2a9d5e95f76df625a9b39f4709267a483962a572e3f3001ef90ea6e6"}, - {file = "zope.interface-6.3.tar.gz", hash = "sha256:f83d6b4b22262d9a826c3bd4b2fbfafe1d0000f085ef8e44cd1328eea274ae6a"}, + {file = "zope.interface-6.4.post2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2eccd5bef45883802848f821d940367c1d0ad588de71e5cabe3813175444202c"}, + {file = "zope.interface-6.4.post2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:762e616199f6319bb98e7f4f27d254c84c5fb1c25c908c2a9d0f92b92fb27530"}, + {file = "zope.interface-6.4.post2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ef8356f16b1a83609f7a992a6e33d792bb5eff2370712c9eaae0d02e1924341"}, + {file = "zope.interface-6.4.post2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e4fa5d34d7973e6b0efa46fe4405090f3b406f64b6290facbb19dcbf642ad6b"}, + {file = "zope.interface-6.4.post2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d22fce0b0f5715cdac082e35a9e735a1752dc8585f005d045abb1a7c20e197f9"}, + {file = "zope.interface-6.4.post2-cp310-cp310-win_amd64.whl", hash = "sha256:97e615eab34bd8477c3f34197a17ce08c648d38467489359cb9eb7394f1083f7"}, + {file = "zope.interface-6.4.post2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:599f3b07bde2627e163ce484d5497a54a0a8437779362395c6b25e68c6590ede"}, + {file = "zope.interface-6.4.post2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:136cacdde1a2c5e5bc3d0b2a1beed733f97e2dad8c2ad3c2e17116f6590a3827"}, + {file = "zope.interface-6.4.post2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47937cf2e7ed4e0e37f7851c76edeb8543ec9b0eae149b36ecd26176ff1ca874"}, + {file = "zope.interface-6.4.post2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f0a6be264afb094975b5ef55c911379d6989caa87c4e558814ec4f5125cfa2e"}, + {file = "zope.interface-6.4.post2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47654177e675bafdf4e4738ce58cdc5c6d6ee2157ac0a78a3fa460942b9d64a8"}, + {file = "zope.interface-6.4.post2-cp311-cp311-win_amd64.whl", hash = "sha256:e2fb8e8158306567a3a9a41670c1ff99d0567d7fc96fa93b7abf8b519a46b250"}, + {file = "zope.interface-6.4.post2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b912750b13d76af8aac45ddf4679535def304b2a48a07989ec736508d0bbfbde"}, + {file = "zope.interface-6.4.post2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4ac46298e0143d91e4644a27a769d1388d5d89e82ee0cf37bf2b0b001b9712a4"}, + {file = "zope.interface-6.4.post2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86a94af4a88110ed4bb8961f5ac72edf782958e665d5bfceaab6bf388420a78b"}, + {file = "zope.interface-6.4.post2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73f9752cf3596771c7726f7eea5b9e634ad47c6d863043589a1c3bb31325c7eb"}, + {file = "zope.interface-6.4.post2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00b5c3e9744dcdc9e84c24ed6646d5cf0cf66551347b310b3ffd70f056535854"}, + {file = "zope.interface-6.4.post2-cp312-cp312-win_amd64.whl", hash = "sha256:551db2fe892fcbefb38f6f81ffa62de11090c8119fd4e66a60f3adff70751ec7"}, + {file = "zope.interface-6.4.post2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96ac6b3169940a8cd57b4f2b8edcad8f5213b60efcd197d59fbe52f0accd66e"}, + {file = "zope.interface-6.4.post2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cebff2fe5dc82cb22122e4e1225e00a4a506b1a16fafa911142ee124febf2c9e"}, + {file = "zope.interface-6.4.post2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33ee982237cffaf946db365c3a6ebaa37855d8e3ca5800f6f48890209c1cfefc"}, + {file = "zope.interface-6.4.post2-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:fbf649bc77510ef2521cf797700b96167bb77838c40780da7ea3edd8b78044d1"}, + {file = "zope.interface-6.4.post2-cp37-cp37m-win_amd64.whl", hash = "sha256:4c0b208a5d6c81434bdfa0f06d9b667e5de15af84d8cae5723c3a33ba6611b82"}, + {file = "zope.interface-6.4.post2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d3fe667935e9562407c2511570dca14604a654988a13d8725667e95161d92e9b"}, + {file = "zope.interface-6.4.post2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a96e6d4074db29b152222c34d7eec2e2db2f92638d2b2b2c704f9e8db3ae0edc"}, + {file = "zope.interface-6.4.post2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:866a0f583be79f0def667a5d2c60b7b4cc68f0c0a470f227e1122691b443c934"}, + {file = "zope.interface-6.4.post2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fe919027f29b12f7a2562ba0daf3e045cb388f844e022552a5674fcdf5d21f1"}, + {file = "zope.interface-6.4.post2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e0343a6e06d94f6b6ac52fbc75269b41dd3c57066541a6c76517f69fe67cb43"}, + {file = "zope.interface-6.4.post2-cp38-cp38-win_amd64.whl", hash = "sha256:dabb70a6e3d9c22df50e08dc55b14ca2a99da95a2d941954255ac76fd6982bc5"}, + {file = "zope.interface-6.4.post2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:706efc19f9679a1b425d6fa2b4bc770d976d0984335eaea0869bd32f627591d2"}, + {file = "zope.interface-6.4.post2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3d136e5b8821073e1a09dde3eb076ea9988e7010c54ffe4d39701adf0c303438"}, + {file = "zope.interface-6.4.post2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1730c93a38b5a18d24549bc81613223962a19d457cfda9bdc66e542f475a36f4"}, + {file = "zope.interface-6.4.post2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bc2676312cc3468a25aac001ec727168994ea3b69b48914944a44c6a0b251e79"}, + {file = "zope.interface-6.4.post2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a62fd6cd518693568e23e02f41816adedfca637f26716837681c90b36af3671"}, + {file = "zope.interface-6.4.post2-cp39-cp39-win_amd64.whl", hash = "sha256:d3f7e001328bd6466b3414215f66dde3c7c13d8025a9c160a75d7b2687090d15"}, + {file = "zope.interface-6.4.post2.tar.gz", hash = "sha256:1c207e6f6dfd5749a26f5a5fd966602d6b824ec00d2df84a7e9a924e8933654e"}, ] [package.dependencies] @@ -7096,4 +7262,4 @@ ydb = ["six", "ydb"] [metadata] lock-version = "2.0" python-versions = "^3.8.1,!=3.9.7" -content-hash = "2bab0798ec48c472886dc47a49d9bab5a0af3dfac810cea1eacf322b0a322eb2" +content-hash = "bc75732b813f1a509fca36ad4ed2eff6d5320e69e9d750f39be3926e6d0961ec" From 21d5af2f1308a406759714b213edf3a948a2a991 Mon Sep 17 00:00:00 2001 From: pseusys Date: Thu, 20 Jun 2024 01:40:23 +0200 Subject: [PATCH 103/140] locked --- poetry.lock | 173 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 156 insertions(+), 17 deletions(-) diff --git a/poetry.lock b/poetry.lock index 1c74b50f2..0c223de15 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2191,7 +2191,55 @@ h11 = ">=0.13,<0.15" asyncio = ["anyio (>=4.0,<5.0)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] -trio = ["trio (>=0.22.0,<0.25.0)"] +trio = ["trio (>=0.22.0,<0.26.0)"] + +[[package]] +name = "httptools" +version = "0.6.1" +description = "A collection of framework independent HTTP protocol utils." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2f6c3c4cb1948d912538217838f6e9960bc4a521d7f9b323b3da579cd14532f"}, + {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:00d5d4b68a717765b1fabfd9ca755bd12bf44105eeb806c03d1962acd9b8e563"}, + {file = "httptools-0.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:639dc4f381a870c9ec860ce5c45921db50205a37cc3334e756269736ff0aac58"}, + {file = "httptools-0.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e57997ac7fb7ee43140cc03664de5f268813a481dff6245e0075925adc6aa185"}, + {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0ac5a0ae3d9f4fe004318d64b8a854edd85ab76cffbf7ef5e32920faef62f142"}, + {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3f30d3ce413088a98b9db71c60a6ada2001a08945cb42dd65a9a9fe228627658"}, + {file = "httptools-0.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:1ed99a373e327f0107cb513b61820102ee4f3675656a37a50083eda05dc9541b"}, + {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7a7ea483c1a4485c71cb5f38be9db078f8b0e8b4c4dc0210f531cdd2ddac1ef1"}, + {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85ed077c995e942b6f1b07583e4eb0a8d324d418954fc6af913d36db7c05a5a0"}, + {file = "httptools-0.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b0bb634338334385351a1600a73e558ce619af390c2b38386206ac6a27fecfc"}, + {file = "httptools-0.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d9ceb2c957320def533671fc9c715a80c47025139c8d1f3797477decbc6edd2"}, + {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4f0f8271c0a4db459f9dc807acd0eadd4839934a4b9b892f6f160e94da309837"}, + {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6a4f5ccead6d18ec072ac0b84420e95d27c1cdf5c9f1bc8fbd8daf86bd94f43d"}, + {file = "httptools-0.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:5cceac09f164bcba55c0500a18fe3c47df29b62353198e4f37bbcc5d591172c3"}, + {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:75c8022dca7935cba14741a42744eee13ba05db00b27a4b940f0d646bd4d56d0"}, + {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:48ed8129cd9a0d62cf4d1575fcf90fb37e3ff7d5654d3a5814eb3d55f36478c2"}, + {file = "httptools-0.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f58e335a1402fb5a650e271e8c2d03cfa7cea46ae124649346d17bd30d59c90"}, + {file = "httptools-0.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ad80d7176aa5788902f207a4e79885f0576134695dfb0fefc15b7a4648d503"}, + {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9bb68d3a085c2174c2477eb3ffe84ae9fb4fde8792edb7bcd09a1d8467e30a84"}, + {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b512aa728bc02354e5ac086ce76c3ce635b62f5fbc32ab7082b5e582d27867bb"}, + {file = "httptools-0.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:97662ce7fb196c785344d00d638fc9ad69e18ee4bfb4000b35a52efe5adcc949"}, + {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8e216a038d2d52ea13fdd9b9c9c7459fb80d78302b257828285eca1c773b99b3"}, + {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3e802e0b2378ade99cd666b5bffb8b2a7cc8f3d28988685dc300469ea8dd86cb"}, + {file = "httptools-0.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bd3e488b447046e386a30f07af05f9b38d3d368d1f7b4d8f7e10af85393db97"}, + {file = "httptools-0.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe467eb086d80217b7584e61313ebadc8d187a4d95bb62031b7bab4b205c3ba3"}, + {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3c3b214ce057c54675b00108ac42bacf2ab8f85c58e3f324a4e963bbc46424f4"}, + {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8ae5b97f690badd2ca27cbf668494ee1b6d34cf1c464271ef7bfa9ca6b83ffaf"}, + {file = "httptools-0.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:405784577ba6540fa7d6ff49e37daf104e04f4b4ff2d1ac0469eaa6a20fde084"}, + {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:95fb92dd3649f9cb139e9c56604cc2d7c7bf0fc2e7c8d7fbd58f96e35eddd2a3"}, + {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dcbab042cc3ef272adc11220517278519adf8f53fd3056d0e68f0a6f891ba94e"}, + {file = "httptools-0.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cf2372e98406efb42e93bfe10f2948e467edfd792b015f1b4ecd897903d3e8d"}, + {file = "httptools-0.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:678fcbae74477a17d103b7cae78b74800d795d702083867ce160fc202104d0da"}, + {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e0b281cf5a125c35f7f6722b65d8542d2e57331be573e9e88bc8b0115c4a7a81"}, + {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:95658c342529bba4e1d3d2b1a874db16c7cca435e8827422154c9da76ac4e13a"}, + {file = "httptools-0.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ebaec1bf683e4bf5e9fbb49b8cc36da482033596a415b3e4ebab5a4c0d7ec5e"}, + {file = "httptools-0.6.1.tar.gz", hash = "sha256:c6e26c30455600b95d94b1b836085138e82f177351454ee841c148f93a9bad5a"}, +] + +[package.extras] +test = ["Cython (>=0.29.24,<0.30.0)"] [[package]] name = "httpx" @@ -4677,22 +4725,8 @@ description = "Wrappers to call pyproject.toml-based build backend hooks." optional = false python-versions = ">=3.7" files = [ - {file = "pyproject_hooks-1.0.0-py3-none-any.whl", hash = "sha256:283c11acd6b928d2f6a7c73fa0d01cb2bdc5f07c57a2eeb6e83d5e56b97976f8"}, - {file = "pyproject_hooks-1.0.0.tar.gz", hash = "sha256:f271b298b97f5955d53fb12b72c1fb1948c22c1a6b70b315c54cedaca0264ef5"}, -] - -[package.dependencies] -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} - -[[package]] -name = "pytelegrambotapi" -version = "4.17.0" -description = "Python Telegram bot api." -optional = true -python-versions = ">=3.8" -files = [ - {file = "pytelegrambotapi-4.17.0-py3-none-any.whl", hash = "sha256:326b51fdaa4f5dcb1e12d326beb9d9aa1289ffb578110b17d5ea269971a39160"}, - {file = "pytelegrambotapi-4.17.0.tar.gz", hash = "sha256:8cfda15e64a12fe73b78545ccc7b59e035d7fb3e54da5722ddf756296f3b8159"}, + {file = "pyproject_hooks-1.1.0-py3-none-any.whl", hash = "sha256:7ceeefe9aec63a1064c18d939bdc3adf2d8aa1988a510afec15151578b232aa2"}, + {file = "pyproject_hooks-1.1.0.tar.gz", hash = "sha256:4b37730834edbd6bd37f26ece6b44802fb1c1ee2ece0e54ddff8bfc06db86965"}, ] [[package]] @@ -6333,6 +6367,111 @@ files = [ {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, ] +[[package]] +name = "tzlocal" +version = "5.2" +description = "tzinfo object for the local timezone" +optional = true +python-versions = ">=3.8" +files = [ + {file = "tzlocal-5.2-py3-none-any.whl", hash = "sha256:49816ef2fe65ea8ac19d19aa7a1ae0551c834303d5014c6d5a62e4cbda8047b8"}, + {file = "tzlocal-5.2.tar.gz", hash = "sha256:8d399205578f1a9342816409cc1e46a93ebd5755e39ea2d85334bea911bf0e6e"}, +] + +[package.dependencies] +"backports.zoneinfo" = {version = "*", markers = "python_version < \"3.9\""} +tzdata = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +devenv = ["check-manifest", "pytest (>=4.3)", "pytest-cov", "pytest-mock (>=3.3)", "zest.releaser"] + +[[package]] +name = "ujson" +version = "5.10.0" +description = "Ultra fast JSON encoder and decoder for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ujson-5.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2601aa9ecdbee1118a1c2065323bda35e2c5a2cf0797ef4522d485f9d3ef65bd"}, + {file = "ujson-5.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:348898dd702fc1c4f1051bc3aacbf894caa0927fe2c53e68679c073375f732cf"}, + {file = "ujson-5.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22cffecf73391e8abd65ef5f4e4dd523162a3399d5e84faa6aebbf9583df86d6"}, + {file = "ujson-5.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26b0e2d2366543c1bb4fbd457446f00b0187a2bddf93148ac2da07a53fe51569"}, + {file = "ujson-5.10.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:caf270c6dba1be7a41125cd1e4fc7ba384bf564650beef0df2dd21a00b7f5770"}, + {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a245d59f2ffe750446292b0094244df163c3dc96b3ce152a2c837a44e7cda9d1"}, + {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:94a87f6e151c5f483d7d54ceef83b45d3a9cca7a9cb453dbdbb3f5a6f64033f5"}, + {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:29b443c4c0a113bcbb792c88bea67b675c7ca3ca80c3474784e08bba01c18d51"}, + {file = "ujson-5.10.0-cp310-cp310-win32.whl", hash = "sha256:c18610b9ccd2874950faf474692deee4223a994251bc0a083c114671b64e6518"}, + {file = "ujson-5.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:924f7318c31874d6bb44d9ee1900167ca32aa9b69389b98ecbde34c1698a250f"}, + {file = "ujson-5.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a5b366812c90e69d0f379a53648be10a5db38f9d4ad212b60af00bd4048d0f00"}, + {file = "ujson-5.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:502bf475781e8167f0f9d0e41cd32879d120a524b22358e7f205294224c71126"}, + {file = "ujson-5.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b91b5d0d9d283e085e821651184a647699430705b15bf274c7896f23fe9c9d8"}, + {file = "ujson-5.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:129e39af3a6d85b9c26d5577169c21d53821d8cf68e079060602e861c6e5da1b"}, + {file = "ujson-5.10.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f77b74475c462cb8b88680471193064d3e715c7c6074b1c8c412cb526466efe9"}, + {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7ec0ca8c415e81aa4123501fee7f761abf4b7f386aad348501a26940beb1860f"}, + {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab13a2a9e0b2865a6c6db9271f4b46af1c7476bfd51af1f64585e919b7c07fd4"}, + {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:57aaf98b92d72fc70886b5a0e1a1ca52c2320377360341715dd3933a18e827b1"}, + {file = "ujson-5.10.0-cp311-cp311-win32.whl", hash = "sha256:2987713a490ceb27edff77fb184ed09acdc565db700ee852823c3dc3cffe455f"}, + {file = "ujson-5.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:f00ea7e00447918ee0eff2422c4add4c5752b1b60e88fcb3c067d4a21049a720"}, + {file = "ujson-5.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98ba15d8cbc481ce55695beee9f063189dce91a4b08bc1d03e7f0152cd4bbdd5"}, + {file = "ujson-5.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9d2edbf1556e4f56e50fab7d8ff993dbad7f54bac68eacdd27a8f55f433578e"}, + {file = "ujson-5.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6627029ae4f52d0e1a2451768c2c37c0c814ffc04f796eb36244cf16b8e57043"}, + {file = "ujson-5.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8ccb77b3e40b151e20519c6ae6d89bfe3f4c14e8e210d910287f778368bb3d1"}, + {file = "ujson-5.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3caf9cd64abfeb11a3b661329085c5e167abbe15256b3b68cb5d914ba7396f3"}, + {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6e32abdce572e3a8c3d02c886c704a38a1b015a1fb858004e03d20ca7cecbb21"}, + {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a65b6af4d903103ee7b6f4f5b85f1bfd0c90ba4eeac6421aae436c9988aa64a2"}, + {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:604a046d966457b6cdcacc5aa2ec5314f0e8c42bae52842c1e6fa02ea4bda42e"}, + {file = "ujson-5.10.0-cp312-cp312-win32.whl", hash = "sha256:6dea1c8b4fc921bf78a8ff00bbd2bfe166345f5536c510671bccececb187c80e"}, + {file = "ujson-5.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:38665e7d8290188b1e0d57d584eb8110951a9591363316dd41cf8686ab1d0abc"}, + {file = "ujson-5.10.0-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:618efd84dc1acbd6bff8eaa736bb6c074bfa8b8a98f55b61c38d4ca2c1f7f287"}, + {file = "ujson-5.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38d5d36b4aedfe81dfe251f76c0467399d575d1395a1755de391e58985ab1c2e"}, + {file = "ujson-5.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67079b1f9fb29ed9a2914acf4ef6c02844b3153913eb735d4bf287ee1db6e557"}, + {file = "ujson-5.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7d0e0ceeb8fe2468c70ec0c37b439dd554e2aa539a8a56365fd761edb418988"}, + {file = "ujson-5.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:59e02cd37bc7c44d587a0ba45347cc815fb7a5fe48de16bf05caa5f7d0d2e816"}, + {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2a890b706b64e0065f02577bf6d8ca3b66c11a5e81fb75d757233a38c07a1f20"}, + {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:621e34b4632c740ecb491efc7f1fcb4f74b48ddb55e65221995e74e2d00bbff0"}, + {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b9500e61fce0cfc86168b248104e954fead61f9be213087153d272e817ec7b4f"}, + {file = "ujson-5.10.0-cp313-cp313-win32.whl", hash = "sha256:4c4fc16f11ac1612f05b6f5781b384716719547e142cfd67b65d035bd85af165"}, + {file = "ujson-5.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:4573fd1695932d4f619928fd09d5d03d917274381649ade4328091ceca175539"}, + {file = "ujson-5.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a984a3131da7f07563057db1c3020b1350a3e27a8ec46ccbfbf21e5928a43050"}, + {file = "ujson-5.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:73814cd1b9db6fc3270e9d8fe3b19f9f89e78ee9d71e8bd6c9a626aeaeaf16bd"}, + {file = "ujson-5.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61e1591ed9376e5eddda202ec229eddc56c612b61ac6ad07f96b91460bb6c2fb"}, + {file = "ujson-5.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2c75269f8205b2690db4572a4a36fe47cd1338e4368bc73a7a0e48789e2e35a"}, + {file = "ujson-5.10.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7223f41e5bf1f919cd8d073e35b229295aa8e0f7b5de07ed1c8fddac63a6bc5d"}, + {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc2fd6b3067c0782e7002ac3b38cf48608ee6366ff176bbd02cf969c9c20fe"}, + {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:232cc85f8ee3c454c115455195a205074a56ff42608fd6b942aa4c378ac14dd7"}, + {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:cc6139531f13148055d691e442e4bc6601f6dba1e6d521b1585d4788ab0bfad4"}, + {file = "ujson-5.10.0-cp38-cp38-win32.whl", hash = "sha256:e7ce306a42b6b93ca47ac4a3b96683ca554f6d35dd8adc5acfcd55096c8dfcb8"}, + {file = "ujson-5.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:e82d4bb2138ab05e18f089a83b6564fee28048771eb63cdecf4b9b549de8a2cc"}, + {file = "ujson-5.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dfef2814c6b3291c3c5f10065f745a1307d86019dbd7ea50e83504950136ed5b"}, + {file = "ujson-5.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4734ee0745d5928d0ba3a213647f1c4a74a2a28edc6d27b2d6d5bd9fa4319e27"}, + {file = "ujson-5.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d47ebb01bd865fdea43da56254a3930a413f0c5590372a1241514abae8aa7c76"}, + {file = "ujson-5.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dee5e97c2496874acbf1d3e37b521dd1f307349ed955e62d1d2f05382bc36dd5"}, + {file = "ujson-5.10.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7490655a2272a2d0b072ef16b0b58ee462f4973a8f6bbe64917ce5e0a256f9c0"}, + {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ba17799fcddaddf5c1f75a4ba3fd6441f6a4f1e9173f8a786b42450851bd74f1"}, + {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2aff2985cef314f21d0fecc56027505804bc78802c0121343874741650a4d3d1"}, + {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ad88ac75c432674d05b61184178635d44901eb749786c8eb08c102330e6e8996"}, + {file = "ujson-5.10.0-cp39-cp39-win32.whl", hash = "sha256:2544912a71da4ff8c4f7ab5606f947d7299971bdd25a45e008e467ca638d13c9"}, + {file = "ujson-5.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:3ff201d62b1b177a46f113bb43ad300b424b7847f9c5d38b1b4ad8f75d4a282a"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5b6fee72fa77dc172a28f21693f64d93166534c263adb3f96c413ccc85ef6e64"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:61d0af13a9af01d9f26d2331ce49bb5ac1fb9c814964018ac8df605b5422dcb3"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecb24f0bdd899d368b715c9e6664166cf694d1e57be73f17759573a6986dd95a"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fbd8fd427f57a03cff3ad6574b5e299131585d9727c8c366da4624a9069ed746"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beeaf1c48e32f07d8820c705ff8e645f8afa690cca1544adba4ebfa067efdc88"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:baed37ea46d756aca2955e99525cc02d9181de67f25515c468856c38d52b5f3b"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7663960f08cd5a2bb152f5ee3992e1af7690a64c0e26d31ba7b3ff5b2ee66337"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:d8640fb4072d36b08e95a3a380ba65779d356b2fee8696afeb7794cf0902d0a1"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78778a3aa7aafb11e7ddca4e29f46bc5139131037ad628cc10936764282d6753"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0111b27f2d5c820e7f2dbad7d48e3338c824e7ac4d2a12da3dc6061cc39c8e6"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:c66962ca7565605b355a9ed478292da628b8f18c0f2793021ca4425abf8b01e5"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ba43cc34cce49cf2d4bc76401a754a81202d8aa926d0e2b79f0ee258cb15d3a4"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:ac56eb983edce27e7f51d05bc8dd820586c6e6be1c5216a6809b0c668bb312b8"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44bd4b23a0e723bf8b10628288c2c7c335161d6840013d4d5de20e48551773b"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c10f4654e5326ec14a46bcdeb2b685d4ada6911050aa8baaf3501e57024b804"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0de4971a89a762398006e844ae394bd46991f7c385d7a6a3b93ba229e6dac17e"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e1402f0564a97d2a52310ae10a64d25bcef94f8dd643fcf5d310219d915484f7"}, + {file = "ujson-5.10.0.tar.gz", hash = "sha256:b3cd8f3c5d8c7738257f1018880444f7b7d9b66232c64649f562d7ba86ad4bc1"}, +] + [[package]] name = "uri-template" version = "1.3.0" From eef4498e856c96eea7bb31564b7255a906f82c2d Mon Sep 17 00:00:00 2001 From: pseusys Date: Thu, 20 Jun 2024 02:16:34 +0200 Subject: [PATCH 104/140] dummy type annotation added --- dff/messengers/telegram/abstract.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index d6f5ff6d1..1d5d86b45 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -6,7 +6,7 @@ """ from pathlib import Path -from typing import Callable, Optional +from typing import Any, Callable, Optional from dff.utils.messengers.verify_params import generate_extra_fields @@ -47,6 +47,10 @@ telegram_available = True except ImportError: + ExtBot = Any + Update = Any + TelegramMessage = Any + telegram_available = False From 25ed6b27e6fcccd2a93c69c18115adfb2a392b22 Mon Sep 17 00:00:00 2001 From: pseusys Date: Thu, 20 Jun 2024 02:23:17 +0200 Subject: [PATCH 105/140] field doclink fixed --- tutorials/messengers/telegram/2_attachments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorials/messengers/telegram/2_attachments.py b/tutorials/messengers/telegram/2_attachments.py index 2c802166f..5ba3125a3 100644 --- a/tutorials/messengers/telegram/2_attachments.py +++ b/tutorials/messengers/telegram/2_attachments.py @@ -45,7 +45,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) Example attachment data is specified below in form of dictionaries. List of attachments that telegram messenger interface can send can be found here: -%mddoclink(api,messengers.telegram.abstract,_AbstractTelegramInterface#supported_request_attachment_types). +%mddoclink(api,messengers.telegram.abstract,_AbstractTelegramInterface.supported_request_attachment_types). """ # %% From 3aa7046f8561c6efcec999f153de81001cb32f45 Mon Sep 17 00:00:00 2001 From: pseusys Date: Thu, 20 Jun 2024 03:55:18 +0200 Subject: [PATCH 106/140] lint fixed partly --- dff/messengers/telegram/abstract.py | 224 +++++++++++++++--- dff/script/conditions/std_conditions.py | 3 +- dff/script/core/message.py | 40 +++- dff/utils/pydantic/__init__.py | 9 +- dff/utils/pydantic/serializing.py | 5 +- tests/messengers/telegram/utils.py | 6 +- .../messengers/telegram/2_attachments.py | 2 +- tutorials/messengers/telegram/3_advanced.py | 25 +- 8 files changed, 269 insertions(+), 45 deletions(-) diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index 1d5d86b45..cba49e65b 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -29,7 +29,7 @@ Sticker, Video, VideoMessage, - VoiceMessage + VoiceMessage, ) try: @@ -42,7 +42,7 @@ Update, Message as TelegramMessage, ) - from telegram.ext import Application, ExtBot, MessageHandler, CallbackQueryHandler, ContextTypes + from telegram.ext import Application, ExtBot, MessageHandler, CallbackQueryHandler from telegram.ext.filters import ALL telegram_available = True @@ -62,7 +62,11 @@ def _is_attachment_mediagroup_combinable(attachment: Attachment): :return: If the attachment can belong to a mediagroup. """ - return isinstance(attachment, DataAttachment) and not isinstance(attachment, VoiceMessage) and not isinstance(attachment, VideoMessage) + return ( + isinstance(attachment, DataAttachment) + and not isinstance(attachment, VoiceMessage) + and not isinstance(attachment, VideoMessage) + ) class _AbstractTelegramInterface(MessengerInterface): @@ -70,8 +74,33 @@ class _AbstractTelegramInterface(MessengerInterface): Messenger interface mixin for Telegram API usage. """ - supported_request_attachment_types = {Location, Contact, Poll, Sticker, Audio, Video, Animation, Image, Document, VoiceMessage, VideoMessage, Invoice} - supported_response_attachment_types = {Location, Contact, Poll, Sticker, Audio, Video, Animation, Image, Document, VoiceMessage, VideoMessage} + supported_request_attachment_types = { + Location, + Contact, + Poll, + Sticker, + Audio, + Video, + Animation, + Image, + Document, + VoiceMessage, + VideoMessage, + Invoice, + } + supported_response_attachment_types = { + Location, + Contact, + Poll, + Sticker, + Audio, + Video, + Animation, + Image, + Document, + VoiceMessage, + VideoMessage, + } def __init__(self, token: str, attachments_directory: Optional[Path] = None) -> None: super().__init__(attachments_directory) @@ -265,14 +294,25 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes message_text_covered = False if message.attachments is not None: files = list() - media_group_attachments_num = len([att for att in message.attachments if _is_attachment_mediagroup_combinable(att)]) + media_group_attachments_num = len( + [att for att in message.attachments if _is_attachment_mediagroup_combinable(att)] + ) for attachment in message.attachments: if isinstance(attachment, Location): await bot.send_location( chat_id, attachment.latitude, attachment.longitude, - **generate_extra_fields(attachment, ["horizontal_accuracy", "disable_notification", "protect_content", "reply_markup", "message_effect_id"]), + **generate_extra_fields( + attachment, + [ + "horizontal_accuracy", + "disable_notification", + "protect_content", + "reply_markup", + "message_effect_id", + ], + ), ) if isinstance(attachment, Contact): await bot.send_contact( @@ -280,21 +320,43 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes attachment.phone_number, attachment.first_name, attachment.last_name, - **generate_extra_fields(attachment, ["vcard", "disable_notification", "protect_content", "reply_markup", "message_effect_id"]), + **generate_extra_fields( + attachment, + ["vcard", "disable_notification", "protect_content", "reply_markup", "message_effect_id"], + ), ) if isinstance(attachment, Poll): await bot.send_poll( chat_id, attachment.question, [option.text for option in attachment.options], - **generate_extra_fields(attachment, ["is_anonymous", "type", "allows_multiple_answers", "correct_option_id", "explanation", "explanation_parse_mode", "open_period", "is_closed", "disable_notification", "protect_content", "reply_markup", "message_effect_id"]), + **generate_extra_fields( + attachment, + [ + "is_anonymous", + "type", + "allows_multiple_answers", + "correct_option_id", + "explanation", + "explanation_parse_mode", + "open_period", + "is_closed", + "disable_notification", + "protect_content", + "reply_markup", + "message_effect_id", + ], + ), ) if isinstance(attachment, Sticker): sticker = await attachment.get_bytes(self) if attachment.id is None else attachment.id await bot.send_sticker( chat_id, sticker, - **generate_extra_fields(attachment, ["disable_notification", "protect_content", "reply_markup", "emoji", "message_effect_id"]), + **generate_extra_fields( + attachment, + ["disable_notification", "protect_content", "reply_markup", "emoji", "message_effect_id"], + ), ) if isinstance(attachment, Audio): attachment_bytes = await attachment.get_bytes(self) @@ -303,7 +365,10 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes files += [ InputMediaAudio( attachment_bytes, - **generate_extra_fields(attachment, ["filename", "caption", "parse_mode", "performer", "title", "thumbnail"]), + **generate_extra_fields( + attachment, + ["filename", "caption", "parse_mode", "performer", "title", "thumbnail"], + ), ), ] else: @@ -311,7 +376,18 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes chat_id, attachment_bytes, caption=message.text, - **generate_extra_fields(attachment, ["performer", "title", "disable_notification", "reply_markup", "parse_mode", "thumbnail", "message_effect_id"]), + **generate_extra_fields( + attachment, + [ + "performer", + "title", + "disable_notification", + "reply_markup", + "parse_mode", + "thumbnail", + "message_effect_id", + ], + ), ) message_text_covered = True if isinstance(attachment, Video): @@ -321,7 +397,17 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes files += [ InputMediaVideo( attachment_bytes, - **generate_extra_fields(attachment, ["filename", "caption", "parse_mode", "supports_streaming", "has_spoiler", "thumbnail"]), + **generate_extra_fields( + attachment, + [ + "filename", + "caption", + "parse_mode", + "supports_streaming", + "has_spoiler", + "thumbnail", + ], + ), ), ] else: @@ -329,7 +415,19 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes chat_id, attachment_bytes, caption=message.text, - **generate_extra_fields(attachment, ["disable_notification", "reply_markup", "parse_mode", "supports_streaming", "has_spoiler", "thumbnail", "filename", "message_effect_id"]), + **generate_extra_fields( + attachment, + [ + "disable_notification", + "reply_markup", + "parse_mode", + "supports_streaming", + "has_spoiler", + "thumbnail", + "filename", + "message_effect_id", + ], + ), ) message_text_covered = True if isinstance(attachment, Animation): @@ -339,7 +437,9 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes files += [ InputMediaAnimation( attachment_bytes, - **generate_extra_fields(attachment, ["filename", "caption", "parse_mode", "has_spoiler", "thumbnail"]), + **generate_extra_fields( + attachment, ["filename", "caption", "parse_mode", "has_spoiler", "thumbnail"] + ), ), ] else: @@ -347,7 +447,18 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes chat_id, attachment_bytes, caption=message.text, - **generate_extra_fields(attachment, ["parse_mode", "disable_notification", "reply_markup", "has_spoiler", "thumbnail", "filename", "message_effect_id"]), + **generate_extra_fields( + attachment, + [ + "parse_mode", + "disable_notification", + "reply_markup", + "has_spoiler", + "thumbnail", + "filename", + "message_effect_id", + ], + ), ) message_text_covered = True if isinstance(attachment, Image): @@ -357,7 +468,9 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes files += [ InputMediaPhoto( attachment_bytes, - **generate_extra_fields(attachment, ["filename", "caption", "parse_mode", "has_spoiler"]), + **generate_extra_fields( + attachment, ["filename", "caption", "parse_mode", "has_spoiler"] + ), ), ] else: @@ -365,7 +478,17 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes chat_id, attachment_bytes, caption=message.text, - **generate_extra_fields(attachment, ["disable_notification", "reply_markup", "parse_mode", "has_spoiler", "filename", "message_effect_id"]), + **generate_extra_fields( + attachment, + [ + "disable_notification", + "reply_markup", + "parse_mode", + "has_spoiler", + "filename", + "message_effect_id", + ], + ), ) message_text_covered = True if isinstance(attachment, Document): @@ -375,7 +498,16 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes files += [ InputMediaDocument( attachment_bytes, - **generate_extra_fields(attachment, ["filename", "caption", "parse_mode", "disable_content_type_detection", "thumbnail"]), + **generate_extra_fields( + attachment, + [ + "filename", + "caption", + "parse_mode", + "disable_content_type_detection", + "thumbnail", + ], + ), ), ] else: @@ -383,7 +515,17 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes chat_id, attachment_bytes, caption=message.text, - **generate_extra_fields(attachment, ["disable_notification", "reply_markup", "parse_mode", "thumbnail", "filename", "message_effect_id"]), + **generate_extra_fields( + attachment, + [ + "disable_notification", + "reply_markup", + "parse_mode", + "thumbnail", + "filename", + "message_effect_id", + ], + ), ) message_text_covered = True if isinstance(attachment, VoiceMessage): @@ -393,7 +535,17 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes chat_id, attachment_bytes, caption=message.text, - **generate_extra_fields(attachment, ["disable_notification", "reply_markup", "parse_mode", "filename", "protect_content", "message_effect_id"]), + **generate_extra_fields( + attachment, + [ + "disable_notification", + "reply_markup", + "parse_mode", + "filename", + "protect_content", + "message_effect_id", + ], + ), ) message_text_covered = True if isinstance(attachment, VideoMessage): @@ -403,7 +555,18 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes chat_id, attachment_bytes, caption=message.text, - **generate_extra_fields(attachment, ["disable_notification", "reply_markup", "parse_mode", "thumbnail", "filename", "protect_content", "message_effect_id"]), + **generate_extra_fields( + attachment, + [ + "disable_notification", + "reply_markup", + "parse_mode", + "thumbnail", + "filename", + "protect_content", + "message_effect_id", + ], + ), ) message_text_covered = True if len(files) > 0: @@ -418,12 +581,13 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes await bot.send_message( chat_id, message.text, - **generate_extra_fields(message, ["parse_mode", "disable_notification", "protect_content", "reply_markup", "message_effect_id"]), + **generate_extra_fields( + message, + ["parse_mode", "disable_notification", "protect_content", "reply_markup", "message_effect_id"], + ), ) - async def _on_event( - self, update: Update, _: ContextTypes.DEFAULT_TYPE, create_message: Callable[[Update], Message] - ) -> None: + async def _on_event(self, update: Update, _: Any, create_message: Callable[[Update], Message]) -> None: """ Process Telegram update, run pipeline and send response to Telegram. @@ -441,7 +605,7 @@ async def _on_event( self.application.bot, update.effective_chat.id, resp.last_response ) - async def on_message(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> None: + async def on_message(self, update: Update, _: Any) -> None: """ Process normal Telegram update, extracting DFF message from it using :py:meth:`~._AbstractTelegramInterface.extract_message_from_telegram`. @@ -451,7 +615,7 @@ async def on_message(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> None await self._on_event(update, _, lambda s: self.extract_message_from_telegram(s.message)) - async def on_callback(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> None: + async def on_callback(self, update: Update, _: Any) -> None: """ Process Telegram callback update, creating empty DFF message with only one callback query attachment from `callback_query.data` field. @@ -459,7 +623,9 @@ async def on_callback(self, update: Update, _: ContextTypes.DEFAULT_TYPE) -> Non :param update: Telegram update that will be processed. """ - await self._on_event(update, _, lambda s: Message(attachments=[CallbackQuery(query_string=s.callback_query.data)])) + await self._on_event( + update, _, lambda s: Message(attachments=[CallbackQuery(query_string=s.callback_query.data)]) + ) async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs): self._pipeline_runner = pipeline_runner diff --git a/dff/script/conditions/std_conditions.py b/dff/script/conditions/std_conditions.py index 56ceb11e8..8568a98ce 100644 --- a/dff/script/conditions/std_conditions.py +++ b/dff/script/conditions/std_conditions.py @@ -252,7 +252,8 @@ def false_handler(ctx: Context, pipeline: Pipeline) -> bool: def has_callback_query(expected_query_string: str) -> Callable[[Context, Pipeline], bool]: """ - Condition that checks if :py:attr:`~.CallbackQuery.query_string` of the last message matches `expected_query_string`. + Condition that checks if :py:attr:`~.CallbackQuery.query_string` + of the last message matches `expected_query_string`. :param expected_query_string: The expected query string to compare with. :return: The callback query comparator function. diff --git a/dff/script/core/message.py b/dff/script/core/message.py index a6a410e0a..2a58fedf9 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -311,7 +311,25 @@ class level variables to store message information. text: Optional[str] = None commands: Optional[List[Command]] = None - attachments: Optional[List[Union[CallbackQuery, Location, Contact, Invoice, Poll, Audio, Video, Animation, Image, Sticker, Document, VoiceMessage, VideoMessage]]] = None + attachments: Optional[ + List[ + Union[ + CallbackQuery, + Location, + Contact, + Invoice, + Poll, + Audio, + Video, + Animation, + Image, + Sticker, + Document, + VoiceMessage, + VideoMessage, + ] + ] + ] = None annotations: Optional[JSONSerializableDict] = None misc: Optional[JSONSerializableDict] = None original_message: Optional[SerializableVaue] = None @@ -320,7 +338,25 @@ def __init__( self, text: Optional[str] = None, commands: Optional[List[Command]] = None, - attachments: Optional[List[Union[CallbackQuery, Location, Contact, Invoice, Poll, Audio, Video, Animation, Image, Sticker, Document, VoiceMessage, VideoMessage]]] = None, + attachments: Optional[ + List[ + Union[ + CallbackQuery, + Location, + Contact, + Invoice, + Poll, + Audio, + Video, + Animation, + Image, + Sticker, + Document, + VoiceMessage, + VideoMessage, + ] + ] + ] = None, annotations: Optional[JSONSerializableDict] = None, misc: Optional[JSONSerializableDict] = None, **kwargs, diff --git a/dff/utils/pydantic/__init__.py b/dff/utils/pydantic/__init__.py index 656538e90..63d5917b2 100644 --- a/dff/utils/pydantic/__init__.py +++ b/dff/utils/pydantic/__init__.py @@ -1 +1,8 @@ -from .serializing import JSONSerializableDict, SerializableVaue, json_pickle_serializer, json_pickle_validator, pickle_serializer, pickle_validator +from .serializing import ( + JSONSerializableDict, + SerializableVaue, + json_pickle_serializer, + json_pickle_validator, + pickle_serializer, + pickle_validator, +) diff --git a/dff/utils/pydantic/serializing.py b/dff/utils/pydantic/serializing.py index 4b78b9f11..5e21a49a0 100644 --- a/dff/utils/pydantic/serializing.py +++ b/dff/utils/pydantic/serializing.py @@ -15,6 +15,7 @@ class WrapperModel(RootModel): """ Wrapper model for testing whether an object is serializable to JSON. """ + root: Any @@ -42,7 +43,9 @@ def pickle_validator(value: JsonValue) -> Any: return loads(decodebytes(value.encode())) -def json_pickle_serializer(model: Serializable, original_serializer: Callable[[Serializable], Serializable]) -> Serializable: +def json_pickle_serializer( + model: Serializable, original_serializer: Callable[[Serializable], Serializable] +) -> Serializable: """ Serializer function that serializes a dictionary or Pydantic object to JSON. For every object field, it checks whether the field is JSON serializable, diff --git a/tests/messengers/telegram/utils.py b/tests/messengers/telegram/utils.py index 31fe24d37..b439e12f5 100644 --- a/tests/messengers/telegram/utils.py +++ b/tests/messengers/telegram/utils.py @@ -66,7 +66,9 @@ def create(cls, interface: _AbstractTelegramInterface, happy_path: List[PathStep return instance @contextmanager - def _check_context_and_trace(self, last_request: Message, last_response: Message, last_trace: List[str]) -> Iterator[None]: + def _check_context_and_trace( + self, last_request: Message, last_response: Message, last_trace: List[str] + ) -> Iterator[None]: self.bot.latest_trace = list() self.latest_ctx = None yield @@ -84,7 +86,7 @@ async def wrapped_pipeline_runner( ) -> Context: self.latest_ctx = await original_pipeline_runner(message, ctx_id, update_ctx_misc) return self.latest_ctx - + self.interface._pipeline_runner = wrapped_pipeline_runner yield self.interface._pipeline_runner = original_pipeline_runner diff --git a/tutorials/messengers/telegram/2_attachments.py b/tutorials/messengers/telegram/2_attachments.py index 5ba3125a3..9c6ee5e87 100644 --- a/tutorials/messengers/telegram/2_attachments.py +++ b/tutorials/messengers/telegram/2_attachments.py @@ -35,7 +35,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) Sticker, Video, VideoMessage, - VoiceMessage + VoiceMessage, ) from dff.utils.testing.common import is_interactive_mode diff --git a/tutorials/messengers/telegram/3_advanced.py b/tutorials/messengers/telegram/3_advanced.py index ce133786f..3f77f4b78 100644 --- a/tutorials/messengers/telegram/3_advanced.py +++ b/tutorials/messengers/telegram/3_advanced.py @@ -49,8 +49,10 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) 5. Document with a thumbnail. 6. Attachment bytes hash. -Check out [this](https://docs.python-telegram-bot.org/en/v21.3/telegram.bot.html#telegram.Bot) -class for information about different arguments for sending attachments, `send_...` methods. +Check out +[this](https://docs.python-telegram-bot.org/en/v21.3/telegram.bot.html#telegram.Bot) +class for information about different arguments +for sending attachments, `send_...` methods. Last option ("Raw attachments!") button might be especially interesting, because it shows how bot precepts different telegram attachments sent by user @@ -104,7 +106,8 @@ async def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: ) else: return Message( - "Last request did not contain any data attachment!\nRun /start command again to restart." + "Last request did not contain any data attachment!\n" + "Run /start command again to restart." ) @@ -182,7 +185,9 @@ async def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: }, "attachments_node": { RESPONSE: Message( - "Here's your message with multiple attachments (a location and a sticker)!\nRun /start command again to restart.", + "Here's your message with multiple attachments " + "(a location and a sticker)!\n" + "Run /start command again to restart.", attachments=[ Location(**location_data), Sticker(**sticker_data), @@ -191,19 +196,22 @@ async def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: }, "secret_node": { RESPONSE: Message( - "Here's your secret image! Run /start command again to restart.", + "Here's your secret image!" + "Run /start command again to restart.", attachments=[Image(**image_data)], ), }, "thumbnail_node": { RESPONSE: Message( - "Here's your document with tumbnail! Run /start command again to restart.", + "Here's your document with tumbnail!" + "Run /start command again to restart.", attachments=[Document(**document_data)], ), }, "hash_init_node": { RESPONSE: Message( - "Alright! Now send me a message with data attachment (audio, video, animation, image, sticker or document)!" + "Alright! Now send me a message with data attachment" + "(audio, video, animation, image, sticker or document)!" ), TRANSITIONS: {"hash_request_node": cnd.true()}, }, @@ -212,7 +220,8 @@ async def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: }, "fallback_node": { RESPONSE: Message( - "Bot has entered unrecoverable state :/\nRun /start command again to restart." + "Bot has entered unrecoverable state:" + "/\nRun /start command again to restart." ), }, }, From 0c12ad2468c21e375a4b92fcc0455ccf17caf574 Mon Sep 17 00:00:00 2001 From: pseusys Date: Fri, 21 Jun 2024 14:29:18 +0200 Subject: [PATCH 107/140] formatting fixed --- .../messengers/telegram/2_attachments.py | 9 ++++--- tutorials/messengers/telegram/3_advanced.py | 25 +++++++++++-------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/tutorials/messengers/telegram/2_attachments.py b/tutorials/messengers/telegram/2_attachments.py index 9c6ee5e87..988202aaa 100644 --- a/tutorials/messengers/telegram/2_attachments.py +++ b/tutorials/messengers/telegram/2_attachments.py @@ -50,7 +50,8 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) # %% -EXAMPLE_ATTACHMENT_SOURCE = "https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments" +EXAMPLE_ATTACHMENT_SOURCE = "https://github.com/deeppavlov/" ++"dialog_flow_framework/wiki/example_attachments" location_data = {"latitude": 50.65, "longitude": 3.916667} @@ -61,7 +62,8 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) } sticker_data = { - "id": "CAACAgIAAxkBAAErAAFXZibO5ksphCKSXSe1CYiw5588yqsAAkEAAzyKVxogmx2BPCogYDQE", + "id": "CAACAgIAAxkBAAErAAFXZibO5ksphCKS" + + "XSe1CYiw5588yqsAAkEAAzyKVxogmx2BPCogYDQE", "title": "A sticker I've just found", } @@ -80,7 +82,8 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) } animation_data = { - # For some reason, if we don't define filename explicitly, animation is sent as file. + # For some reason, if we don't define filename explicitly, + # animation is sent as file. "source": HttpUrl( f"{EXAMPLE_ATTACHMENT_SOURCE}/hong-kong-simplyart4794.gif" ), diff --git a/tutorials/messengers/telegram/3_advanced.py b/tutorials/messengers/telegram/3_advanced.py index 3f77f4b78..df41cab0d 100644 --- a/tutorials/messengers/telegram/3_advanced.py +++ b/tutorials/messengers/telegram/3_advanced.py @@ -61,21 +61,23 @@ class for information about different arguments # %% -EXAMPLE_ATTACHMENT_SOURCE = "https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments" +EXAMPLE_ATTACHMENT_SOURCE = "https://github.com/deeppavlov/" ++"dialog_flow_framework/wiki/example_attachments" image_url = HttpUrl(f"{EXAMPLE_ATTACHMENT_SOURCE}/deeppavlov.png") formatted_text = r""" Here's your formatted text\! You can see **text in bold** and _text in italic_\. -\> Here's a [link](https://github.com/deeppavlov/dialog_flow_framework) in a quote\. +\> Here's a [link](https://deeppavlov.ai/) in a quote\. Run /start command again to restart\. """ location_data = {"latitude": 59.9386, "longitude": 30.3141} sticker_data = { - "id": "CAACAgIAAxkBAAErBZ1mKAbZvEOmhscojaIL5q0u8vgp1wACRygAAiSjCUtLa7RHZy76ezQE", + "id": "CAACAgIAAxkBAAErBZ1mKAbZvEOmhscojaIL5q0u8v" + + "gp1wACRygAAiSjCUtLa7RHZy76ezQE", } image_data = { @@ -100,14 +102,15 @@ async def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: ] if len(atch) > 0: atch_hash = hash(await atch[0].get_bytes(pipe.messenger_interface)) - resp_format = "Here's your previous request hash: `{}`!\nRun /start command again to restart." + resp_format = "Here's your previous request hash: `{}`!\n" + +"Run /start command again to restart." return Message( resp_format.format(atch_hash, parse_mode=ParseMode.MARKDOWN_V2) ) else: return Message( "Last request did not contain any data attachment!\n" - "Run /start command again to restart." + + "Run /start command again to restart." ) @@ -186,8 +189,8 @@ async def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: "attachments_node": { RESPONSE: Message( "Here's your message with multiple attachments " - "(a location and a sticker)!\n" - "Run /start command again to restart.", + + "(a location and a sticker)!\n" + + "Run /start command again to restart.", attachments=[ Location(**location_data), Sticker(**sticker_data), @@ -197,21 +200,21 @@ async def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: "secret_node": { RESPONSE: Message( "Here's your secret image!" - "Run /start command again to restart.", + + "Run /start command again to restart.", attachments=[Image(**image_data)], ), }, "thumbnail_node": { RESPONSE: Message( "Here's your document with tumbnail!" - "Run /start command again to restart.", + + "Run /start command again to restart.", attachments=[Document(**document_data)], ), }, "hash_init_node": { RESPONSE: Message( "Alright! Now send me a message with data attachment" - "(audio, video, animation, image, sticker or document)!" + + "(audio, video, animation, image, sticker or document)!" ), TRANSITIONS: {"hash_request_node": cnd.true()}, }, @@ -221,7 +224,7 @@ async def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: "fallback_node": { RESPONSE: Message( "Bot has entered unrecoverable state:" - "/\nRun /start command again to restart." + + "/\nRun /start command again to restart." ), }, }, From 652453c1e8095eae54a1f9a6039395552f2b02d3 Mon Sep 17 00:00:00 2001 From: pseusys Date: Fri, 21 Jun 2024 14:42:45 +0200 Subject: [PATCH 108/140] Once again string concatenation fixed --- .../messengers/telegram/2_attachments.py | 8 +++-- tutorials/messengers/telegram/3_advanced.py | 30 +++++++++++-------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/tutorials/messengers/telegram/2_attachments.py b/tutorials/messengers/telegram/2_attachments.py index 988202aaa..ba5fdb036 100644 --- a/tutorials/messengers/telegram/2_attachments.py +++ b/tutorials/messengers/telegram/2_attachments.py @@ -50,8 +50,10 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) # %% -EXAMPLE_ATTACHMENT_SOURCE = "https://github.com/deeppavlov/" -+"dialog_flow_framework/wiki/example_attachments" +EXAMPLE_ATTACHMENT_SOURCE = ( + "https://github.com/deeppavlov/" + "dialog_flow_framework/wiki/example_attachments" +) location_data = {"latitude": 50.65, "longitude": 3.916667} @@ -63,7 +65,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) sticker_data = { "id": "CAACAgIAAxkBAAErAAFXZibO5ksphCKS" - + "XSe1CYiw5588yqsAAkEAAzyKVxogmx2BPCogYDQE", + "XSe1CYiw5588yqsAAkEAAzyKVxogmx2BPCogYDQE", "title": "A sticker I've just found", } diff --git a/tutorials/messengers/telegram/3_advanced.py b/tutorials/messengers/telegram/3_advanced.py index df41cab0d..3a734be91 100644 --- a/tutorials/messengers/telegram/3_advanced.py +++ b/tutorials/messengers/telegram/3_advanced.py @@ -61,8 +61,10 @@ class for information about different arguments # %% -EXAMPLE_ATTACHMENT_SOURCE = "https://github.com/deeppavlov/" -+"dialog_flow_framework/wiki/example_attachments" +EXAMPLE_ATTACHMENT_SOURCE = ( + "https://github.com/deeppavlov/" + "dialog_flow_framework/wiki/example_attachments" +) image_url = HttpUrl(f"{EXAMPLE_ATTACHMENT_SOURCE}/deeppavlov.png") @@ -77,7 +79,7 @@ class for information about different arguments sticker_data = { "id": "CAACAgIAAxkBAAErBZ1mKAbZvEOmhscojaIL5q0u8v" - + "gp1wACRygAAiSjCUtLa7RHZy76ezQE", + "gp1wACRygAAiSjCUtLa7RHZy76ezQE", } image_data = { @@ -102,15 +104,17 @@ async def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: ] if len(atch) > 0: atch_hash = hash(await atch[0].get_bytes(pipe.messenger_interface)) - resp_format = "Here's your previous request hash: `{}`!\n" - +"Run /start command again to restart." + resp_format = ( + "Here's your previous request hash: `{}`!\n" + "Run /start command again to restart." + ) return Message( resp_format.format(atch_hash, parse_mode=ParseMode.MARKDOWN_V2) ) else: return Message( "Last request did not contain any data attachment!\n" - + "Run /start command again to restart." + "Run /start command again to restart." ) @@ -189,8 +193,8 @@ async def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: "attachments_node": { RESPONSE: Message( "Here's your message with multiple attachments " - + "(a location and a sticker)!\n" - + "Run /start command again to restart.", + "(a location and a sticker)!\n" + "Run /start command again to restart.", attachments=[ Location(**location_data), Sticker(**sticker_data), @@ -200,21 +204,21 @@ async def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: "secret_node": { RESPONSE: Message( "Here's your secret image!" - + "Run /start command again to restart.", + "Run /start command again to restart.", attachments=[Image(**image_data)], ), }, "thumbnail_node": { RESPONSE: Message( "Here's your document with tumbnail!" - + "Run /start command again to restart.", + "Run /start command again to restart.", attachments=[Document(**document_data)], ), }, "hash_init_node": { RESPONSE: Message( - "Alright! Now send me a message with data attachment" - + "(audio, video, animation, image, sticker or document)!" + "Alright! Now send me a message with data attachment " + "(audio, video, animation, image, sticker or document)!" ), TRANSITIONS: {"hash_request_node": cnd.true()}, }, @@ -224,7 +228,7 @@ async def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: "fallback_node": { RESPONSE: Message( "Bot has entered unrecoverable state:" - + "/\nRun /start command again to restart." + "/\nRun /start command again to restart." ), }, }, From a50fabed67bb986f83beac39d3e90144713e10c9 Mon Sep 17 00:00:00 2001 From: pseusys Date: Fri, 21 Jun 2024 14:49:05 +0200 Subject: [PATCH 109/140] once again: reformatting --- .../messengers/telegram/2_attachments.py | 18 +++++++------ tutorials/messengers/telegram/3_advanced.py | 26 ++++++++++--------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/tutorials/messengers/telegram/2_attachments.py b/tutorials/messengers/telegram/2_attachments.py index ba5fdb036..95e75e2f9 100644 --- a/tutorials/messengers/telegram/2_attachments.py +++ b/tutorials/messengers/telegram/2_attachments.py @@ -52,7 +52,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) EXAMPLE_ATTACHMENT_SOURCE = ( "https://github.com/deeppavlov/" - "dialog_flow_framework/wiki/example_attachments" + + "dialog_flow_framework/wiki/example_attachments" ) location_data = {"latitude": 50.65, "longitude": 3.916667} @@ -64,8 +64,10 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) } sticker_data = { - "id": "CAACAgIAAxkBAAErAAFXZibO5ksphCKS" - "XSe1CYiw5588yqsAAkEAAzyKVxogmx2BPCogYDQE", + "id": ( + "CAACAgIAAxkBAAErAAFXZibO5ksphCKS" + + "XSe1CYiw5588yqsAAkEAAzyKVxogmx2BPCogYDQE" + ), "title": "A sticker I've just found", } @@ -146,8 +148,8 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) "intro_node": { RESPONSE: Message( 'Type "location", "contact", "poll", "sticker" ' - '"audio", "video", "animation", "image", ' - '"document" or to receive a corresponding attachment!' + + '"audio", "video", "animation", "image", ' + + '"document" or to receive a corresponding attachment!' ), }, "location_node": { @@ -227,9 +229,9 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) "fallback_node": { RESPONSE: Message( "Unknown attachment type, try again! " - 'Supported attachments are: "location", ' - '"contact", "poll", "sticker", "audio", ' - '"video", "animation", "image" and "document".' + + 'Supported attachments are: "location", ' + + '"contact", "poll", "sticker", "audio", ' + + '"video", "animation", "image" and "document".' ), }, }, diff --git a/tutorials/messengers/telegram/3_advanced.py b/tutorials/messengers/telegram/3_advanced.py index 3a734be91..f1d86a948 100644 --- a/tutorials/messengers/telegram/3_advanced.py +++ b/tutorials/messengers/telegram/3_advanced.py @@ -63,7 +63,7 @@ class for information about different arguments EXAMPLE_ATTACHMENT_SOURCE = ( "https://github.com/deeppavlov/" - "dialog_flow_framework/wiki/example_attachments" + + "dialog_flow_framework/wiki/example_attachments" ) image_url = HttpUrl(f"{EXAMPLE_ATTACHMENT_SOURCE}/deeppavlov.png") @@ -78,8 +78,10 @@ class for information about different arguments location_data = {"latitude": 59.9386, "longitude": 30.3141} sticker_data = { - "id": "CAACAgIAAxkBAAErBZ1mKAbZvEOmhscojaIL5q0u8v" - "gp1wACRygAAiSjCUtLa7RHZy76ezQE", + "id": ( + "CAACAgIAAxkBAAErBZ1mKAbZvEOmhscojaIL5q0u8v" + + "gp1wACRygAAiSjCUtLa7RHZy76ezQE" + ), } image_data = { @@ -106,7 +108,7 @@ async def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: atch_hash = hash(await atch[0].get_bytes(pipe.messenger_interface)) resp_format = ( "Here's your previous request hash: `{}`!\n" - "Run /start command again to restart." + + "Run /start command again to restart." ) return Message( resp_format.format(atch_hash, parse_mode=ParseMode.MARKDOWN_V2) @@ -114,7 +116,7 @@ async def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: else: return Message( "Last request did not contain any data attachment!\n" - "Run /start command again to restart." + + "Run /start command again to restart." ) @@ -193,8 +195,8 @@ async def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: "attachments_node": { RESPONSE: Message( "Here's your message with multiple attachments " - "(a location and a sticker)!\n" - "Run /start command again to restart.", + + "(a location and a sticker)!\n" + + "Run /start command again to restart.", attachments=[ Location(**location_data), Sticker(**sticker_data), @@ -203,22 +205,22 @@ async def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: }, "secret_node": { RESPONSE: Message( - "Here's your secret image!" - "Run /start command again to restart.", + "Here's your secret image! " + + "Run /start command again to restart.", attachments=[Image(**image_data)], ), }, "thumbnail_node": { RESPONSE: Message( "Here's your document with tumbnail!" - "Run /start command again to restart.", + + "Run /start command again to restart.", attachments=[Document(**document_data)], ), }, "hash_init_node": { RESPONSE: Message( "Alright! Now send me a message with data attachment " - "(audio, video, animation, image, sticker or document)!" + + "(audio, video, animation, image, sticker or document)!" ), TRANSITIONS: {"hash_request_node": cnd.true()}, }, @@ -228,7 +230,7 @@ async def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: "fallback_node": { RESPONSE: Message( "Bot has entered unrecoverable state:" - "/\nRun /start command again to restart." + + "/\nRun /start command again to restart." ), }, }, From e55d8016293b6bb3f815704202ab3e5d0ac59c07 Mon Sep 17 00:00:00 2001 From: pseusys Date: Fri, 21 Jun 2024 15:00:02 +0200 Subject: [PATCH 110/140] one whitespace removed --- tutorials/messengers/telegram/3_advanced.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorials/messengers/telegram/3_advanced.py b/tutorials/messengers/telegram/3_advanced.py index f1d86a948..878507b67 100644 --- a/tutorials/messengers/telegram/3_advanced.py +++ b/tutorials/messengers/telegram/3_advanced.py @@ -212,7 +212,7 @@ async def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: }, "thumbnail_node": { RESPONSE: Message( - "Here's your document with tumbnail!" + "Here's your document with tumbnail! " + "Run /start command again to restart.", attachments=[Document(**document_data)], ), From 23ee7f24314298b74182f698d5b5688be89448b0 Mon Sep 17 00:00:00 2001 From: pseusys Date: Fri, 21 Jun 2024 15:26:03 +0200 Subject: [PATCH 111/140] json dump fixed --- tests/messengers/telegram/test_happy_paths.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/messengers/telegram/test_happy_paths.json b/tests/messengers/telegram/test_happy_paths.json index 276ecb9a4..3f0bafc0c 100644 --- a/tests/messengers/telegram/test_happy_paths.json +++ b/tests/messengers/telegram/test_happy_paths.json @@ -139,9 +139,9 @@ { "update": "Update(callback_query=CallbackQuery(chat_instance='-1', data='formatted', from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), id='1', message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 9, 20, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Bot', id=16, is_bot=True, username='dff_bot'), group_chat_created=False, location=Location(latitude=58.43162, longitude=27.792879), message_id=2, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), supergroup_chat_created=False)), update_id=2)", "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"formatted\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", - "response_message": "{\"text\":\"\\nHere's your formatted text\\\\!\\nYou can see **text in bold** and _text in italic_\\\\.\\n\\\\> Here's a [link](https:\/\/github.com\/deeppavlov\/dialog_flow_framework) in a quote\\\\.\\nRun \/start command again to restart\\\\.\\n\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null,\"parse_mode\":\"MarkdownV2\",\"__pickled_extra_fields__\":[]}", + "response_message": "{\"text\":\"\\nHere's your formatted text\\\\!\\nYou can see **text in bold** and _text in italic_\\\\.\\n\\\\> Here's a [link](https:\/\/deeppavlov.ai\/) in a quote\\\\.\\nRun \/start command again to restart\\\\.\\n\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null,\"parse_mode\":\"MarkdownV2\",\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_message(42, \"\\nHere's your formatted text\\\\!\\nYou can see **text in bold** and _text in italic_\\\\.\\n\\\\> Here's a [link](https://github.com/deeppavlov/dialog_flow_framework) in a quote\\\\.\\nRun /start command again to restart\\\\.\\n\", parse_mode=, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" + "send_message(42, \"\\nHere's your formatted text\\\\!\\nYou can see **text in bold** and _text in italic_\\\\.\\n\\\\> Here's a [link](https:\/\/deeppavlov.ai\/) in a quote\\\\.\\nRun /start command again to restart\\\\.\\n\", parse_mode=, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" ] }, { @@ -229,9 +229,9 @@ { "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 9, 57, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=13, supergroup_chat_created=False, text='some text'), update_id=13)", "received_message": "{\"text\":\"some text\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", - "response_message": "{\"text\":\"Bot has entered unrecoverable state :\/\\nRun \/start command again to restart.\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_message": "{\"text\":\"Bot has entered unrecoverable state:\/\\nRun \/start command again to restart.\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_message(42, 'Bot has entered unrecoverable state :/\\nRun /start command again to restart.', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" + "send_message(42, 'Bot has entered unrecoverable state:/\\nRun /start command again to restart.', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" ] }, { @@ -253,9 +253,9 @@ { "update": "Update(callback_query=CallbackQuery(chat_instance='-1', data='quit', from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), id='15', message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 10, 1, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Bot', id=16, is_bot=True, username='dff_bot'), group_chat_created=False, location=Location(latitude=58.43162, longitude=27.792879), message_id=16, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), supergroup_chat_created=False)), update_id=16)", "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"quit\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", - "response_message": "{\"text\":\"Bot has entered unrecoverable state :\/\\nRun \/start command again to restart.\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_message": "{\"text\":\"Bot has entered unrecoverable state:\/\\nRun \/start command again to restart.\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_message(42, 'Bot has entered unrecoverable state :/\\nRun /start command again to restart.', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" + "send_message(42, 'Bot has entered unrecoverable state:/\\nRun /start command again to restart.', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" ] } ] From f82d4ed20eb1b5168058ee8bb8b7ec42912a00a9 Mon Sep 17 00:00:00 2001 From: pseusys Date: Fri, 21 Jun 2024 15:34:07 +0200 Subject: [PATCH 112/140] import error check added --- dff/messengers/telegram/interface.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dff/messengers/telegram/interface.py b/dff/messengers/telegram/interface.py index 81c82fb51..8e52fba81 100644 --- a/dff/messengers/telegram/interface.py +++ b/dff/messengers/telegram/interface.py @@ -1,11 +1,15 @@ from pathlib import Path -from typing import Optional -from telegram import Update +from typing import Any, Optional from dff.pipeline.types import PipelineRunnerFunction from .abstract import _AbstractTelegramInterface +try: + from telegram import Update +except ImportError: + Update = Any + class LongpollingInterface(_AbstractTelegramInterface): """ From c6cd39c8a8a3475f94a1860eeffffbfef119dd5f Mon Sep 17 00:00:00 2001 From: pseusys Date: Sat, 22 Jun 2024 04:27:52 +0200 Subject: [PATCH 113/140] non-native hashes used --- .../messengers/telegram/test_happy_paths.json | 22 +++++++++---------- tests/messengers/telegram/utils.py | 10 ++++----- tutorials/messengers/telegram/3_advanced.py | 4 +++- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/tests/messengers/telegram/test_happy_paths.json b/tests/messengers/telegram/test_happy_paths.json index 3f0bafc0c..176deaf01 100644 --- a/tests/messengers/telegram/test_happy_paths.json +++ b/tests/messengers/telegram/test_happy_paths.json @@ -67,7 +67,7 @@ "received_message": "{\"text\":\"audio\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your audio!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/separation-william-king.mp3\",\"cached_filename\":null,\"id\":null,\"title\":\"Separation melody by William King\",\"use_cache\":true,\"dff_attachment_type\":\"audio\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_audio(42, <>, caption=\"Here's your audio!\", performer=None, title=None, disable_notification=None, reply_markup=None, parse_mode=None, thumbnail=None, message_effect_id=None)" + "send_audio(42, '75ff19e65eef262114e1a6d93794ed8b4af6135a0cd0620f58023fd5b57204f4', caption=\"Here's your audio!\", performer=None, title=None, disable_notification=None, reply_markup=None, parse_mode=None, thumbnail=None, message_effect_id=None)" ] }, { @@ -75,7 +75,7 @@ "received_message": "{\"text\":\"video\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your video!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/crownfall-lags-nkognit0.mp4\",\"cached_filename\":null,\"id\":null,\"title\":\"Epic Dota2 gameplay by Nkognit0\",\"use_cache\":true,\"dff_attachment_type\":\"video\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_video(42, <>, caption=\"Here's your video!\", disable_notification=None, reply_markup=None, parse_mode=None, supports_streaming=None, has_spoiler=None, thumbnail=None, filename=None, message_effect_id=None)" + "send_video(42, '11d3472ad8534d632beca6a452e2dc03b7ec9f4aba19fbb3346da43bac7389d6', caption=\"Here's your video!\", disable_notification=None, reply_markup=None, parse_mode=None, supports_streaming=None, has_spoiler=None, thumbnail=None, filename=None, message_effect_id=None)" ] }, { @@ -83,7 +83,7 @@ "received_message": "{\"text\":\"animation\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your animation!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/hong-kong-simplyart4794.gif\",\"cached_filename\":null,\"id\":null,\"title\":\"Hong Kong skyscraper views by Simplyart4794\",\"use_cache\":true,\"dff_attachment_type\":\"animation\",\"filename\":\"hk.gif\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_animation(42, <>, caption=\"Here's your animation!\", parse_mode=None, disable_notification=None, reply_markup=None, has_spoiler=None, thumbnail=None, filename='hk.gif', message_effect_id=None)" + "send_animation(42, '033391bab1eaefd6d8aa6ceb667b5dbaada7c4d6b1649a726541924a74539dc5', caption=\"Here's your animation!\", parse_mode=None, disable_notification=None, reply_markup=None, has_spoiler=None, thumbnail=None, filename='hk.gif', message_effect_id=None)" ] }, { @@ -91,7 +91,7 @@ "received_message": "{\"text\":\"image\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your image!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/deeppavlov.png\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov logo\",\"use_cache\":true,\"dff_attachment_type\":\"image\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_photo(42, <>, caption=\"Here's your image!\", disable_notification=None, reply_markup=None, parse_mode=None, has_spoiler=None, filename=None, message_effect_id=None)" + "send_photo(42, '9f39f64560e6415032325cae1fec1ca06b3cc1a3549208a8a35564c0d3749062', caption=\"Here's your image!\", disable_notification=None, reply_markup=None, parse_mode=None, has_spoiler=None, filename=None, message_effect_id=None)" ] }, { @@ -99,7 +99,7 @@ "received_message": "{\"text\":\"document\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your document!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/deeppavlov-article.pdf\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov article\",\"use_cache\":true,\"dff_attachment_type\":\"document\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_document(42, <>, caption=\"Here's your document!\", disable_notification=None, reply_markup=None, parse_mode=None, thumbnail=None, filename=None, message_effect_id=None)" + "send_document(42, '08a7a36671950baba722d94a87dea286f03d7a6f7d0c32d5707d877cd211739a', caption=\"Here's your document!\", disable_notification=None, reply_markup=None, parse_mode=None, thumbnail=None, filename=None, message_effect_id=None)" ] }, { @@ -107,7 +107,7 @@ "received_message": "{\"text\":\"voice message\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your voice message!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/separation-william-king.mp3\",\"cached_filename\":null,\"id\":null,\"title\":null,\"use_cache\":true,\"dff_attachment_type\":\"voice_message\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_voice(42, <>, caption=\"Here's your voice message!\", disable_notification=None, reply_markup=None, parse_mode=None, filename=None, protect_content=None, message_effect_id=None)" + "send_voice(42, '75ff19e65eef262114e1a6d93794ed8b4af6135a0cd0620f58023fd5b57204f4', caption=\"Here's your voice message!\", disable_notification=None, reply_markup=None, parse_mode=None, filename=None, protect_content=None, message_effect_id=None)" ] }, { @@ -115,7 +115,7 @@ "received_message": "{\"text\":\"video message\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your video message!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/crownfall-lags-nkognit0.mp4\",\"cached_filename\":null,\"id\":null,\"title\":null,\"use_cache\":true,\"dff_attachment_type\":\"video_message\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_video_note(42, <>, caption=\"Here's your video message!\", disable_notification=None, reply_markup=None, parse_mode=None, thumbnail=None, filename=None, protect_content=None, message_effect_id=None)" + "send_video_note(42, '11d3472ad8534d632beca6a452e2dc03b7ec9f4aba19fbb3346da43bac7389d6', caption=\"Here's your video message!\", disable_notification=None, reply_markup=None, parse_mode=None, thumbnail=None, filename=None, protect_content=None, message_effect_id=None)" ] }, { @@ -175,7 +175,7 @@ "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"secret\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":\"Here's your secret image! Run \/start command again to restart.\",\"commands\":null,\"attachments\":[{\"source\":\"https:\/\/github.com\/deeppavlov\/dialog_flow_framework\/wiki\/example_attachments\/deeppavlov.png\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov logo\",\"use_cache\":true,\"dff_attachment_type\":\"image\",\"has_spoiler\":true,\"filename\":\"deeppavlov_logo.png\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_photo(42, <>, caption=\"Here's your secret image! Run /start command again to restart.\", disable_notification=None, reply_markup=None, parse_mode=None, has_spoiler=True, filename='deeppavlov_logo.png', message_effect_id=None)" + "send_photo(42, '9f39f64560e6415032325cae1fec1ca06b3cc1a3549208a8a35564c0d3749062', caption=\"Here's your secret image! Run /start command again to restart.\", disable_notification=None, reply_markup=None, parse_mode=None, has_spoiler=True, filename='deeppavlov_logo.png', message_effect_id=None)" ] }, { @@ -191,7 +191,7 @@ "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"thumbnail\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":\"Here's your document with tumbnail! Run \/start command again to restart.\",\"commands\":null,\"attachments\":[{\"source\":\"https:\/\/github.com\/deeppavlov\/dialog_flow_framework\/wiki\/example_attachments\/deeppavlov-article.pdf\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov article\",\"use_cache\":true,\"dff_attachment_type\":\"document\",\"filename\":\"deeppavlov_article.pdf\",\"thumbnail\":\"gASVySsAAAAAAABCwisAAIlQTkcNChoKAAAADUlIRFIAAADIAAAAyAgCAAAAIjo5yQAAK4lJREFU\\neJzsnXdcFFfXx+\/sbF\/KsnSQLiiIUlSwgo+KYi9RAbvYS4wleZ7EqNFojEaTqOlqLLFh77F3jaCA\\nCEgREJDeWbb394PwIiIus7NTFrPfj3\/g7My9h+U3t557DlWj0QAjRrCGQrYBRj5MjMIyggtGYRnB\\nBaOwjOCCUVhGcMEoLCO4YBSWEVwwCssILhiFZQQXqGQbgBIlf6+qYhHZVuAMbEOzP0NhdifbDjS0\\n1xZLVbmSbBPwR1WuEsSQbQRK2quwgEZCtgWEoBGTbQFK2q2wjBg2RmEZwQWjsIzgglFYRnDBKCw0\\n8GXsEhGPL2PrU4hcRX1a4Y6dUYZFe13H0s7DEu9tTycxYflgp8Ro76sQhOipx2Ve3yZEMWH5KNfY\\nyZ1ua7nzUn7Q7ucjulrm7gz5FbWRNwv9l91fkDl1DuoSDJkPU1ilYh6DIt8QvD\/65qdcunCoS8LG\\nJ5OFCtZnAcddzCrOvex9raB7oHXWbJ+rmxPq9ZfF77Cgy8VyiYVGAzb13jvk3OYulnnPq10flnSh\\nQqqvgw8czw515FQOd31yLCuExxBM9rrDl5mk1zg3VLcrddizKo\/edmmhDsm704Z9HXywWMj74\/mI\\n9cEHm1t1JPM\/D0u7MGH5V0EHM2s6XMzrNanjPZK+Idz5YLtCGqzqyC0Jc46\/Vej\/6YO5tqyavvap\\n824vyxdYr4md8XHXs\/vShqZVO5\/L7ZNe4+zFLVjxYD4AQAMgCqQGQEOnKDtxC77scVitATFZoQo1\\nvDF+MgDgh6cfmdFFzSs6md3vyIuBEzzurX88tUzC\/Tsv6G6R76X8IImS3sKkblYvV\/c4nFdnczwr\\n1MO8JNz5ybGsAcR+K8TxYbZYTYgVTAum8GZhQHKVuylNYsuqiSvtXCs3+eT+Ig5NSgFqAMB837\/N\\naKIfnk4AAMSWek++umplwClvXsHauGl3Cv3rFGwf3qto76urHkUnlHfkyznBdi+aV5FV6xhkm\/Gf\\nDsn97FOTq9wjve5cyO39qNRnR8gvDTeoNRAF0mg0YF\/6kCflncrEFiGOqTym0Ncyj6RvhQg+WGEV\\nCS1\/SxlxIifk0sjVpSIelyGY3OkOC5axqPX\/1vQ8DEEaJ9MKAEB6tVNiRcchzvEAgP4OqSeHbWgo\\n4VhW6Ibgv\/akhQMAmFTFoq7nh1345que9b3bS75dvsCmTMzN4dt15BZtSYi8VeB37VWPdUH1n\/Y5\\nucOGXdPdOhsAcDqnz+K7S4tnRQoVzN3Ph18Yufa\/D+sHVeVi82eV7jIV7XFZpyDbTLK\/LeyB161b\\nR7YNOpNUJLBWbtVyA5WigiDIhCZdH3zQw7w0xDGlQGjzoMS3g0mln3Wuv1XO+dzeVVKzYNuMvzLD\\nxEpmgHXOZ4EnmLDcgV3dyaKwoZBO3MLYMu+JHvd9eK\/sODU+vPwqqfmSbufYNHlcWSeJitnBpJIC\\naUa7xVox6+4U+23svd+HV8BliJmwfIRrnA+vAADAospMaeJ+DmkMWOlrmXe\/2HdOlyte3CKJkpFY\\n4dXLLqNAaNPbLv19v0iB2FvNGsamwzh8i\/gCta9zhQkFdRuu515IqxQvGIFJgb1Pbv8l9OfA162L\\nAbInLXz14+VL+nb4dICLOas9dS\/txtbcasmmG3kH4ksUKizfhEsjV5vSDXo\/u1ai3Hgjb09c8ReD\\nXJf2dyLbHKS0g1mhUqVefTnHc9M\/e+KKsVUVAIDHFNIoKmzLxINSgfyTsy\/cNj688aKabFsQYegt\\n1vnUioWnMorr5GQbYhDk1UjD\/ng6pBNvX4SPgzmDbHO0YbgtVoVQPn5f8ph9yUZVteBaZnWnzY9+\\neVBAtiHaMFBh7Xtc7L0l9kxqBdmGGChCuWrJmRcDfk14Xiok25bWMThhVYrkkw+mRh9LrxIrtNym\\n1iDb\/2vnKFTaFhru5tQG73iy61GRSk2gTcgwLGHdf1nTdWvc0aSyNu88n9uLEIvIRKGCrxcEar9H\\nJFfPP5kxdt+zaq3vIfEY0DrWqr+zt95+pVQjsodNlYY5JVix6vC3ixw0GpBY4ZlU2RHh\/Xam9PPR\\n3Xo6m+NsF1IMQlg1YsWMo2kX0irJNqR9w6RSNg33WB7qTLYhwCCElVkuGrsvOaO8vR5HMTSW9u+w\\nbZQnDSZ5kEOysO69rBn95zO+tB0sUbYj+rtzL0T7kbsFRKaw9sYVLziZoUA2qDKiE64WzDuLAl14\\nLLIMIEdYagDWXc7ZcONDdkgiHWcu88QM3yCShvMkCEupUn95Oee7268IrvdfiCWbejbar58bl\/iq\\nSRDWsrOZO+4XElzpvxZTBnw+2m9ARwuC6yVaWPNPpO+KLSayRiMsGuXvOf4Ea4vQSemS0xlGVRGP\\nRKEevifpQW4NkZUSJ6z\/Xcz65WERYdUZaY5EoR79Z3JCAXEbFQQJ69ubucbROrnUSJRDdydllImI\\nqY4IYR14Urzq75cEVGREO1UixcDfEssEMgLqwl1Yj1\/xF5z8AI83tVNKBPLwXUkiOe5bHfgKK7Nc\\nNHx3klRpeO5C\/2KSioXzTqTLcfbhwlFYApky8mBqlViJXxVG0HEksWzLLXy3PfASlkoN5p\/ISCo2\\nUMdZI19dyb2agaOfEl7C+vFe\/tGnbTuCGiELDQBRh57nVuF1phIXYT15Vbf6cg4eJRvBkBqJctJf\\nKTg5l2AvLIlCFXUoVaY0OsO0A+ILBeuv4tIEYC+sZWdf5ODWwBrBnK+v5z1+xce8WIyFdTWjyrgb\\n2O6IPJgqUWC8soWlsKpE8jnH3xuRx4jBklstXXEuC9sysXSbWXAi\/Y\/22FypVeySZJqwTOQYqDSx\\naf4JpJSZ5j9kVuXAMoGKaS4zcxS49FEzzcizFS9gCDxe1jOwA2a\/GmbCupdTM+j3pwhPBRoU1k\/2\\nmL2KrZ+BQ3DRgM9lPLfGD9Qqu39+4pSlNr9ZRWOV95gtdvAnx1Y88XcwebS0B5OGTZA3bLpChUq9\\n7NyL9qgqWFRl+lpVr18ylVnOraaPOMWJLVRVf79CYvfoF0algQZq04ekYuEvDzHz7MXmhNAfj4qe\\nFrXLRXYNBQYQBWgaN86cLU1H9LTvwGX06GBWEF+5Pe7Nnc4uLr6+vjeuX5fL5X1Lz2\/9JkahUqeW\\nCp8VCwv5sttZNbXSdr95tf5a7pRAOzszDAIkYdAVVork3ltiK0WGFTsAOc4pR2gv6hsqFpt1\/NQp\\nb2\/vhusymWxu9Ox\/Hj4EAMyZO3fFZ5\/S6fSE+PiICRMBAIdjjgb3ehM\/Qq5SpxQLb2fX3M+tvZxR\\nhXmAOMJY3Nfx5\/Gd9S8HA2GtbZ8HubhMamSA7YI+jn4OpokJiTk52UOHDjUzb3lYKutFFpfLtbax\\nbroya\/qM+\/furV67ZmZ0dKslK1TqI4mlx5LKr2RUtUd9PV0R5O9oqmch+naFxXzZd3fy9SyEYAId\\nTRf1dZzZ07HpGHpg98DA7q3HdfH08mxxxdHREQCQnfXeYRYNpszo6TCjp0OZQLb19qtdsUUCWXs6\\n6r3+2sszs\/z0LETfFuvj05k\/Yzfiw5t+buabhnv0d9frvMqd27dTklM6enYcNnw4kvsVKvWeuOLV\\nl3Oq248HUeKKoAD9Gi29hFUqkDl9\/bBdTAaDnM2+Dncf2smSLANEctX2e6++u51f1x4CVQzsaHFz\\nYRuhubSjl7AWn8r49R9DP3hjxoQ3hnss6ONIegAWAMCrGunnl7JjnpYZ\/rv4bGVQNwf0jRZ6YfGl\\nSpu19+SGPf0Z2om3P9IHk\/kzhlxKq1xwMqOQT8ShBtREBdgemeqL+nH0L\/E313MNWVVUCrRrYucr\\n8wIMTVUAgBE+Vokrg0Z3sSLbEG0cfVqWVYE+aBlKYSnVmt1xBrMtqFZRheX0mnxGTV7DP0+W9OHH\\n3ef2ciTbsvdizaGfi\/bbPMKDUfuKKqoAakMceP3+CP04B2VXeCSxdMrh56hr1R8av4hdlsoue04V\\nV9FElZDmzR+mi2+XP\/fvt7Iy6PagiatXrny2YqVIKpOb2Co51mIbH7G9n5JjEMYzqJSqDSEcVCmi\\nUAor8Ic4cvZw1ErT\/Fjuiyt0YWmrn\/fr33\/vgf0UCvnjdOQ8S3q2cN688vLypisSS8+aTsMk9t1I\\ntaue3z7qtKBPBxQPohHW00JB4I+PUVSmJ5xXsVYpJ6jS97o7jhk7dsu2rVSqoedxeZfCgoIJ48ZX\\nVr51bEbKdan0j5JZIg2cjAe9XMweLe2J4kE0b\/aBeKJHV1Rhuf397+2e7NGiqtFjxnz3\/bb2qCoA\\nQAcnp1Nnzzg4ODS\/yKzNd7yzhffsGKQkLelLbH7do7xaFA\/qLCylWnOW2EwkjKoch3tb2eXafFND\\nBwzYvPU7GG5\/CSObcOzQ4fc9u1tsVkJAY5F93fbRzxQ5ac4jp5LR\/Ll1Ftbl9Mr8GuIWYFhlaQ73\\nttIk2mI7+fr6bv9pJ53eMrl3u8PHx+fnX39lMpktrnPK0+zv\/0iRCUix6nRKOYrj+DoL62Ryuc6V\\noIVZnmH\/z06KWtsWm42NzW+7d5ma6rsbbyD06dtn9Vdr373OrM23f7gDaP0qcCK3WhpfoPMxHt2E\\nJVOqL6dX6VoHOmiCUtvHu6C2vsodP\/9kb29PjEnEEBkVNSli0rvXmTV5No93k2ER2BWr84KWbsJK\\nKKirIMahT6O2jt9HlbURge6T5ct7BgURYQ+xrFm3zt3D493rpkUJJv\/vSE0kt7J1DjOpm7CQ5OXC\\nBIv0i6zqNk7oevv4fPzJUmLsIRgWi7Xz558gqJXUedaJByGllGB78qqlz4p0G+HpJqzrmUQkJKbI\\nBOY5N9u4h0LZvnMHAcaQRWdv70VLlrx7naKScTOvEG\/PZR1D0+ggrIwyUaYeu5LI4WZegeVthMpc\\nsGihR0cyVw4JYM68ue7u7u9et3hxBaiIPmHwIFe38bsOwnpCSMxdSCExzX+o\/R4nZ+ely5YRYAy5\\nmJqafva\/\/757HVIrTfP\/IdiY6y+qq0Q6rNPqICxiFhpYFZnUthYD5y9c0E5X2HUlbOjQ3n36vHud\\nU5RIsCVyleZ5qQ4Rl3UQVjIh4fk4JUnab+js7T1u\/HgCLDEQlq9c8e5FZlU28b3h5QwdVpqQCiu9\\nVJRXg\/9kRKNhad26AQDMmh3NYBic7x5+BHbv7u\/f8kQ\/rJKxKl8QbEkaHi3Wy2oiQl5RheU0sbbX\\nwsbGZviIEQRYYlAsXd7KgJImIjrT8a2cGhniANhIhXU3h4hMLLCijVlnRFQki0Vackey6NO3r5OT\\nU4uLlLYmzpgjlKmQJ7ZAKqzCWiIW5WCtm80wDE+KiCDADEODSqV+NHFCi4uwhIg1xRZkI47ViHRu\\ndVP3RX0UwFrngz2Dguzf9lgiHpXghFp0UcsNsNl0CnsQ5vWOHT9+5\/YdavWbngjSkJCW4V5OzUfd\\nbJDciUhYLyvF5UIi5iCQVnfWEaNGEmCDdjTyFLXwmJYbKKw+AGAvrA4dOvQMCoqLfbNR2Mp2D\/5U\\nId4pRiSsKjH5kWRodNrgsDCyrQAAdoAYAVpvwOscxKDBg5oLixSQZ4RAJCxDiILs69vV2tqabCsA\\nlbsAcBeQUnW\/kBAAviGl6iYyKjAdvCcUkuO72JzQ0FCyTSAZLy8vNptNrg0qNRAgiy+HqMWqM4BY\\ndZ28MYgGpj+qugMqgbYxFsxdBHPwGguGDR1y7sxZnApHSEa5qKdzyyhi74JIWHyyhUWj0VrdMiMe\\njSJHI7ml7Q6TMfjVHhgYSLqwXlZJMBPWS7LHWB09PU1MTMi1oRGqE8QM1nYDbItf5W6tedEQDMIB\\nd\/sQlo0NorUTAqCazwXmc8mqPSBQr5hVmFCAbKkc0eCd9GSW3fzIP2xOInm5uc+Skhpclkk\/O4kw\\nzF77iHFga4tj\/2KwSCSSixcuTI2aPPg\/A9PT0houurm5kWuVGllMhvbhLkej0cg2gVAqKipOHj8R\\nc\/RoUWHL+K5OLs7Z2WSmL0AY6rJtYUnwz3jeJtYGM8bCG4lE8v3WrTFHjkqlrQ9l7OxIPkRZK8Fo\\nHYv0tQYAAJfLJdsE3MnLy1uz6stH\/7R0Znd3d1+\/cUPTaguX2\/ZUH1cQ6qFtYRlCNEhDWWvAAZVK\\ndfP6jZijR+\/dvdv8OgzDYUOGRERF9g8JaX7dhOxgAgj10D7GWKRPhfCgurr67OnTMUdjXua8dTSX\\nx+ONHjt2yrSprY7T28tX0T6EhTq085d\/Z1\/F9JDtwj6Os4P1DW2anZW1e9euy5f+Fovf8pj19PKa\\nO2\/ekPChWlpoDPNL4kr7EJZIhNINN7daiu0OeqkAfQw0pVJ58fyFYzFH45\/EN9cHlUodNnz4lGlT\\nu3fvDrUV5FIkJNojGR1tC8sEVWxTbOHzsc+GTSTV1dVHDh0+deJEQUFB8+s8Hi8iKnJSZOS7Lu3v\\ng89HE18PQxDqoW1hmTLJb9XKyvQNRjKzpz266L8NJBTWxeajPAi+etWqvy9eqqt763Hfrl0jo6KG\\njxxhZqZbttzSUoLisrwPHhuRHsgXDRJk71nUQc6m4R72emQS2HwrD7WwYo4cbfoZgqCh4eFz58\/3\\n80eZXutVPsm51syRNTTtQ1jV1SScSMEWM3Pz2XPmTJ4y2YLH06ecFlNI4oEpiLztEQnLnAnzSc1Z\\nlfwsmcTa9aRnUM\/IqKiRo0frv1Igk8kUCpLPH7QWtKsVEAnL3ZJFbsrn4iKUuTc6mDO62HIaUuvo\\nY4AVh9ZQjhVH513Lo8eP61N1c1JTUrAqCjUOyEYUiITlYckmV1g5OTkymQxFyIbvRnl+N6plilQU\\nzAl2nKP38pX+vMjMJNsE4GGJ6CQ6ImEhnAjgh0wmi330KHTAAMxLlkqlL3NyGg6C0mg0Ty8vAtKl\\nFBYW1tY0HgB2cHTkIR51paam4mkXItwxFBabRv5SVkZ6OrbCevjgwYF9+588fiwQvFlBtbGxCRkQ\\nOmPWrKZc9hhSXV198MCBy39fzs7KaroIQZB\/QMC48eMmToqg0dvoZ69eJiFIZAu8bThIbkP0dvo7\\nkr8HfO\/uPayKqigvX7p4yYyp027dvCkQCKhUqourq6ubG5VKLS8vP3n8xISx47Z8++37HFfQcfHC\\nhWFhQ37asbNBVRYWFp5enjweT6PRPE1MXLt6zbgxY5KfPWv12ZI6WWM7V0vy6igEAJeNaJSJqMXy\\nsUUkUlx5lpRUV1en63Liu7zMyZkxdVpJSQmDwZgwaeL0mTPNzMzOnjmjUqpGjRktkUgOHvjr+LGY\\n3X\/sSkxI\/H3XH3quDjTw7Tff\/Ll7DwDAz98\/Iipy7LhxTXk0FArF6VOnYg4fSUlJmTBu\/A87to8c\\nNar5s2dSyiMPpu6N8FanthHwlwA6WiEN9YOoxTIjb\/F9dqTgt01VENBIpdLrV6\/pWVpeXl7kpIiS\\nkpKQ0NCbd++s37DBxcUlcuLELZu+3fbddyPCh1lZWa3f8PWtu3eDe\/dKiI+fEhkll+ubIOnrdev+\\n3L2HzeHs+PmnU2fPTIqIaJ6dhUajRURGnrlwftPmzVQqddnHS4\/HxDR9uvZKzvj9KUq1Rq7S3Lh2\\nXU9L9KeHE1KnHUTC8rThcFkkaGvx9LoVc4QhQfIfv6qBIM2li9rCvCCBTqezmMyZ0bP2HthvZ2fX\\nMNLKz2tcyxYKBNevXgUA2Nvb\/3Xo0JixYyEKRf8O0dLS0tzc\/PS5syNGajvIOiky4ujxY6ampqrX\\nmWvUAET8lbLhep4ZA742P2CkKzU+Pl5PS\/THgoV0tQWRXCiv8+WfTiE06dfi6XWLpjfu5Hu5KV0c\\nlU8eP66qrLTUI3Wqg4PDqbNnrJrFgHBxdaXT6U3NkrOLS8MPMAxv+\/EHkUikv4\/h4o8\/njJ1KtfC\\nos07\/fz9H8Q+4nA4ZQL58N1JiUUCPweTmGm+nW04e\/7YJZORn5y8rytS\/1WkU2snLqFhP0eHiRdO\\na1RVVS20aLVFXiFNIpFc1LvRsno7soirq+vylSs4HA6DwZg1Ozq4V6+mjyAIwspzFYmqGuBw6oez\\n4\/clJxYJLNnUMzO7dX49Czt96hQmluhJZ8SjbaTCCvVA+tXoj7uzYu0n\/IatA6kMTF9umVfY2AIf\\n2Lcf8z2NufPnP05MiE96+uWaNdiWjJqTM7t6WLKqxMqPDiQX82WJCYkvXhAdyvZdGFTIG3NheVoR\\nFOcEhjVbV9Wy\/j9l3+qt5nkFb\/r1V\/n5N65jP4ZlMBgGFdrU3oyRsCJopLfl0yJhjx8ff\/bjfrIt\\nqmeAhwUL8YomUmF525o4mhGRaXJ0mKRzx8ZzILcfMS7faSnovXv2NI+Y+KFizqQem951RahTiUB+\\n2zJc0AFNYmZs8bXTYdUJqbBgCvDRpVx0MBia6EmNm5JyOdj6eyuT26eJT69euYq3JYYAmw5\/P9or\\nnJaloVCVLOKGIu9jaCdL5DfrsC\/2UVfcT42G9hC6Ozf658RcYOcXtT65\/XnnTrwtMRD+efgwK2aL\\n85UvLLL0XcPTEwgCvvY6TGV0EFZvxFNN1Ezo9ibfyZkr7x30ZGZk7NlFTq5RIlEoFN9u\/AYAQCMj\\n8nYLBna00MkFVwdhdXMwdeW1zIONITCkGuka1\/BzYirtRa62Id3O7dtLiovxM8YQ2L93X3p6Gwlg\\nCKOfm26H0XVbT\/+Ph8W+6hIdTUJKkE0GldI4Kk\/JaGOFVywWf7pi5eGYozgZQwxqeYZaeF6jyNYo\\nXwF1HQTbAKoDhdkLZg8qKFbt+PFHsg18w0gf3daldRPW1O52+57gJaxQxzf+x0+etT0DjYuNPX3q\\n1PiPPsLJHlxRS5+oqjepxZebX2w4aqiu26uE6JkJ7mymSCol32Hp9fIH3d9Rt+1\/3ZzaermYmzPx\\n+lU7mLzJOlRWiaiWLz\/\/orlvUztBoaz8QlE4sIWq3kIj7x+YcWlfxZAQ8gOh17\/z7lyqju6Put3O\\npsNhXhi4kbQO9N7\/vA+FQrFk0eKKCkI3MfVCLZUXR6lqfwSg7f0DM1PN1i9royeRHwt9fm+d3bJ1\\ndsNFmEqFMLKzslYuW65Ukh9rCQmKypUasQ7bnVQYLJstDA8ls91yMKP3cdU5jJTOwprgZ2tnSsQS\\nPHL+efhw7ZerybaibVSCE+q6P3V9CobBuhV8SwvSjt+N6WJN17UjRCEsKgUa44tLuhil+o0xpia6\\nfY\/Hjx37et06HIzCDrVIWbUW3aOmHM2SGaR1iONR9VFoTqRMCbRD8VSbpFa9CQfVK0Bnv82\/9h\/Y\\ntuU7rI3CDJXwIlDmon58fLjEzpqE7r6LLWcwqlE1GmH1d7fwssbeF+Dqq+5NP3u6ofkSf\/\/tt7Wr\\n16hV5AdNfReV6Lw+j1OpYEAvEhz95gSjTBCJ8gzd8hBndA9qoUBoWyZuHCSGBMnsrNHo48ihQ4sX\\nLRIKyTxe2yptJEpBgI8X0YfrYQhM64Eyli5KYc3sac\/UfUDXJmdeNoZwhWEQPgDlVOj61WsfjR2n\\nf+QjjFHrm6LWxZHornB2sIOl7iEFGkApDiYNnhOMfVzo\/Sn\/afp5yliRqQlKv6uc7OzhQ4ZeuqCX\\nH7NYrhLK6v\/pU0gDapVQqGAKFUypEn3AeksLor3QPu6HNBzcu6BvdT4f5IZ59ti0XM6dR41b6A62\\n6qjR6MMi8vn85Z98surzL1B3iyG\/JJiuumO66o5K7z9oeoXGas8pqz2nIq+uQl2IWEJott5hnXk6\\n+cm0AL2wHM0Zk\/wxXixV0Tnb95oq\/7+NmDdZ6OyAvv1Xq9XHY2LGjBx55\/Ztfayqk+o7uGnK5kCB\\n0Is0v5DQE3irBuuVW0WvcdK3Izrq8\/i7qBimWbm0\/ScaXVVZTLBjXTUV1itOcH5e\/pxZ0XOjZyPY\\nVVQCIH99pA+83r9q\/HKuvdDXHepKRlXDD\/Yc9EUVlRG3Id3L2UxXP5kW6CUsNx4rKgDT9ElUhgpm\\n7NxnmpLR+HZ6uat+2VjdPGy9h4fH77t37T1woE\/fvsgLvn3rVnjYkGUfLy0pafDOkKtEV5UVKxUl\\nkbK8TrJs5ut\/JrJsM1k2u\/7nHPN+vEMNzy46lXEtswr173QsqWzzrcYzsSH26ANc3biPozNcCzbr\\n3WRAesYNzygT+X0fJ1dhFnzc6fIXdHGFvY1y95ZqN6fGHuTvW8zV27gyef0g4+DhQ71fS0omk4X2\\n7VdZWYm8cEsLVXiodOFsDwtWUps5Fh6Vdh589juVprGdgCBgb0qnNAtoZ8aEfe1MhnlbTgm0o8GU\\nrArxTw8KnhYJCmplqtfJ19QaTUmdvKkaDlXyfMocOzaawB5PntFmrnxrw6POLaQicDqKotpkYEeL\\nmwv1TYyob7fd2ZYzr5fjzw9bJqlCjYrNBeKKknLq0rUWf2yudrCt75gCfOUOtsrc1+fAmP9\/TovB\\nYAwcNOj4MW0Zmpvo6KqYPEY8Llz8OlLQUySP9LbLmO1zedfzxnPxGg0ornt7P4AP0srEx5+Vv6yS\\nTPSz7fNTvPYp5Ajz8+hUpdaAXw8Sl+nkqyEYZK7DYC1q1WBXDGPBK9iN7+XLAtr8z3kl5ZQaPrRg\\nFS\/3\/08X7ty+XSJpXOKi0dveDre3UW78tPbUH5URo8RtxZ9qyQ\/9\/ljc9RyH2saK2jc38uafSNei\\nKopMwEs9lXn4ak4+mjf5xEXW4ySCTqJH+NuEYHE4GYOJhr0Z438DXdZceal\/UfXAb\/74LwtokxZa\\nmXA0r4rf2Hn\/3v2Tx49PmzGjzTDdNJpmyljR4ulCNgtlT02lqL\/vt+t\/gcfSa5wzapyqpGYaTWNX\\nqAGgSmp2vaB7Nt9RrQGPXgfrhpQyVkUGvbYA0rwWmUYDywT0uiIGv4CilAkBZeGXFod3VFlb6jA3\\njHtK3\/IbQRm\/WDTKhnAPTIrCZga7ItR5\/5MShGmotaOivjVErebD1e9kpTDnWjR4+V1\/\/6loF0fl\\n1ytre3TDYBvEhs23YaeEOrYy7lao4YmXV195FQRey8jhzhYm\/5WWoopKqVM+sfxpfU0nD0TLKBdv\\nMr\/eYd4wuCSA\/\/7HxdMamyPv2AiLTYe3juo4fj8GMX0VHOv3fdS7T28nJ+d+If2HjxhRP3PZtIn\/\\nngh3vQJl276ssTDHPZ8RjaL6LPCEs2lFQgrtZZpQu6oaKCqlzlhpOWuicE6kSEt87vJKyva9pueu\\nsRA60+qPE5fx6QDMtoD1nRU2Z\/Kh1KNP9d2hY1a+cLzbuvfL2Qvnfbt2BQBUVlRu2rjx\/Llzrd42\\nOkz87f+Izr0jkYLpyy3TsnRwgTThqKMnCXsFyrt1VjTNNWVyEJvIuBvLOH6J3dTttgrms8KbCwIG\\nemLmd46lsAprpQE\/PK4U6dX70GvynW5taPWjhYsXWfB4D+7de3D\/wfvCN0SMEq39BGVuEj1RqcC0\\nZZbP0tG417JZag5bU1tHUSiQtk\/YCmtyoO3hKb5YlYaxsAAAB+NLph9N06cESCF1O78EXes\/anB9\\nW4UwdQIeCITQvC94yai0pSsYCsvRjJG4MsjGBEuzMXZ9mdbDXk\/HZQ2NqTBB46Havats\/QoyVfXa\\no1rz84ZqF0eSs5Loyh8TO2OrKuyFBQDYPdHbxgS9c8jrHUOdN9XtrFXbv6phGMAhD0uuZuuqWiaj\\n3QRaig6yH6HjKWckYC8saxO6nr211FK3jSoqrNn6ZQ2Payg5bbt0Uq6cS\/5hQCR4WDJ\/Ht8Jj5Jx\\nSe8x2Iu3JswV9eMS68463T9ppDjQ17B6n4jR4uAALPMP4AGLRjkzyw95kD6dwCtvzBeDXAd7otwZ\\nUHJ0aJmteaolM9E3D2K913SFoubDusY\/EkwB\/50vYNB1a0RRZKHShz2TvLvq4cqnHbyExaLB+6N8\\nEGaKaoHCxFbBQrqgMm+y0NwUZSf4LI02bIbNzYfo\/5z7jnPGz7fKL6rXE9X2L6rN701faeeOytFh\\nYuRF+XTpcvn6tb79dPAF0ofFfR0n43OMrwEcM105mjPPzOrGoeteBQQpTBC5eXHNVJGjdfjjNaeq\\nBprzP15lNbz0K4vLt3V2ddJowE\/7TbbtMisqpc5cYQm438Cmk2CzaVT7kwBqbAbmRgnbdM5pILhX\\nr5gTx52dnXfv3dvFF8v1pFbp78b9YYwXrlXgm0Ktq73J4Sm+KFYAxPbdkNw2f4oQdRI4SwvNgqkN\\n7vDQp99YfL7ZHHmA\/tIKSvSnvN8PNbqyzJwoBMI\/gaq6vhPkDKc5xQF6vf2OdupxQ9vua1d+9tnh\\nmKNsNhsAEPvoURbOkbfdecwzs7rSYXz\/9Ljn5hvja71lpM7uiFKee5v3UGHN+GF6DZFmR4g+ia6D\\nKfWNyoUb7CFTbY6cY\/PrtL0IxWWUnftMhs+wefyM8bpt1Xy+iD9jghgoX8oLBwNlaf13SvdgOMfC\\n1jsAzWPCiPc2qAwGY9z48ddu3li4eFHDlevXri9esFD\/7D1asDGh3VoUaMnBfWEG45X39\/H5xawt\\nt9veoH2DRuN8ZRVNrC0+0eC+kh3rMUizdvwia\/OvbzwITDjqIf2lQX4ydxcVk6GhUTVCMSSRQk+e\\n0VMy6HfjGE1beEyGZv3y2pGDm83+qB40+yMURlOGerVKeOnbHxLjnyQIBHXVVdVMJtPaxsbe3r5P\\nv77Dhg+3sXlzGuXk8RNfrVmDPK8JipV3Dh2+Pt+\/t+6hY1BAkLAAAFGHUmN02aK2jt9vlv9Ayw2b\\n\/lszZgg2U\/rMHOoXW8wzX+rwHnu5KTZ8Wuvb6R3vF8iEarkR5s5B7jnC5\/N\/3Pb9oYMHdbBYd2Gx\\naZQjU33H+L7XeQRbiBOWSg0iDyafTEYaJI1TmGAX99v7P9c8vVyqq0eoFtRqcOEm6\/eDJs2dClvF\\n3kY5f4pwzBCJltoheleq5UYKZ5B2eUml0nNnzn6\/bVt1lc6HNXQSFgRAzDTfSf6YnnzRCnFH1WAK\\nODzFV6ZMuZCG6PiDyMFPyeRSpa13dl07KzBUVf3AiALGhElGDpQkZ9Cu32dm5VKT0uhiSeMYlGeu\\n8vFSuDsrB\/aRBXaVtznw1chTFCVjANUVNp0EMfwlmkEmpm+8QIsKC+Pi4uJiY2\/duFlTo+\/R+zaB\\nADg+3XeCH3GqIlRYAAA6lXJiRtfIgylnUxFoi0IV2fuZ595t9cPOHrgstcMwCOiiCOiiaGjDauvq\\nR1cmHA3KpTJlnqrmu\/pZwnLLogpnCx5PrVaXFBfX1RHn2ANB4ECkD8GqImJW2AIGlXJierepyJbm\\nhI7d3\/cRujhHOkGhAB5X42inRr0A24S7s7K0tDQ9LS0zI4NIVVEp0MnpXVFHjNEHooX1epkAOjCl\\ny+ygtn9bqa2Pktn6OYIunoa1Oagdax4JIbtM6PDfc\/zQxePTHxKE1VDrngifTcPbXqyq9Rza6nU3\\np\/YRzbYB4q3lsakPlnQP0yWtEraQI6wGvhjktjfCm03TZkOdW381pZWBoLmZoTjJIMEWVRA51Pg7\\nmCStDPZzJO6M67uQKSwAwKwgh2vzA7Q4BmpoLIFTL2KNwh5TDnGvwfDOltfnBzhxiQv00CokCwsA\\n0NeNm\/xpr27v99+o6TJWQ9QRKJwgzGF69WDXC3P8rbD2M0YB+cKq7ylM6XGf9JzXq\/U4qioWV+Dc\\nvhstKf5hablM6rlZ3TYM86AYxjtoEMJ6HXuS8sdE74OTfXjsVkZUtZ2Ga6C3TMVzoxZ7qmqw99JU\\nN\/vbBTmbJa4IGk3Udg0SDEVYDUztbv9gSY8+72TcVJjZ891Cm18pLDWItFgIKSzBQVg0VkMnu3qw\\n662FgW6ofCrxw7CE9TqrOefhxz02hLvT4bfa9NrOw1W0N2EFMnIw3dDBmZcF2O9wqJjm7pbM+4u7\\nbxjmwcEu2g9WGJywGlgd5pa0Mrhvs6ZLxbKo7jK26b\/J6e1JWDV8zL9naNmIwLT\/9u6rX0BH\/DBQ\\nYTU0XQ8+7vHLeK+mUON1riEy08YBfjGBATn1J+4pltM0H7+A0xcurJ8\/kYFDqH2sMFzLGljU1+n5\\nZ8GzguxhCgRgakXgdA1UL6l\/4gk90KIPFVUUvgCb18CCx1v99cbz505369oFkwLxw9CF9XoxgrE3\\nwufhku4DO1rIrDrWufYDAEhklLQsQl0zUHMvDoN3gMnmTJkx89qt2zOnT8HCKNxpH38bAECwi\/nN\\nhYE3s6qXnYD5RzLpwtIzV9g+nuQEltGJK\/f0WgSnUKnhI0Z99tlKpw46pzklEeI8SLFCrQaXHzzZ\\ntW1TWUHC3RPlZJvTBlU1lJCJNuiCp8FU2pjx42fPndvJE5vwjUTS\/oTVgFqtvnX7jrPJZhfLe2Tb\\noo29xzjf79Yt\/3t9P0JnTIyMmhU9y90V+yxrxNBehdWEVJSqqNpKkZykwgaXplClAn3H2wpEOgxk\\nbRw6RERGzps\/l2UIoXP0oN0LqwGVUiSu+AkSHaSBHLJteUPMedaGncjWmSAoZOCgydOmDx7QH3ez\\nCOEDEVYTgpoEWH5Jzf+FRiE6DGkLpDIQPs2morqNhQbvbv6TJ0cNGxbONde5xzRkPjRhNVFXeRWS\\n3wOSK1TVc9TH8PVhy2+mf51q3RfIxNSsq5\/f4LCwoeHhdrYGtHOMIR+ssJoQ8p8D6TW1+AakTKND\\nJcRU+iKXGrnYqnl8dogCe3h6+gd2Dw7uGRYWZsLBJpy6wfLhC6s5UmGyVBgPSf6mQ3lKSTqdhst4\\nXyyBJi2yzC2gmfOsnJw6dPMPCAsb7Onp+aE2Tq3y7xJWc6RSPlWTIxZmaqSJGlkCiy4W8DNNWSij\\njKhUQKp2YHNsJQrH\/MretfJe9ra2HTu6w6R0wwbAv1dYrVInKGFSSqmwhi8oA6pKJk0sEpZSNLVQ\\ns8yoSjWDzrCm0y3FcjbLxJFJ54hldCrTncn4wHs3nTAKywgu\/EsbaiN4YxSWEVwwCssILhiFZQQX\\njMIyggtGYRnBBaOwjOCCUVhGcOH\/AgAA\/\/+S+QX6OV9s5wAAAABJRU5ErkJggpQu\\n\",\"__pickled_extra_fields__\":[\"thumbnail\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_document(42, <>, caption=\"Here's your document with tumbnail! Run /start command again to restart.\", disable_notification=None, reply_markup=None, parse_mode=None, thumbnail=<>, filename='deeppavlov_article.pdf', message_effect_id=None)" + "send_document(42, '08a7a36671950baba722d94a87dea286f03d7a6f7d0c32d5707d877cd211739a', caption=\"Here's your document with tumbnail! Run /start command again to restart.\", disable_notification=None, reply_markup=None, parse_mode=None, thumbnail='9f39f64560e6415032325cae1fec1ca06b3cc1a3549208a8a35564c0d3749062', filename='deeppavlov_article.pdf', message_effect_id=None)" ] }, { @@ -213,9 +213,9 @@ { "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 22, 20, 12, 23, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=12, sticker=Sticker(api_kwargs={'thumb': {'file_id': 'AAMCAgADGQEAAgS0Zk5Rpz6PkZu6Ef3Ef7GwnxAZYocAAiwCAAJHBkMF0YsKl8FLgq8BAAdtAAM1BA', 'file_unique_id': 'AQADLAIAAkcGQwVy', 'file_size': 5416, 'width': 128, 'height': 128}}, emoji='👨', file_id='CAACAgIAAxkBAAIEtGZOUac-j5GbuhH9xH-xsJ8QGWKHAAIsAgACRwZDBdGLCpfBS4KvNQQ', file_size=29492, file_unique_id='AgADLAIAAkcGQwU', height=512, is_animated=False, is_video=False, set_name='citati_prosto', thumbnail=PhotoSize(file_id='AAMCAgADGQEAAgS0Zk5Rpz6PkZu6Ef3Ef7GwnxAZYocAAiwCAAJHBkMF0YsKl8FLgq8BAAdtAAM1BA', file_size=5416, file_unique_id='AQADLAIAAkcGQwVy', height=128, width=128), type=StickerType.REGULAR, width=512), supergroup_chat_created=False), update_id=11)", "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"source\":null,\"cached_filename\":null,\"id\":\"CAACAgIAAxkBAAIEtGZOUac-j5GbuhH9xH-xsJ8QGWKHAAIsAgACRwZDBdGLCpfBS4KvNQQ\",\"title\":null,\"use_cache\":true,\"dff_attachment_type\":\"sticker\",\"is_animated\":false,\"is_video\":false,\"type\":\"regular\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", - "response_message": "{\"text\":\"Here's your previous request hash: `0`!\\nRun \/start command again to restart.\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_message": "{\"text\":\"Here's your previous request hash: `75681742958b21061e6d5ebb80340d62de4c7348e71fb8c1d2c356a2239b2334`!\\nRun \/start command again to restart.\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_message(42, \"Here's your previous request hash: `0`!\\nRun /start command again to restart.\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" + "send_message(42, \"Here's your previous request hash: `75681742958b21061e6d5ebb80340d62de4c7348e71fb8c1d2c356a2239b2334`!\\nRun /start command again to restart.\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" ] }, { diff --git a/tests/messengers/telegram/utils.py b/tests/messengers/telegram/utils.py index b439e12f5..0a2e18b0f 100644 --- a/tests/messengers/telegram/utils.py +++ b/tests/messengers/telegram/utils.py @@ -1,6 +1,7 @@ from asyncio import get_event_loop from contextlib import contextmanager from importlib import import_module +from hashlib import sha256 from typing import Any, Dict, Hashable, Iterator, List, Optional, Tuple from pydantic import BaseModel @@ -37,9 +38,8 @@ class MockBot(BaseModel, arbitrary_types_allowed=True): @staticmethod def representation(any: Any) -> str: if isinstance(any, bytes): - return "<>" - else: - return any.__repr__() + any = sha256(any).hexdigest() + return any.__repr__() def __getattribute__(self, name: str) -> Any: async def set_trace(*args, **kwargs): @@ -93,8 +93,8 @@ async def wrapped_pipeline_runner( @contextmanager def _wrap_populate_attachment(self) -> Iterator[None]: - async def wrapped_populate_attachment(_: DataAttachment) -> bytes: - return bytes() + async def wrapped_populate_attachment(att: DataAttachment) -> bytes: + return str(att.id).encode() original_populate_attachment = self.interface.populate_attachment self.interface.populate_attachment = wrapped_populate_attachment diff --git a/tutorials/messengers/telegram/3_advanced.py b/tutorials/messengers/telegram/3_advanced.py index 878507b67..98ce3b003 100644 --- a/tutorials/messengers/telegram/3_advanced.py +++ b/tutorials/messengers/telegram/3_advanced.py @@ -15,6 +15,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) # %% import os +from hashlib import sha256 from urllib.request import urlopen from pydantic import HttpUrl @@ -105,7 +106,8 @@ async def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: a for a in ctx.last_request.attachments if isinstance(a, DataAttachment) ] if len(atch) > 0: - atch_hash = hash(await atch[0].get_bytes(pipe.messenger_interface)) + atch = await atch[0].get_bytes(pipe.messenger_interface) + atch_hash = sha256(atch).hexdigest() resp_format = ( "Here's your previous request hash: `{}`!\n" + "Run /start command again to restart." From e95d236805a9f510d7a75fd29c0b40b3a402f717 Mon Sep 17 00:00:00 2001 From: pseusys Date: Sat, 22 Jun 2024 04:43:10 +0200 Subject: [PATCH 114/140] hashing description fixed --- tutorials/messengers/telegram/3_advanced.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tutorials/messengers/telegram/3_advanced.py b/tutorials/messengers/telegram/3_advanced.py index 98ce3b003..37aba9f35 100644 --- a/tutorials/messengers/telegram/3_advanced.py +++ b/tutorials/messengers/telegram/3_advanced.py @@ -109,7 +109,7 @@ async def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: atch = await atch[0].get_bytes(pipe.messenger_interface) atch_hash = sha256(atch).hexdigest() resp_format = ( - "Here's your previous request hash: `{}`!\n" + "Here's your previous request first attachment sha256 hash: `{}`!\n" + "Run /start command again to restart." ) return Message( @@ -164,7 +164,7 @@ async def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: ], [ InlineKeyboardButton( - "Attachment bytes hash!", + "First attachment bytes hash!", callback_data="hash", ), ], From d02fa87fc685cc3a6470c069649a3234b6da8684 Mon Sep 17 00:00:00 2001 From: Roman Zlobin Date: Mon, 24 Jun 2024 17:02:46 +0300 Subject: [PATCH 115/140] log level for tmpdir attachment_dir set to info --- dff/messengers/common/interface.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dff/messengers/common/interface.py b/dff/messengers/common/interface.py index 80943ba4f..e8b26ff1b 100644 --- a/dff/messengers/common/interface.py +++ b/dff/messengers/common/interface.py @@ -52,10 +52,10 @@ def __init__(self, attachments_directory: Optional[Path] = None) -> None: warning_end = "attachment data won't be cached locally!" if attachments_directory is None: self.attachments_directory = Path(tempdir) / f"dff-cache-{type(self).__name__}" - logger.warning(f"{warning_start} is None, so will be set to tempdir and {warning_end}") + logger.info(f"{warning_start} is None, so will be set to tempdir and {warning_end}") else: self.attachments_directory = attachments_directory - logger.warning(f"{warning_start} is in tempdir, so {warning_end}") + logger.info(f"{warning_start} is in tempdir, so {warning_end}") self.attachments_directory.mkdir(parents=True, exist_ok=True) @abc.abstractmethod From 680d6b68399edcd092d78b742ee1756d0deceb66 Mon Sep 17 00:00:00 2001 From: Roman Zlobin Date: Mon, 24 Jun 2024 17:25:30 +0300 Subject: [PATCH 116/140] add abstract class MessengerInterfaceWithAttachments --- dff/messengers/common/__init__.py | 7 +++++- dff/messengers/common/interface.py | 38 ++++++++++++++++------------- dff/messengers/telegram/abstract.py | 4 +-- dff/script/core/message.py | 6 ++--- 4 files changed, 32 insertions(+), 23 deletions(-) diff --git a/dff/messengers/common/__init__.py b/dff/messengers/common/__init__.py index 8bcf7eaa7..1dd46fa8f 100644 --- a/dff/messengers/common/__init__.py +++ b/dff/messengers/common/__init__.py @@ -1,4 +1,9 @@ # -*- coding: utf-8 -*- -from .interface import MessengerInterface, PollingMessengerInterface, CallbackMessengerInterface +from .interface import ( + MessengerInterface, + MessengerInterfaceWithAttachments, + PollingMessengerInterface, + CallbackMessengerInterface +) from .types import PollingInterfaceLoopFunction diff --git a/dff/messengers/common/interface.py b/dff/messengers/common/interface.py index e8b26ff1b..f2afefdd8 100644 --- a/dff/messengers/common/interface.py +++ b/dff/messengers/common/interface.py @@ -26,6 +26,23 @@ class MessengerInterface(abc.ABC): """ Class that represents a message interface used for communication between pipeline and users. It is responsible for connection between user and pipeline, as well as for request-response transactions. + """ + + @abc.abstractmethod + async def connect(self, pipeline_runner: PipelineRunnerFunction): + """ + Method invoked when message interface is instantiated and connection is established. + May be used for sending an introduction message or displaying general bot information. + + :param pipeline_runner: A function that should process user request and return context; + usually it's a :py:meth:`~dff.pipeline.pipeline.pipeline.Pipeline._run_pipeline` function. + """ + raise NotImplementedError + + +class MessengerInterfaceWithAttachments(MessengerInterface, abc.ABC): + """ + MessengerInterface subclass that has methods for attachment handling. :param attachments_directory: Directory where attachments will be stored. If not specified, the temporary directory will be used. @@ -37,7 +54,7 @@ class MessengerInterface(abc.ABC): Attachments not in this list will be neglected. """ - supported_response_attachment_types: set[type[Attachment]] = set() + supported_response_attachment_types: set[Type[Attachment]] = set() """ Types of attachment that this messenger interface can send. Attachments not in this list will be neglected. @@ -59,28 +76,16 @@ def __init__(self, attachments_directory: Optional[Path] = None) -> None: self.attachments_directory.mkdir(parents=True, exist_ok=True) @abc.abstractmethod - async def connect(self, pipeline_runner: PipelineRunnerFunction): - """ - Method invoked when message interface is instantiated and connection is established. - May be used for sending an introduction message or displaying general bot information. - - :param pipeline_runner: A function that should process user request and return context; - usually it's a :py:meth:`~dff.pipeline.pipeline.pipeline.Pipeline._run_pipeline` function. - """ - raise NotImplementedError - async def populate_attachment(self, attachment: DataAttachment) -> bytes: """ Method that can be used by some messenger interfaces for attachment population. - E.g. if a file attachment consists of an URL of the file uploaded to the messenger servers, + E.g. if a file attachment consists of a URL of the file uploaded to the messenger servers, this method is the right place to call the messenger API for the file downloading. - Since many messenger interfaces don't have built-in attachment population functionality, - this method is not abstract and thus should not always be overridden. :param attachment: Attachment that should be populated. :return: The attachment bytes. """ - raise RuntimeError(f"Messanger interface {type(self).__name__} can't populate attachment {attachment}!") + raise NotImplementedError class PollingMessengerInterface(MessengerInterface): @@ -163,8 +168,7 @@ class CallbackMessengerInterface(MessengerInterface): Callback message interface is waiting for user input and answers once it gets one. """ - def __init__(self, attachments_directory: Optional[Path] = None) -> None: - super().__init__(attachments_directory) + def __init__(self) -> None: self._pipeline_runner: Optional[PipelineRunnerFunction] = None async def connect(self, pipeline_runner: PipelineRunnerFunction): diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index cba49e65b..67f10a67f 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -10,7 +10,7 @@ from dff.utils.messengers.verify_params import generate_extra_fields -from dff.messengers.common import MessengerInterface +from dff.messengers.common import MessengerInterfaceWithAttachments from dff.pipeline.types import PipelineRunnerFunction from dff.script.core.message import ( Animation, @@ -69,7 +69,7 @@ def _is_attachment_mediagroup_combinable(attachment: Attachment): ) -class _AbstractTelegramInterface(MessengerInterface): +class _AbstractTelegramInterface(MessengerInterfaceWithAttachments): """ Messenger interface mixin for Telegram API usage. """ diff --git a/dff/script/core/message.py b/dff/script/core/message.py index 2a58fedf9..3a982188e 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -14,7 +14,7 @@ from pydantic import BaseModel, Field, JsonValue, field_validator, FilePath, HttpUrl, model_serializer, model_validator from pydantic_core import Url -from dff.messengers.common.interface import MessengerInterface +from dff.messengers.common.interface import MessengerInterfaceWithAttachments from dff.utils.pydantic import JSONSerializableDict, SerializableVaue, json_pickle_serializer, json_pickle_validator @@ -196,12 +196,12 @@ async def _cache_attachment(self, data: bytes, directory: Path) -> None: with open(self.cached_filename, "wb") as file: file.write(data) - async def get_bytes(self, from_interface: MessengerInterface) -> Optional[bytes]: + async def get_bytes(self, from_interface: MessengerInterfaceWithAttachments) -> Optional[bytes]: """ Download attachment bytes. If the attachment is represented by URL or saved in a file, it will be downloaded or read automatically. - Otherwise, a :py:meth:`~dff.messengers.common.MessengerInterface.populate_attachment` + Otherwise, a :py:meth:`~dff.messengers.common.MessengerInterfaceWithAttachments.populate_attachment` will be used for receiving attachment bytes by ID and title. :param from_interface: messenger interface the attachment was received from. From d8ad2f18fbf7ceeae7e229ab060ca943b425c831 Mon Sep 17 00:00:00 2001 From: Roman Zlobin Date: Mon, 24 Jun 2024 18:34:51 +0300 Subject: [PATCH 117/140] revert init call changes --- dff/messengers/telegram/interface.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dff/messengers/telegram/interface.py b/dff/messengers/telegram/interface.py index 8e52fba81..2f3208233 100644 --- a/dff/messengers/telegram/interface.py +++ b/dff/messengers/telegram/interface.py @@ -24,7 +24,7 @@ class LongpollingInterface(_AbstractTelegramInterface): def __init__( self, token: str, attachments_directory: Optional[Path] = None, interval: int = 2, timeout: int = 20 ) -> None: - _AbstractTelegramInterface.__init__(self, token, attachments_directory) + super().__init__(token, attachments_directory) self.interval = interval self.timeout = timeout @@ -49,7 +49,7 @@ class WebhookInterface(_AbstractTelegramInterface): def __init__( self, token: str, attachments_directory: Optional[Path] = None, host: str = "localhost", port: int = 844 ): - _AbstractTelegramInterface.__init__(self, token, attachments_directory) + super().__init__(token, attachments_directory) self.listen = host self.port = port From 386f028ed56f019d9f6c6b6e563ec59693e432db Mon Sep 17 00:00:00 2001 From: Roman Zlobin Date: Mon, 24 Jun 2024 23:33:59 +0300 Subject: [PATCH 118/140] return venv ignores --- .dockerignore | 2 ++ .gitignore | 1 + 2 files changed, 3 insertions(+) diff --git a/.dockerignore b/.dockerignore index 7f6151613..20720fea6 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,7 @@ *.DS_Store* *.egg-info/ dist/ +venv/ build/ docs/source/apiref docs/source/_misc @@ -21,6 +22,7 @@ GlobalUserTableAccessor* memory_debugging* opening_database* _globals.py +venv* .vscode .coverage .coverage.* diff --git a/.gitignore b/.gitignore index a6828357b..20720fea6 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ GlobalUserTableAccessor* memory_debugging* opening_database* _globals.py +venv* .vscode .coverage .coverage.* From 1bd7fffe347e50e39b79e745475ee24cc89c5b1a Mon Sep 17 00:00:00 2001 From: Roman Zlobin Date: Mon, 24 Jun 2024 23:47:44 +0300 Subject: [PATCH 119/140] fix typo --- dff/script/core/message.py | 4 ++-- dff/utils/pydantic/__init__.py | 2 +- dff/utils/pydantic/serializing.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dff/script/core/message.py b/dff/script/core/message.py index 3a982188e..f3a2a2052 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -15,7 +15,7 @@ from pydantic_core import Url from dff.messengers.common.interface import MessengerInterfaceWithAttachments -from dff.utils.pydantic import JSONSerializableDict, SerializableVaue, json_pickle_serializer, json_pickle_validator +from dff.utils.pydantic import JSONSerializableDict, SerializableValue, json_pickle_serializer, json_pickle_validator class DataModel(BaseModel, extra="allow", arbitrary_types_allowed=True): @@ -332,7 +332,7 @@ class level variables to store message information. ] = None annotations: Optional[JSONSerializableDict] = None misc: Optional[JSONSerializableDict] = None - original_message: Optional[SerializableVaue] = None + original_message: Optional[SerializableValue] = None def __init__( self, diff --git a/dff/utils/pydantic/__init__.py b/dff/utils/pydantic/__init__.py index 63d5917b2..b5e29ea7b 100644 --- a/dff/utils/pydantic/__init__.py +++ b/dff/utils/pydantic/__init__.py @@ -1,6 +1,6 @@ from .serializing import ( JSONSerializableDict, - SerializableVaue, + SerializableValue, json_pickle_serializer, json_pickle_validator, pickle_serializer, diff --git a/dff/utils/pydantic/serializing.py b/dff/utils/pydantic/serializing.py index 5e21a49a0..21580c4f8 100644 --- a/dff/utils/pydantic/serializing.py +++ b/dff/utils/pydantic/serializing.py @@ -100,7 +100,7 @@ def json_pickle_validator(model: Serializable) -> Serializable: PickleSerializer = PlainSerializer(pickle_serializer, when_used="json") PickleValidator = PlainValidator(pickle_validator) -SerializableVaue = Annotated[Any, PickleSerializer, PickleValidator] +SerializableValue = Annotated[Any, PickleSerializer, PickleValidator] """Annotation for field that makes it JSON serializable""" JSONPickleSerializer = WrapSerializer(json_pickle_serializer, when_used="json") From 39ca58c40df24d0319d49840a6c451f09d02a383 Mon Sep 17 00:00:00 2001 From: Roman Zlobin Date: Tue, 25 Jun 2024 01:46:56 +0300 Subject: [PATCH 120/140] doc improvements --- dff/messengers/telegram/abstract.py | 8 +++---- dff/messengers/telegram/interface.py | 6 +++++ dff/script/core/message.py | 26 ++++++++++++++++----- tutorials/messengers/telegram/1_basic.py | 20 ++++++++++------ tutorials/messengers/telegram/3_advanced.py | 2 +- 5 files changed, 44 insertions(+), 18 deletions(-) diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index 67f10a67f..13567d5ed 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -1,8 +1,8 @@ """ -Interface ------------- -This module implements various interfaces for :py:class:`~dff.messengers.telegram.messenger.TelegramMessenger` -that can be used to interact with the Telegram API. +Telegram Base +------------- +This module implements a base interface for interactions with the +Telegram API. """ from pathlib import Path diff --git a/dff/messengers/telegram/interface.py b/dff/messengers/telegram/interface.py index 2f3208233..226e1aa41 100644 --- a/dff/messengers/telegram/interface.py +++ b/dff/messengers/telegram/interface.py @@ -1,3 +1,9 @@ +""" +Telegram Interfaces +------------------- +This module provides concrete implementations of the +:py:class:`~._AbstractTelegramInterface`. +""" from pathlib import Path from typing import Any, Optional diff --git a/dff/script/core/message.py b/dff/script/core/message.py index f3a2a2052..254ad3388 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -178,17 +178,28 @@ class DataAttachment(Attachment): """ source: Optional[Union[HttpUrl, FilePath]] = None - cached_filename: Optional[FilePath] = None - id: Optional[str] = None + """Attachment source -- either a URL to a file or a local filepath.""" title: Optional[str] = None + """Text accompanying the data.""" use_cache: bool = True + """ + Whether to cache the file (only for URL and ID files). + Disable this if you want to always respond with the most up-to-date version of the file. + """ + cached_filename: Optional[FilePath] = None + """This field stores a path to cached version of this file (retrieved from id or URL).""" + id: Optional[str] = None + """ + ID of the file on a file server. + :py:meth:`~.MessengerInterfaceWithAttachments.populate_attachment` is used to retrieve bytes from ID. + """ async def _cache_attachment(self, data: bytes, directory: Path) -> None: """ Cache attachment, save bytes into a file. :param data: attachment data bytes. - :param directory: cache file where attachment will be saved. + :param directory: cache directory where attachment will be saved. """ title = str(uuid4()) if self.title is None else self.title @@ -198,12 +209,15 @@ async def _cache_attachment(self, data: bytes, directory: Path) -> None: async def get_bytes(self, from_interface: MessengerInterfaceWithAttachments) -> Optional[bytes]: """ - Download attachment bytes. + Retrieve attachment bytes. If the attachment is represented by URL or saved in a file, it will be downloaded or read automatically. - Otherwise, a :py:meth:`~dff.messengers.common.MessengerInterfaceWithAttachments.populate_attachment` + If cache use is allowed and the attachment is cached, cached file will be used. + Otherwise, a :py:meth:`~.MessengerInterfaceWithAttachments.populate_attachment` will be used for receiving attachment bytes by ID and title. + If cache use is allowed and the attachment is a URL or an ID, bytes will be cached locally. + :param from_interface: messenger interface the attachment was received from. """ @@ -305,7 +319,7 @@ class level variables to store message information. It includes message text, list of commands included in the message, list of attachments, annotations, MISC dictionary (that consists of - user-defined parameters) and original message field that somehow represent + user-defined parameters) and original message field that represents the update received from messenger interface API. """ diff --git a/tutorials/messengers/telegram/1_basic.py b/tutorials/messengers/telegram/1_basic.py index 3342fdfc5..c6a7a41ff 100644 --- a/tutorials/messengers/telegram/1_basic.py +++ b/tutorials/messengers/telegram/1_basic.py @@ -27,17 +27,23 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) # %% [markdown] """ -In order to integrate your script with Telegram, you need an instance of -`_AbstractTelegramMessenger` class and one of the following interfaces: -`LongpollingInterface` or `WebhookInterface`. +In order to integrate your script with Telegram, you need an instance of the +%mddoclink(api,messengers.telegram.abstract,_AbstractTelegramInterface) class. +Two of its child subclasses that can be instantiated +are %mddoclink(api,messengers.telegram.interface,LongpollingInterface) and +%mddoclink(api,messengers.telegram.interface,WebhookInterface). The latter requires a webserver, so here we use long polling interface. -`_AbstractTelegramMessenger` encapsulates the bot logic. The only required +%mddoclink(api,messengers.telegram.abstract,_AbstractTelegramInterface) +encapsulates the bot logic. The only required argument for a bot to run is a token. Some other parameters -(such as host, port, interval, etc.) can be passed as keyword arguments. +(such as host, port, interval, etc.) can be passed as keyword arguments +(for their specs see [documentations of the child subclasses]( +%doclink(api,messengers.telegram.interface) +). -The two interfaces connect the bot to Telegram. They can be passed directly -to the DFF `Pipeline` instance. +Either of the two interfaces connect the bot to Telegram. +They can be passed directly to the DFF `Pipeline` instance. """ diff --git a/tutorials/messengers/telegram/3_advanced.py b/tutorials/messengers/telegram/3_advanced.py index 37aba9f35..b7f8aaf3a 100644 --- a/tutorials/messengers/telegram/3_advanced.py +++ b/tutorials/messengers/telegram/3_advanced.py @@ -56,7 +56,7 @@ class for information about different arguments for sending attachments, `send_...` methods. Last option ("Raw attachments!") button might be especially interesting, -because it shows how bot precepts different telegram attachments sent by user +because it shows how bot percepts different telegram attachments sent by user in terms and datastructures of Dialog Flow Framework. """ From 44156065971cf4d1169f076a302993717a13690d Mon Sep 17 00:00:00 2001 From: Roman Zlobin Date: Tue, 25 Jun 2024 13:46:37 +0300 Subject: [PATCH 121/140] json-serialization improvements - add a class with serializable extras to be subclassed from - add docs - make wrappermodel private --- dff/script/core/message.py | 60 +++-------------------- dff/utils/pydantic/__init__.py | 5 +- dff/utils/pydantic/serializing.py | 80 ++++++++++++++++++++++++++++--- 3 files changed, 81 insertions(+), 64 deletions(-) diff --git a/dff/script/core/message.py b/dff/script/core/message.py index 254ad3388..d32d68be2 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -5,59 +5,25 @@ DFF. It only contains types and properties that are compatible with most messaging services. """ -from typing import Any, Callable, Dict, Literal, Optional, List, Union +from typing import Literal, Optional, List, Union from enum import Enum, auto from pathlib import Path from urllib.request import urlopen from uuid import uuid4 -from pydantic import BaseModel, Field, JsonValue, field_validator, FilePath, HttpUrl, model_serializer, model_validator +from pydantic import Field, field_validator, FilePath, HttpUrl, model_validator from pydantic_core import Url from dff.messengers.common.interface import MessengerInterfaceWithAttachments -from dff.utils.pydantic import JSONSerializableDict, SerializableValue, json_pickle_serializer, json_pickle_validator +from dff.utils.pydantic import JSONSerializableDict, SerializableValue, JSONSerializableExtras -class DataModel(BaseModel, extra="allow", arbitrary_types_allowed=True): +class DataModel(JSONSerializableExtras): """ This class is a Pydantic BaseModel that can have any type and number of extras. """ - def __init__(self, **kwargs): - super().__init__(**kwargs) - - -# TODO: inline once annotated __pydantic_extra__ will be available in pydantic -def _json_extra_serializer(model: DataModel, original_serializer: Callable[[DataModel], JsonValue]) -> JsonValue: - """ - Serialize model along with the `extras` field: i.e. all the fields not listed in the model. - This function should be used as "wrap" serializer. - - :param model: Pydantic model for serialization. - :param original_serializer: Function originally used for serialization by Pydantic. - :return: Serialized model. - """ - - model_copy = model.model_copy(deep=True) - for extra_name in model.model_extra.keys(): - delattr(model_copy, extra_name) - model_dict = original_serializer(model_copy) - model_dict.update(json_pickle_serializer(model.model_extra, original_serializer)) - return model_dict - - -# TODO: inline once annotated __pydantic_extra__ will be available in pydantic -def _json_extra_validator(model: DataModel) -> DataModel: - """ - Validate model along with the `extras` field: i.e. all the fields not listed in the model. - This function should be used as "after" validator. - - :param model: Pydantic model for validation. - :return: Validated model. - """ - - model.__pydantic_extra__ = json_pickle_validator(model.__pydantic_extra__) - return model + pass class Session(Enum): @@ -84,13 +50,7 @@ class Attachment(DataModel): It is capable of serializing and validating all the model fields to JSON. """ - @model_validator(mode="after") - def extra_validator(self) -> "Attachment": - return _json_extra_validator(self) - - @model_serializer(mode="wrap", when_used="json") - def extra_serializer(self, original_serializer: Callable[["Attachment"], Dict[str, Any]]) -> Dict[str, Any]: - return _json_extra_serializer(self, original_serializer) + pass class CallbackQuery(Attachment): @@ -391,11 +351,3 @@ def __eq__(self, other): def __repr__(self) -> str: return " ".join([f"{key}='{value}'" for key, value in self.model_dump(exclude_none=True).items()]) - - @model_validator(mode="after") - def extra_validator(self) -> "Message": - return _json_extra_validator(self) - - @model_serializer(mode="wrap", when_used="json") - def extra_serializer(self, original_serializer: Callable[["Message"], Dict[str, Any]]) -> Dict[str, Any]: - return _json_extra_serializer(self, original_serializer) diff --git a/dff/utils/pydantic/__init__.py b/dff/utils/pydantic/__init__.py index b5e29ea7b..5c873cc42 100644 --- a/dff/utils/pydantic/__init__.py +++ b/dff/utils/pydantic/__init__.py @@ -1,8 +1,5 @@ from .serializing import ( JSONSerializableDict, SerializableValue, - json_pickle_serializer, - json_pickle_validator, - pickle_serializer, - pickle_validator, + JSONSerializableExtras, ) diff --git a/dff/utils/pydantic/serializing.py b/dff/utils/pydantic/serializing.py index 21580c4f8..201196253 100644 --- a/dff/utils/pydantic/serializing.py +++ b/dff/utils/pydantic/serializing.py @@ -1,17 +1,45 @@ +""" +Serialization +------------- +Tools that provide JSON serialization via Pickle for unserializable objects. + +- :py:const:`~.SerializableValue`: + A field annotated with this will be pickled/unpickled during JSON-serialization/validation. +- :py:const:`~.JSONSerializableDict`: + A dictionary field annotated with this will make all its items smart-serializable: + If an item is serializable -- nothing would change. + Otherwise -- it will be serialized via pickle. +- :py:class:`~.JSONSerializableExtras`: + A pydantic base class that makes its extra fields a `JSONSerializableDict`. +""" + from base64 import decodebytes, encodebytes from copy import deepcopy from pickle import dumps, loads from typing import Any, Callable, Dict, List, Union from typing_extensions import Annotated, TypeAlias -from pydantic import AfterValidator, JsonValue, PlainSerializer, PlainValidator, RootModel, WrapSerializer +from pydantic import ( + AfterValidator, + JsonValue, + PlainSerializer, + PlainValidator, + RootModel, + WrapSerializer, + BaseModel, + model_validator, + model_serializer, +) from pydantic_core import PydanticSerializationError _JSON_EXTRA_FIELDS_KEYS = "__pickled_extra_fields__" +""" +This key is used in :py:const:`~.JSONSerializableDict` to remember pickled items. +""" Serializable: TypeAlias = Dict[str, Union[JsonValue, Any, List[Any], Dict[str, Any]]] -class WrapperModel(RootModel): +class _WrapperModel(RootModel): """ Wrapper model for testing whether an object is serializable to JSON. """ @@ -21,7 +49,7 @@ class WrapperModel(RootModel): def pickle_serializer(value: Any) -> JsonValue: """ - Serializer funtion that serializes any pickle-serializable value into JSON-serializable. + Serializer function that serializes any pickle-serializable value into JSON-serializable. Serializes value with pickle and encodes bytes as base64 string. :param value: Pickle-serializable object. @@ -33,7 +61,7 @@ def pickle_serializer(value: Any) -> JsonValue: def pickle_validator(value: JsonValue) -> Any: """ - Validator funtion that validates base64 string encoded bytes as a pickle-serializable value. + Validator function that validates base64 string encoded bytes as a pickle-serializable value. Decodes base64 string and validates value with pickle. :param value: String-encoded string. @@ -65,7 +93,7 @@ def json_pickle_serializer( if isinstance(field_value, bytes): raise PydanticSerializationError("") else: - WrapperModel(root=field_value).model_dump_json() + _WrapperModel(root=field_value).model_dump_json() except PydanticSerializationError: model_copy[field_name] = pickle_serializer(field_value) extra_fields += [field_name] @@ -106,4 +134,44 @@ def json_pickle_validator(model: Serializable) -> Serializable: JSONPickleSerializer = WrapSerializer(json_pickle_serializer, when_used="json") JSONPickleValidator = AfterValidator(json_pickle_validator) JSONSerializableDict = Annotated[Serializable, JSONPickleSerializer, JSONPickleValidator] -"""Annotation for dictionary or Pydantic model that makes all its fields JSON serializable""" +""" +Annotation for dictionary or Pydantic model that makes all its fields JSON serializable. + +This uses a reserved dictionary key :py:const:`~._JSON_EXTRA_FIELDS_KEYS` to store +fields serialized that way. +""" + + +class JSONSerializableExtras(BaseModel, extra="allow"): + """ + This model makes extra fields pickle-serializable. + Do not use :py:const:`~._JSON_EXTRA_FIELDS_KEYS` as an extra field name. + """ + + def __init__(self, **kwargs): # supress unknown arg warnings + super().__init__(**kwargs) + + @model_validator(mode="after") + def extra_validator(self): + """ + Validate model along with the `extras` field: i.e. all the fields not listed in the model. + + :return: Validated model. + """ + self.__pydantic_extra__ = json_pickle_validator(self.__pydantic_extra__) + return self + + @model_serializer(mode="wrap", when_used="json") + def extra_serializer(self, original_serializer) -> Dict[str, Any]: + """ + Serialize model along with the `extras` field: i.e. all the fields not listed in the model. + + :param original_serializer: Function originally used for serialization by Pydantic. + :return: Serialized model. + """ + model_copy = self.model_copy(deep=True) + for extra_name in self.model_extra.keys(): + delattr(model_copy, extra_name) + model_dict = original_serializer(model_copy) + model_dict.update(json_pickle_serializer(self.model_extra, original_serializer)) + return model_dict From fc98008c148ecfe506abe46f4ce7d8dbaff1db3d Mon Sep 17 00:00:00 2001 From: Roman Zlobin Date: Tue, 25 Jun 2024 14:31:01 +0300 Subject: [PATCH 122/140] patch TG_BOT_TOKEN for tutorial tests --- tests/messengers/telegram/test_tutorials.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/messengers/telegram/test_tutorials.py b/tests/messengers/telegram/test_tutorials.py index 9d90a139d..07b893dea 100644 --- a/tests/messengers/telegram/test_tutorials.py +++ b/tests/messengers/telegram/test_tutorials.py @@ -21,7 +21,8 @@ "tutorial_module_name", ["1_basic", "2_attachments", "3_advanced"], ) -def test_tutorials_memory(tutorial_module_name: str): +def test_tutorials_memory(tutorial_module_name: str, monkeypatch): + monkeypatch.setenv("TG_BOT_TOKEN", "token") happy_path_data = loads(happy_paths_file.read_text())[tutorial_module_name] happy_path_steps = cast_dict_to_happy_step(happy_path_data) module = import_module(f"tutorials.{dot_path_to_addon}.{tutorial_module_name}") From 4f49b99419fe5d58e77b8a07e2892f34e3ed36ce Mon Sep 17 00:00:00 2001 From: Roman Zlobin Date: Tue, 25 Jun 2024 14:31:40 +0300 Subject: [PATCH 123/140] remove pytest.mark.telegram --- pyproject.toml | 1 - scripts/test.py | 8 +++----- tests/messengers/telegram/test_tutorials.py | 1 - 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 3601bb1b6..7744f0f11 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -207,7 +207,6 @@ profile = "black" addopts = "--strict-markers" markers = [ "docker: marks tests as requiring docker containers to work", - "telegram: marks tests as requiring telegram client API token to work", "slow: marks tests as slow (taking more than a minute to complete)", "no_coverage: tests that either cannot run inside the `coverage` workflow or do not affect coverage stats", "all: reserved by allow-skip", diff --git a/scripts/test.py b/scripts/test.py index 1af932f29..4a040099d 100644 --- a/scripts/test.py +++ b/scripts/test.py @@ -12,10 +12,9 @@ def _test(coverage: bool = False, dependencies: bool = False, quick: bool = Fals Run framework tests, located in `tests/` dir, using env defined in `.env_file`. Please keep in mind that: - 1. Skipping `telegram` tests is **always** allowed. - 2. Enabling dependencies is effectively same as enabling docker + 1. Enabling dependencies is effectively same as enabling docker (docker containers **should** be running in that case). - 3. Coverage requires all dependencies and docker (will have no effect otherwise). + 2. Coverage requires all dependencies and docker (will have no effect otherwise). :param coverage: Enable coverage calculation :param dependencies: Disallow skipping tests @@ -39,12 +38,11 @@ def _test(coverage: bool = False, dependencies: bool = False, quick: bool = Fals args = [ "-m", "not no_coverage", - "--allow-skip=telegram", *args, ] elif dependencies: args = [ - "--allow-skip=telegram,docker", + "--allow-skip=docker", *args, ] else: diff --git a/tests/messengers/telegram/test_tutorials.py b/tests/messengers/telegram/test_tutorials.py index 07b893dea..1d53a16e4 100644 --- a/tests/messengers/telegram/test_tutorials.py +++ b/tests/messengers/telegram/test_tutorials.py @@ -15,7 +15,6 @@ @pytest.mark.skipif(not telegram_available, reason="Telegram dependencies missing") -@pytest.mark.telegram @pytest.mark.asyncio @pytest.mark.parametrize( "tutorial_module_name", From 1765a33989c1594ec60622fc1c6f8afd159ac8bc Mon Sep 17 00:00:00 2001 From: Roman Zlobin Date: Tue, 25 Jun 2024 14:33:59 +0300 Subject: [PATCH 124/140] lint --- dff/messengers/common/__init__.py | 2 +- dff/messengers/telegram/interface.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/dff/messengers/common/__init__.py b/dff/messengers/common/__init__.py index 1dd46fa8f..713974c16 100644 --- a/dff/messengers/common/__init__.py +++ b/dff/messengers/common/__init__.py @@ -4,6 +4,6 @@ MessengerInterface, MessengerInterfaceWithAttachments, PollingMessengerInterface, - CallbackMessengerInterface + CallbackMessengerInterface, ) from .types import PollingInterfaceLoopFunction diff --git a/dff/messengers/telegram/interface.py b/dff/messengers/telegram/interface.py index 226e1aa41..bcfabb0c1 100644 --- a/dff/messengers/telegram/interface.py +++ b/dff/messengers/telegram/interface.py @@ -4,6 +4,7 @@ This module provides concrete implementations of the :py:class:`~._AbstractTelegramInterface`. """ + from pathlib import Path from typing import Any, Optional From 85e84a486e407ee318c4843e4936dc45a07de45a Mon Sep 17 00:00:00 2001 From: Roman Zlobin Date: Tue, 25 Jun 2024 14:50:31 +0300 Subject: [PATCH 125/140] standardize main block in tutorials `if __name__ == "__main__":` is a standard structure in python and should be left unchanged --- tutorials/messengers/telegram/1_basic.py | 7 ++++--- tutorials/messengers/telegram/2_attachments.py | 7 ++++--- tutorials/messengers/telegram/3_advanced.py | 7 ++++--- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/tutorials/messengers/telegram/1_basic.py b/tutorials/messengers/telegram/1_basic.py index c6a7a41ff..11d20b1fd 100644 --- a/tutorials/messengers/telegram/1_basic.py +++ b/tutorials/messengers/telegram/1_basic.py @@ -79,6 +79,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) ) -if __name__ == "__main__" and is_interactive_mode(): - # prevent run during doc building - pipeline.run() +if __name__ == "__main__": + if is_interactive_mode(): + # prevent run during doc building + pipeline.run() diff --git a/tutorials/messengers/telegram/2_attachments.py b/tutorials/messengers/telegram/2_attachments.py index 95e75e2f9..b6ef0911c 100644 --- a/tutorials/messengers/telegram/2_attachments.py +++ b/tutorials/messengers/telegram/2_attachments.py @@ -252,6 +252,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) ) -if __name__ == "__main__" and is_interactive_mode(): - # prevent run during doc building - pipeline.run() +if __name__ == "__main__": + if is_interactive_mode(): + # prevent run during doc building + pipeline.run() diff --git a/tutorials/messengers/telegram/3_advanced.py b/tutorials/messengers/telegram/3_advanced.py index b7f8aaf3a..cdc6a0d40 100644 --- a/tutorials/messengers/telegram/3_advanced.py +++ b/tutorials/messengers/telegram/3_advanced.py @@ -253,6 +253,7 @@ async def hash_data_attachment_request(ctx: Context, pipe: Pipeline) -> Message: ) -if __name__ == "__main__" and is_interactive_mode(): - # prevent run during doc building - pipeline.run() +if __name__ == "__main__": + if is_interactive_mode(): + # prevent run during doc building + pipeline.run() From 2412ba82e2aab6923e952b7dfe67d4d068880e91 Mon Sep 17 00:00:00 2001 From: Roman Zlobin Date: Tue, 25 Jun 2024 15:48:51 +0300 Subject: [PATCH 126/140] merge utils as development utils --- dff/messengers/telegram/abstract.py | 38 +++++++++---------- dff/script/core/message.py | 2 +- dff/utils/devel/__init__.py | 13 +++++++ .../extra_field_helpers.py} | 8 +++- .../json_serialization.py} | 10 ++--- dff/utils/messengers/__init__.py | 1 - dff/utils/pydantic/__init__.py | 5 --- docs/source/conf.py | 1 + 8 files changed, 46 insertions(+), 32 deletions(-) create mode 100644 dff/utils/devel/__init__.py rename dff/utils/{messengers/verify_params.py => devel/extra_field_helpers.py} (77%) rename dff/utils/{pydantic/serializing.py => devel/json_serialization.py} (94%) delete mode 100644 dff/utils/messengers/__init__.py delete mode 100644 dff/utils/pydantic/__init__.py diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index 13567d5ed..b47bc8549 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -8,7 +8,7 @@ from pathlib import Path from typing import Any, Callable, Optional -from dff.utils.messengers.verify_params import generate_extra_fields +from dff.utils.devel.extra_field_helpers import grab_extra_fields from dff.messengers.common import MessengerInterfaceWithAttachments from dff.pipeline.types import PipelineRunnerFunction @@ -303,7 +303,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes chat_id, attachment.latitude, attachment.longitude, - **generate_extra_fields( + **grab_extra_fields( attachment, [ "horizontal_accuracy", @@ -320,7 +320,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes attachment.phone_number, attachment.first_name, attachment.last_name, - **generate_extra_fields( + **grab_extra_fields( attachment, ["vcard", "disable_notification", "protect_content", "reply_markup", "message_effect_id"], ), @@ -330,7 +330,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes chat_id, attachment.question, [option.text for option in attachment.options], - **generate_extra_fields( + **grab_extra_fields( attachment, [ "is_anonymous", @@ -353,7 +353,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes await bot.send_sticker( chat_id, sticker, - **generate_extra_fields( + **grab_extra_fields( attachment, ["disable_notification", "protect_content", "reply_markup", "emoji", "message_effect_id"], ), @@ -365,7 +365,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes files += [ InputMediaAudio( attachment_bytes, - **generate_extra_fields( + **grab_extra_fields( attachment, ["filename", "caption", "parse_mode", "performer", "title", "thumbnail"], ), @@ -376,7 +376,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes chat_id, attachment_bytes, caption=message.text, - **generate_extra_fields( + **grab_extra_fields( attachment, [ "performer", @@ -397,7 +397,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes files += [ InputMediaVideo( attachment_bytes, - **generate_extra_fields( + **grab_extra_fields( attachment, [ "filename", @@ -415,7 +415,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes chat_id, attachment_bytes, caption=message.text, - **generate_extra_fields( + **grab_extra_fields( attachment, [ "disable_notification", @@ -437,7 +437,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes files += [ InputMediaAnimation( attachment_bytes, - **generate_extra_fields( + **grab_extra_fields( attachment, ["filename", "caption", "parse_mode", "has_spoiler", "thumbnail"] ), ), @@ -447,7 +447,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes chat_id, attachment_bytes, caption=message.text, - **generate_extra_fields( + **grab_extra_fields( attachment, [ "parse_mode", @@ -468,7 +468,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes files += [ InputMediaPhoto( attachment_bytes, - **generate_extra_fields( + **grab_extra_fields( attachment, ["filename", "caption", "parse_mode", "has_spoiler"] ), ), @@ -478,7 +478,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes chat_id, attachment_bytes, caption=message.text, - **generate_extra_fields( + **grab_extra_fields( attachment, [ "disable_notification", @@ -498,7 +498,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes files += [ InputMediaDocument( attachment_bytes, - **generate_extra_fields( + **grab_extra_fields( attachment, [ "filename", @@ -515,7 +515,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes chat_id, attachment_bytes, caption=message.text, - **generate_extra_fields( + **grab_extra_fields( attachment, [ "disable_notification", @@ -535,7 +535,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes chat_id, attachment_bytes, caption=message.text, - **generate_extra_fields( + **grab_extra_fields( attachment, [ "disable_notification", @@ -555,7 +555,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes chat_id, attachment_bytes, caption=message.text, - **generate_extra_fields( + **grab_extra_fields( attachment, [ "disable_notification", @@ -574,14 +574,14 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes chat_id, files, caption=message.text, - **generate_extra_fields(message, ["disable_notification", "protect_content", "message_effect_id"]), + **grab_extra_fields(message, ["disable_notification", "protect_content", "message_effect_id"]), ) message_text_covered = True if message.text is not None and not message_text_covered: await bot.send_message( chat_id, message.text, - **generate_extra_fields( + **grab_extra_fields( message, ["parse_mode", "disable_notification", "protect_content", "reply_markup", "message_effect_id"], ), diff --git a/dff/script/core/message.py b/dff/script/core/message.py index d32d68be2..c1973aede 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -15,7 +15,7 @@ from pydantic_core import Url from dff.messengers.common.interface import MessengerInterfaceWithAttachments -from dff.utils.pydantic import JSONSerializableDict, SerializableValue, JSONSerializableExtras +from dff.utils.devel import JSONSerializableDict, SerializableValue, JSONSerializableExtras class DataModel(JSONSerializableExtras): diff --git a/dff/utils/devel/__init__.py b/dff/utils/devel/__init__.py new file mode 100644 index 000000000..aee672838 --- /dev/null +++ b/dff/utils/devel/__init__.py @@ -0,0 +1,13 @@ +""" +Devel Utils +----------- +These utils contain useful classes/functions that are often used in various +parts of the framework. +""" + +from .json_serialization import ( + JSONSerializableDict, + SerializableValue, + JSONSerializableExtras, +) +from .extra_field_helpers import grab_extra_fields diff --git a/dff/utils/messengers/verify_params.py b/dff/utils/devel/extra_field_helpers.py similarity index 77% rename from dff/utils/messengers/verify_params.py rename to dff/utils/devel/extra_field_helpers.py index da5f121b1..13f457d27 100644 --- a/dff/utils/messengers/verify_params.py +++ b/dff/utils/devel/extra_field_helpers.py @@ -1,9 +1,15 @@ +""" +Extra field helpers +------------------- +Helpers for managing pydantic extra fields. +""" + from typing import List from pydantic import BaseModel -def generate_extra_fields(attachment: BaseModel, extra_fields: List[str]): +def grab_extra_fields(attachment: BaseModel, extra_fields: List[str]): """ Convenience method for passing attachment extras as named arguments to API functions. This might be useful for making sure no typos appear in code. diff --git a/dff/utils/pydantic/serializing.py b/dff/utils/devel/json_serialization.py similarity index 94% rename from dff/utils/pydantic/serializing.py rename to dff/utils/devel/json_serialization.py index 201196253..552d94823 100644 --- a/dff/utils/pydantic/serializing.py +++ b/dff/utils/devel/json_serialization.py @@ -3,9 +3,9 @@ ------------- Tools that provide JSON serialization via Pickle for unserializable objects. -- :py:const:`~.SerializableValue`: +- :py:data:`~.SerializableValue`: A field annotated with this will be pickled/unpickled during JSON-serialization/validation. -- :py:const:`~.JSONSerializableDict`: +- :py:data:`~.JSONSerializableDict`: A dictionary field annotated with this will make all its items smart-serializable: If an item is serializable -- nothing would change. Otherwise -- it will be serialized via pickle. @@ -33,7 +33,7 @@ _JSON_EXTRA_FIELDS_KEYS = "__pickled_extra_fields__" """ -This key is used in :py:const:`~.JSONSerializableDict` to remember pickled items. +This key is used in :py:data:`~.JSONSerializableDict` to remember pickled items. """ Serializable: TypeAlias = Dict[str, Union[JsonValue, Any, List[Any], Dict[str, Any]]] @@ -137,7 +137,7 @@ def json_pickle_validator(model: Serializable) -> Serializable: """ Annotation for dictionary or Pydantic model that makes all its fields JSON serializable. -This uses a reserved dictionary key :py:const:`~._JSON_EXTRA_FIELDS_KEYS` to store +This uses a reserved dictionary key :py:data:`~._JSON_EXTRA_FIELDS_KEYS` to store fields serialized that way. """ @@ -145,7 +145,7 @@ def json_pickle_validator(model: Serializable) -> Serializable: class JSONSerializableExtras(BaseModel, extra="allow"): """ This model makes extra fields pickle-serializable. - Do not use :py:const:`~._JSON_EXTRA_FIELDS_KEYS` as an extra field name. + Do not use :py:data:`~._JSON_EXTRA_FIELDS_KEYS` as an extra field name. """ def __init__(self, **kwargs): # supress unknown arg warnings diff --git a/dff/utils/messengers/__init__.py b/dff/utils/messengers/__init__.py deleted file mode 100644 index 09e683272..000000000 --- a/dff/utils/messengers/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .verify_params import generate_extra_fields diff --git a/dff/utils/pydantic/__init__.py b/dff/utils/pydantic/__init__.py deleted file mode 100644 index 5c873cc42..000000000 --- a/dff/utils/pydantic/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .serializing import ( - JSONSerializableDict, - SerializableValue, - JSONSerializableExtras, -) diff --git a/docs/source/conf.py b/docs/source/conf.py index 3ac5149e3..a4b65beb8 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -197,5 +197,6 @@ def setup(_): ("dff.utils.testing", "Testing Utils"), ("dff.utils.turn_caching", "Caching"), ("dff.utils.db_benchmark", "DB Benchmark"), + ("dff.utils.devel", "Development Utils"), ] ) From 16022425644006a28a43ebff4893ce471c4fea2e Mon Sep 17 00:00:00 2001 From: Roman Zlobin Date: Tue, 25 Jun 2024 19:10:40 +0300 Subject: [PATCH 127/140] fix test data --- .../messengers/telegram/test_happy_paths.json | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/messengers/telegram/test_happy_paths.json b/tests/messengers/telegram/test_happy_paths.json index 176deaf01..ecb04d199 100644 --- a/tests/messengers/telegram/test_happy_paths.json +++ b/tests/messengers/telegram/test_happy_paths.json @@ -133,11 +133,11 @@ "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" ] }, { - "update": "Update(callback_query=CallbackQuery(chat_instance='-1', data='formatted', from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), id='1', message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 9, 20, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Bot', id=16, is_bot=True, username='dff_bot'), group_chat_created=False, location=Location(latitude=58.43162, longitude=27.792879), message_id=2, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), supergroup_chat_created=False)), update_id=2)", + "update": "Update(callback_query=CallbackQuery(chat_instance='-1', data='formatted', from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), id='1', message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 9, 20, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Bot', id=16, is_bot=True, username='dff_bot'), group_chat_created=False, location=Location(latitude=58.43162, longitude=27.792879), message_id=2, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), supergroup_chat_created=False)), update_id=2)", "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"formatted\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":\"\\nHere's your formatted text\\\\!\\nYou can see **text in bold** and _text in italic_\\\\.\\n\\\\> Here's a [link](https:\/\/deeppavlov.ai\/) in a quote\\\\.\\nRun \/start command again to restart\\\\.\\n\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null,\"parse_mode\":\"MarkdownV2\",\"__pickled_extra_fields__\":[]}", "response_functions": [ @@ -149,11 +149,11 @@ "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" ] }, { - "update": "Update(callback_query=CallbackQuery(chat_instance='-1', data='attachments', from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), id='3', message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 9, 24, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Bot', id=16, is_bot=True, username='dff_bot'), group_chat_created=False, location=Location(latitude=58.43162, longitude=27.792879), message_id=4, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), supergroup_chat_created=False)), update_id=4)", + "update": "Update(callback_query=CallbackQuery(chat_instance='-1', data='attachments', from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), id='3', message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 9, 24, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Bot', id=16, is_bot=True, username='dff_bot'), group_chat_created=False, location=Location(latitude=58.43162, longitude=27.792879), message_id=4, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), supergroup_chat_created=False)), update_id=4)", "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"attachments\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":\"Here's your message with multiple attachments (a location and a sticker)!\\nRun \/start command again to restart.\",\"commands\":null,\"attachments\":[{\"longitude\":30.3141,\"latitude\":59.9386,\"dff_attachment_type\":\"location\",\"__pickled_extra_fields__\":[]},{\"source\":null,\"cached_filename\":null,\"id\":\"CAACAgIAAxkBAAErBZ1mKAbZvEOmhscojaIL5q0u8vgp1wACRygAAiSjCUtLa7RHZy76ezQE\",\"title\":null,\"use_cache\":true,\"dff_attachment_type\":\"sticker\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ @@ -167,11 +167,11 @@ "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" ] }, { - "update": "Update(callback_query=CallbackQuery(chat_instance='-1', data='secret', from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), id='5', message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 9, 28, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Bot', id=16, is_bot=True, username='dff_bot'), group_chat_created=False, location=Location(latitude=58.43162, longitude=27.792879), message_id=6, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), supergroup_chat_created=False)), update_id=6)", + "update": "Update(callback_query=CallbackQuery(chat_instance='-1', data='secret', from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), id='5', message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 9, 28, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Bot', id=16, is_bot=True, username='dff_bot'), group_chat_created=False, location=Location(latitude=58.43162, longitude=27.792879), message_id=6, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), supergroup_chat_created=False)), update_id=6)", "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"secret\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":\"Here's your secret image! Run \/start command again to restart.\",\"commands\":null,\"attachments\":[{\"source\":\"https:\/\/github.com\/deeppavlov\/dialog_flow_framework\/wiki\/example_attachments\/deeppavlov.png\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov logo\",\"use_cache\":true,\"dff_attachment_type\":\"image\",\"has_spoiler\":true,\"filename\":\"deeppavlov_logo.png\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ @@ -183,11 +183,11 @@ "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" ] }, { - "update": "Update(callback_query=CallbackQuery(chat_instance='-1', data='thumbnail', from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), id='7', message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 9, 34, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Bot', id=16, is_bot=True, username='dff_bot'), group_chat_created=False, location=Location(latitude=58.43162, longitude=27.792879), message_id=8, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), supergroup_chat_created=False)), update_id=8)", + "update": "Update(callback_query=CallbackQuery(chat_instance='-1', data='thumbnail', from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), id='7', message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 9, 34, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Bot', id=16, is_bot=True, username='dff_bot'), group_chat_created=False, location=Location(latitude=58.43162, longitude=27.792879), message_id=8, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), supergroup_chat_created=False)), update_id=8)", "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"thumbnail\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":\"Here's your document with tumbnail! Run \/start command again to restart.\",\"commands\":null,\"attachments\":[{\"source\":\"https:\/\/github.com\/deeppavlov\/dialog_flow_framework\/wiki\/example_attachments\/deeppavlov-article.pdf\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov article\",\"use_cache\":true,\"dff_attachment_type\":\"document\",\"filename\":\"deeppavlov_article.pdf\",\"thumbnail\":\"gASVySsAAAAAAABCwisAAIlQTkcNChoKAAAADUlIRFIAAADIAAAAyAgCAAAAIjo5yQAAK4lJREFU\\neJzsnXdcFFfXx+\/sbF\/KsnSQLiiIUlSwgo+KYi9RAbvYS4wleZ7EqNFojEaTqOlqLLFh77F3jaCA\\nCEgREJDeWbb394PwIiIus7NTFrPfj3\/g7My9h+U3t557DlWj0QAjRrCGQrYBRj5MjMIyggtGYRnB\\nBaOwjOCCUVhGcMEoLCO4YBSWEVwwCssILhiFZQQXqGQbgBIlf6+qYhHZVuAMbEOzP0NhdifbDjS0\\n1xZLVbmSbBPwR1WuEsSQbQRK2quwgEZCtgWEoBGTbQFK2q2wjBg2RmEZwQWjsIzgglFYRnDBKCw0\\n8GXsEhGPL2PrU4hcRX1a4Y6dUYZFe13H0s7DEu9tTycxYflgp8Ro76sQhOipx2Ve3yZEMWH5KNfY\\nyZ1ua7nzUn7Q7ucjulrm7gz5FbWRNwv9l91fkDl1DuoSDJkPU1ilYh6DIt8QvD\/65qdcunCoS8LG\\nJ5OFCtZnAcddzCrOvex9raB7oHXWbJ+rmxPq9ZfF77Cgy8VyiYVGAzb13jvk3OYulnnPq10flnSh\\nQqqvgw8czw515FQOd31yLCuExxBM9rrDl5mk1zg3VLcrddizKo\/edmmhDsm704Z9HXywWMj74\/mI\\n9cEHm1t1JPM\/D0u7MGH5V0EHM2s6XMzrNanjPZK+Idz5YLtCGqzqyC0Jc46\/Vej\/6YO5tqyavvap\\n824vyxdYr4md8XHXs\/vShqZVO5\/L7ZNe4+zFLVjxYD4AQAMgCqQGQEOnKDtxC77scVitATFZoQo1\\nvDF+MgDgh6cfmdFFzSs6md3vyIuBEzzurX88tUzC\/Tsv6G6R76X8IImS3sKkblYvV\/c4nFdnczwr\\n1MO8JNz5ybGsAcR+K8TxYbZYTYgVTAum8GZhQHKVuylNYsuqiSvtXCs3+eT+Ig5NSgFqAMB837\/N\\naKIfnk4AAMSWek++umplwClvXsHauGl3Cv3rFGwf3qto76urHkUnlHfkyznBdi+aV5FV6xhkm\/Gf\\nDsn97FOTq9wjve5cyO39qNRnR8gvDTeoNRAF0mg0YF\/6kCflncrEFiGOqTym0Ncyj6RvhQg+WGEV\\nCS1\/SxlxIifk0sjVpSIelyGY3OkOC5axqPX\/1vQ8DEEaJ9MKAEB6tVNiRcchzvEAgP4OqSeHbWgo\\n4VhW6Ibgv\/akhQMAmFTFoq7nh1345que9b3bS75dvsCmTMzN4dt15BZtSYi8VeB37VWPdUH1n\/Y5\\nucOGXdPdOhsAcDqnz+K7S4tnRQoVzN3Ph18Yufa\/D+sHVeVi82eV7jIV7XFZpyDbTLK\/LeyB161b\\nR7YNOpNUJLBWbtVyA5WigiDIhCZdH3zQw7w0xDGlQGjzoMS3g0mln3Wuv1XO+dzeVVKzYNuMvzLD\\nxEpmgHXOZ4EnmLDcgV3dyaKwoZBO3MLYMu+JHvd9eK\/sODU+vPwqqfmSbufYNHlcWSeJitnBpJIC\\naUa7xVox6+4U+23svd+HV8BliJmwfIRrnA+vAADAospMaeJ+DmkMWOlrmXe\/2HdOlyte3CKJkpFY\\n4dXLLqNAaNPbLv19v0iB2FvNGsamwzh8i\/gCta9zhQkFdRuu515IqxQvGIFJgb1Pbv8l9OfA162L\\nAbInLXz14+VL+nb4dICLOas9dS\/txtbcasmmG3kH4ksUKizfhEsjV5vSDXo\/u1ai3Hgjb09c8ReD\\nXJf2dyLbHKS0g1mhUqVefTnHc9M\/e+KKsVUVAIDHFNIoKmzLxINSgfyTsy\/cNj688aKabFsQYegt\\n1vnUioWnMorr5GQbYhDk1UjD\/ng6pBNvX4SPgzmDbHO0YbgtVoVQPn5f8ph9yUZVteBaZnWnzY9+\\neVBAtiHaMFBh7Xtc7L0l9kxqBdmGGChCuWrJmRcDfk14Xiok25bWMThhVYrkkw+mRh9LrxIrtNym\\n1iDb\/2vnKFTaFhru5tQG73iy61GRSk2gTcgwLGHdf1nTdWvc0aSyNu88n9uLEIvIRKGCrxcEar9H\\nJFfPP5kxdt+zaq3vIfEY0DrWqr+zt95+pVQjsodNlYY5JVix6vC3ixw0GpBY4ZlU2RHh\/Xam9PPR\\n3Xo6m+NsF1IMQlg1YsWMo2kX0irJNqR9w6RSNg33WB7qTLYhwCCElVkuGrsvOaO8vR5HMTSW9u+w\\nbZQnDSZ5kEOysO69rBn95zO+tB0sUbYj+rtzL0T7kbsFRKaw9sYVLziZoUA2qDKiE64WzDuLAl14\\nLLIMIEdYagDWXc7ZcONDdkgiHWcu88QM3yCShvMkCEupUn95Oee7268IrvdfiCWbejbar58bl\/iq\\nSRDWsrOZO+4XElzpvxZTBnw+2m9ARwuC6yVaWPNPpO+KLSayRiMsGuXvOf4Ea4vQSemS0xlGVRGP\\nRKEevifpQW4NkZUSJ6z\/Xcz65WERYdUZaY5EoR79Z3JCAXEbFQQJ69ubucbROrnUSJRDdydllImI\\nqY4IYR14Urzq75cEVGREO1UixcDfEssEMgLqwl1Yj1\/xF5z8AI83tVNKBPLwXUkiOe5bHfgKK7Nc\\nNHx3klRpeO5C\/2KSioXzTqTLcfbhwlFYApky8mBqlViJXxVG0HEksWzLLXy3PfASlkoN5p\/ISCo2\\nUMdZI19dyb2agaOfEl7C+vFe\/tGnbTuCGiELDQBRh57nVuF1phIXYT15Vbf6cg4eJRvBkBqJctJf\\nKTg5l2AvLIlCFXUoVaY0OsO0A+ILBeuv4tIEYC+sZWdf5ODWwBrBnK+v5z1+xce8WIyFdTWjyrgb\\n2O6IPJgqUWC8soWlsKpE8jnH3xuRx4jBklstXXEuC9sysXSbWXAi\/Y\/22FypVeySZJqwTOQYqDSx\\naf4JpJSZ5j9kVuXAMoGKaS4zcxS49FEzzcizFS9gCDxe1jOwA2a\/GmbCupdTM+j3pwhPBRoU1k\/2\\nmL2KrZ+BQ3DRgM9lPLfGD9Qqu39+4pSlNr9ZRWOV95gtdvAnx1Y88XcwebS0B5OGTZA3bLpChUq9\\n7NyL9qgqWFRl+lpVr18ylVnOraaPOMWJLVRVf79CYvfoF0algQZq04ekYuEvDzHz7MXmhNAfj4qe\\nFrXLRXYNBQYQBWgaN86cLU1H9LTvwGX06GBWEF+5Pe7Nnc4uLr6+vjeuX5fL5X1Lz2\/9JkahUqeW\\nCp8VCwv5sttZNbXSdr95tf5a7pRAOzszDAIkYdAVVork3ltiK0WGFTsAOc4pR2gv6hsqFpt1\/NQp\\nb2\/vhusymWxu9Ox\/Hj4EAMyZO3fFZ5\/S6fSE+PiICRMBAIdjjgb3ehM\/Qq5SpxQLb2fX3M+tvZxR\\nhXmAOMJY3Nfx5\/Gd9S8HA2GtbZ8HubhMamSA7YI+jn4OpokJiTk52UOHDjUzb3lYKutFFpfLtbax\\nbroya\/qM+\/furV67ZmZ0dKslK1TqI4mlx5LKr2RUtUd9PV0R5O9oqmch+naFxXzZd3fy9SyEYAId\\nTRf1dZzZ07HpGHpg98DA7q3HdfH08mxxxdHREQCQnfXeYRYNpszo6TCjp0OZQLb19qtdsUUCWXs6\\n6r3+2sszs\/z0LETfFuvj05k\/Yzfiw5t+buabhnv0d9frvMqd27dTklM6enYcNnw4kvsVKvWeuOLV\\nl3Oq248HUeKKoAD9Gi29hFUqkDl9\/bBdTAaDnM2+Dncf2smSLANEctX2e6++u51f1x4CVQzsaHFz\\nYRuhubSjl7AWn8r49R9DP3hjxoQ3hnss6ONIegAWAMCrGunnl7JjnpYZ\/rv4bGVQNwf0jRZ6YfGl\\nSpu19+SGPf0Z2om3P9IHk\/kzhlxKq1xwMqOQT8ShBtREBdgemeqL+nH0L\/E313MNWVVUCrRrYucr\\n8wIMTVUAgBE+Vokrg0Z3sSLbEG0cfVqWVYE+aBlKYSnVmt1xBrMtqFZRheX0mnxGTV7DP0+W9OHH\\n3ef2ciTbsvdizaGfi\/bbPMKDUfuKKqoAakMceP3+CP04B2VXeCSxdMrh56hr1R8av4hdlsoue04V\\nV9FElZDmzR+mi2+XP\/fvt7Iy6PagiatXrny2YqVIKpOb2Co51mIbH7G9n5JjEMYzqJSqDSEcVCmi\\nUAor8Ic4cvZw1ErT\/Fjuiyt0YWmrn\/fr33\/vgf0UCvnjdOQ8S3q2cN688vLypisSS8+aTsMk9t1I\\ntaue3z7qtKBPBxQPohHW00JB4I+PUVSmJ5xXsVYpJ6jS97o7jhk7dsu2rVSqoedxeZfCgoIJ48ZX\\nVr51bEbKdan0j5JZIg2cjAe9XMweLe2J4kE0b\/aBeKJHV1Rhuf397+2e7NGiqtFjxnz3\/bb2qCoA\\nQAcnp1Nnzzg4ODS\/yKzNd7yzhffsGKQkLelLbH7do7xaFA\/qLCylWnOW2EwkjKoch3tb2eXafFND\\nBwzYvPU7GG5\/CSObcOzQ4fc9u1tsVkJAY5F93fbRzxQ5ac4jp5LR\/Ll1Ftbl9Mr8GuIWYFhlaQ73\\nttIk2mI7+fr6bv9pJ53eMrl3u8PHx+fnX39lMpktrnPK0+zv\/0iRCUix6nRKOYrj+DoL62Ryuc6V\\noIVZnmH\/z06KWtsWm42NzW+7d5ma6rsbbyD06dtn9Vdr373OrM23f7gDaP0qcCK3WhpfoPMxHt2E\\nJVOqL6dX6VoHOmiCUtvHu6C2vsodP\/9kb29PjEnEEBkVNSli0rvXmTV5No93k2ER2BWr84KWbsJK\\nKKirIMahT6O2jt9HlbURge6T5ct7BgURYQ+xrFm3zt3D493rpkUJJv\/vSE0kt7J1DjOpm7CQ5OXC\\nBIv0i6zqNk7oevv4fPzJUmLsIRgWi7Xz558gqJXUedaJByGllGB78qqlz4p0G+HpJqzrmUQkJKbI\\nBOY5N9u4h0LZvnMHAcaQRWdv70VLlrx7naKScTOvEG\/PZR1D0+ggrIwyUaYeu5LI4WZegeVthMpc\\nsGihR0cyVw4JYM68ue7u7u9et3hxBaiIPmHwIFe38bsOwnpCSMxdSCExzX+o\/R4nZ+ely5YRYAy5\\nmJqafva\/\/757HVIrTfP\/IdiY6y+qq0Q6rNPqICxiFhpYFZnUthYD5y9c0E5X2HUlbOjQ3n36vHud\\nU5RIsCVyleZ5qQ4Rl3UQVjIh4fk4JUnab+js7T1u\/HgCLDEQlq9c8e5FZlU28b3h5QwdVpqQCiu9\\nVJRXg\/9kRKNhad26AQDMmh3NYBic7x5+BHbv7u\/f8kQ\/rJKxKl8QbEkaHi3Wy2oiQl5RheU0sbbX\\nwsbGZviIEQRYYlAsXd7KgJImIjrT8a2cGhniANhIhXU3h4hMLLCijVlnRFQki0Vackey6NO3r5OT\\nU4uLlLYmzpgjlKmQJ7ZAKqzCWiIW5WCtm80wDE+KiCDADEODSqV+NHFCi4uwhIg1xRZkI47ViHRu\\ndVP3RX0UwFrngz2Dguzf9lgiHpXghFp0UcsNsNl0CnsQ5vWOHT9+5\/YdavWbngjSkJCW4V5OzUfd\\nbJDciUhYLyvF5UIi5iCQVnfWEaNGEmCDdjTyFLXwmJYbKKw+AGAvrA4dOvQMCoqLfbNR2Mp2D\/5U\\nId4pRiSsKjH5kWRodNrgsDCyrQAAdoAYAVpvwOscxKDBg5oLixSQZ4RAJCxDiILs69vV2tqabCsA\\nlbsAcBeQUnW\/kBAAviGl6iYyKjAdvCcUkuO72JzQ0FCyTSAZLy8vNptNrg0qNRAgiy+HqMWqM4BY\\ndZ28MYgGpj+qugMqgbYxFsxdBHPwGguGDR1y7sxZnApHSEa5qKdzyyhi74JIWHyyhUWj0VrdMiMe\\njSJHI7ml7Q6TMfjVHhgYSLqwXlZJMBPWS7LHWB09PU1MTMi1oRGqE8QM1nYDbItf5W6tedEQDMIB\\nd\/sQlo0NorUTAqCazwXmc8mqPSBQr5hVmFCAbKkc0eCd9GSW3fzIP2xOInm5uc+Skhpclkk\/O4kw\\nzF77iHFga4tj\/2KwSCSSixcuTI2aPPg\/A9PT0houurm5kWuVGllMhvbhLkej0cg2gVAqKipOHj8R\\nc\/RoUWHL+K5OLs7Z2WSmL0AY6rJtYUnwz3jeJtYGM8bCG4lE8v3WrTFHjkqlrQ9l7OxIPkRZK8Fo\\nHYv0tQYAAJfLJdsE3MnLy1uz6stH\/7R0Znd3d1+\/cUPTaguX2\/ZUH1cQ6qFtYRlCNEhDWWvAAZVK\\ndfP6jZijR+\/dvdv8OgzDYUOGRERF9g8JaX7dhOxgAgj10D7GWKRPhfCgurr67OnTMUdjXua8dTSX\\nx+ONHjt2yrSprY7T28tX0T6EhTq085d\/Z1\/F9JDtwj6Os4P1DW2anZW1e9euy5f+Fovf8pj19PKa\\nO2\/ekPChWlpoDPNL4kr7EJZIhNINN7daiu0OeqkAfQw0pVJ58fyFYzFH45\/EN9cHlUodNnz4lGlT\\nu3fvDrUV5FIkJNojGR1tC8sEVWxTbOHzsc+GTSTV1dVHDh0+deJEQUFB8+s8Hi8iKnJSZOS7Lu3v\\ng89HE18PQxDqoW1hmTLJb9XKyvQNRjKzpz266L8NJBTWxeajPAi+etWqvy9eqqt763Hfrl0jo6KG\\njxxhZqZbttzSUoLisrwPHhuRHsgXDRJk71nUQc6m4R72emQS2HwrD7WwYo4cbfoZgqCh4eFz58\/3\\n80eZXutVPsm51syRNTTtQ1jV1SScSMEWM3Pz2XPmTJ4y2YLH06ecFlNI4oEpiLztEQnLnAnzSc1Z\\nlfwsmcTa9aRnUM\/IqKiRo0frv1Igk8kUCpLPH7QWtKsVEAnL3ZJFbsrn4iKUuTc6mDO62HIaUuvo\\nY4AVh9ZQjhVH513Lo8eP61N1c1JTUrAqCjUOyEYUiITlYckmV1g5OTkymQxFyIbvRnl+N6plilQU\\nzAl2nKP38pX+vMjMJNsE4GGJ6CQ6ImEhnAjgh0wmi330KHTAAMxLlkqlL3NyGg6C0mg0Ty8vAtKl\\nFBYW1tY0HgB2cHTkIR51paam4mkXItwxFBabRv5SVkZ6OrbCevjgwYF9+588fiwQvFlBtbGxCRkQ\\nOmPWrKZc9hhSXV198MCBy39fzs7KaroIQZB\/QMC48eMmToqg0dvoZ69eJiFIZAu8bThIbkP0dvo7\\nkr8HfO\/uPayKqigvX7p4yYyp027dvCkQCKhUqourq6ubG5VKLS8vP3n8xISx47Z8++37HFfQcfHC\\nhWFhQ37asbNBVRYWFp5enjweT6PRPE1MXLt6zbgxY5KfPWv12ZI6WWM7V0vy6igEAJeNaJSJqMXy\\nsUUkUlx5lpRUV1en63Liu7zMyZkxdVpJSQmDwZgwaeL0mTPNzMzOnjmjUqpGjRktkUgOHvjr+LGY\\n3X\/sSkxI\/H3XH3quDjTw7Tff\/Ll7DwDAz98\/Iipy7LhxTXk0FArF6VOnYg4fSUlJmTBu\/A87to8c\\nNar5s2dSyiMPpu6N8FanthHwlwA6WiEN9YOoxTIjb\/F9dqTgt01VENBIpdLrV6\/pWVpeXl7kpIiS\\nkpKQ0NCbd++s37DBxcUlcuLELZu+3fbddyPCh1lZWa3f8PWtu3eDe\/dKiI+fEhkll+ubIOnrdev+\\n3L2HzeHs+PmnU2fPTIqIaJ6dhUajRURGnrlwftPmzVQqddnHS4\/HxDR9uvZKzvj9KUq1Rq7S3Lh2\\nXU9L9KeHE1KnHUTC8rThcFkkaGvx9LoVc4QhQfIfv6qBIM2li9rCvCCBTqezmMyZ0bP2HthvZ2fX\\nMNLKz2tcyxYKBNevXgUA2Nvb\/3Xo0JixYyEKRf8O0dLS0tzc\/PS5syNGajvIOiky4ujxY6ampqrX\\nmWvUAET8lbLhep4ZA742P2CkKzU+Pl5PS\/THgoV0tQWRXCiv8+WfTiE06dfi6XWLpjfu5Hu5KV0c\\nlU8eP66qrLTUI3Wqg4PDqbNnrJrFgHBxdaXT6U3NkrOLS8MPMAxv+\/EHkUikv4\/h4o8\/njJ1KtfC\\nos07\/fz9H8Q+4nA4ZQL58N1JiUUCPweTmGm+nW04e\/7YJZORn5y8rytS\/1WkU2snLqFhP0eHiRdO\\na1RVVS20aLVFXiFNIpFc1LvRsno7soirq+vylSs4HA6DwZg1Ozq4V6+mjyAIwspzFYmqGuBw6oez\\n4\/clJxYJLNnUMzO7dX49Czt96hQmluhJZ8SjbaTCCvVA+tXoj7uzYu0n\/IatA6kMTF9umVfY2AIf\\n2Lcf8z2NufPnP05MiE96+uWaNdiWjJqTM7t6WLKqxMqPDiQX82WJCYkvXhAdyvZdGFTIG3NheVoR\\nFOcEhjVbV9Wy\/j9l3+qt5nkFb\/r1V\/n5N65jP4ZlMBgGFdrU3oyRsCJopLfl0yJhjx8ff\/bjfrIt\\nqmeAhwUL8YomUmF525o4mhGRaXJ0mKRzx8ZzILcfMS7faSnovXv2NI+Y+KFizqQem951RahTiUB+\\n2zJc0AFNYmZs8bXTYdUJqbBgCvDRpVx0MBia6EmNm5JyOdj6eyuT26eJT69euYq3JYYAmw5\/P9or\\nnJaloVCVLOKGIu9jaCdL5DfrsC\/2UVfcT42G9hC6Ozf658RcYOcXtT65\/XnnTrwtMRD+efgwK2aL\\n85UvLLL0XcPTEwgCvvY6TGV0EFZvxFNN1Ezo9ibfyZkr7x30ZGZk7NlFTq5RIlEoFN9u\/AYAQCMj\\n8nYLBna00MkFVwdhdXMwdeW1zIONITCkGuka1\/BzYirtRa62Id3O7dtLiovxM8YQ2L93X3p6Gwlg\\nCKOfm26H0XVbT\/+Ph8W+6hIdTUJKkE0GldI4Kk\/JaGOFVywWf7pi5eGYozgZQwxqeYZaeF6jyNYo\\nXwF1HQTbAKoDhdkLZg8qKFbt+PFHsg18w0gf3daldRPW1O52+57gJaxQxzf+x0+etT0DjYuNPX3q\\n1PiPPsLJHlxRS5+oqjepxZebX2w4aqiu26uE6JkJ7mymSCol32Hp9fIH3d9Rt+1\/3ZzaermYmzPx\\n+lU7mLzJOlRWiaiWLz\/\/orlvUztBoaz8QlE4sIWq3kIj7x+YcWlfxZAQ8gOh17\/z7lyqju6Put3O\\npsNhXhi4kbQO9N7\/vA+FQrFk0eKKCkI3MfVCLZUXR6lqfwSg7f0DM1PN1i9royeRHwt9fm+d3bJ1\\ndsNFmEqFMLKzslYuW65Ukh9rCQmKypUasQ7bnVQYLJstDA8ls91yMKP3cdU5jJTOwprgZ2tnSsQS\\nPHL+efhw7ZerybaibVSCE+q6P3V9CobBuhV8SwvSjt+N6WJN17UjRCEsKgUa44tLuhil+o0xpia6\\nfY\/Hjx37et06HIzCDrVIWbUW3aOmHM2SGaR1iONR9VFoTqRMCbRD8VSbpFa9CQfVK0Bnv82\/9h\/Y\\ntuU7rI3CDJXwIlDmon58fLjEzpqE7r6LLWcwqlE1GmH1d7fwssbeF+Dqq+5NP3u6ofkSf\/\/tt7Wr\\n16hV5AdNfReV6Lw+j1OpYEAvEhz95gSjTBCJ8gzd8hBndA9qoUBoWyZuHCSGBMnsrNHo48ihQ4sX\\nLRIKyTxe2yptJEpBgI8X0YfrYQhM64Eyli5KYc3sac\/UfUDXJmdeNoZwhWEQPgDlVOj61WsfjR2n\\nf+QjjFHrm6LWxZHornB2sIOl7iEFGkApDiYNnhOMfVzo\/Sn\/afp5yliRqQlKv6uc7OzhQ4ZeuqCX\\nH7NYrhLK6v\/pU0gDapVQqGAKFUypEn3AeksLor3QPu6HNBzcu6BvdT4f5IZ59ti0XM6dR41b6A62\\n6qjR6MMi8vn85Z98surzL1B3iyG\/JJiuumO66o5K7z9oeoXGas8pqz2nIq+uQl2IWEJott5hnXk6\\n+cm0AL2wHM0Zk\/wxXixV0Tnb95oq\/7+NmDdZ6OyAvv1Xq9XHY2LGjBx55\/Ztfayqk+o7uGnK5kCB\\n0Is0v5DQE3irBuuVW0WvcdK3Izrq8\/i7qBimWbm0\/ScaXVVZTLBjXTUV1itOcH5e\/pxZ0XOjZyPY\\nVVQCIH99pA+83r9q\/HKuvdDXHepKRlXDD\/Yc9EUVlRG3Id3L2UxXP5kW6CUsNx4rKgDT9ElUhgpm\\n7NxnmpLR+HZ6uat+2VjdPGy9h4fH77t37T1woE\/fvsgLvn3rVnjYkGUfLy0pafDOkKtEV5UVKxUl\\nkbK8TrJs5ut\/JrJsM1k2u\/7nHPN+vEMNzy46lXEtswr173QsqWzzrcYzsSH26ANc3biPozNcCzbr\\n3WRAesYNzygT+X0fJ1dhFnzc6fIXdHGFvY1y95ZqN6fGHuTvW8zV27gyef0g4+DhQ71fS0omk4X2\\n7VdZWYm8cEsLVXiodOFsDwtWUps5Fh6Vdh589juVprGdgCBgb0qnNAtoZ8aEfe1MhnlbTgm0o8GU\\nrArxTw8KnhYJCmplqtfJ19QaTUmdvKkaDlXyfMocOzaawB5PntFmrnxrw6POLaQicDqKotpkYEeL\\nmwv1TYyob7fd2ZYzr5fjzw9bJqlCjYrNBeKKknLq0rUWf2yudrCt75gCfOUOtsrc1+fAmP9\/TovB\\nYAwcNOj4MW0Zmpvo6KqYPEY8Llz8OlLQUySP9LbLmO1zedfzxnPxGg0ornt7P4AP0srEx5+Vv6yS\\nTPSz7fNTvPYp5Ajz8+hUpdaAXw8Sl+nkqyEYZK7DYC1q1WBXDGPBK9iN7+XLAtr8z3kl5ZQaPrRg\\nFS\/3\/08X7ty+XSJpXOKi0dveDre3UW78tPbUH5URo8RtxZ9qyQ\/9\/ljc9RyH2saK2jc38uafSNei\\nKopMwEs9lXn4ak4+mjf5xEXW4ySCTqJH+NuEYHE4GYOJhr0Z438DXdZceal\/UfXAb\/74LwtokxZa\\nmXA0r4rf2Hn\/3v2Tx49PmzGjzTDdNJpmyljR4ulCNgtlT02lqL\/vt+t\/gcfSa5wzapyqpGYaTWNX\\nqAGgSmp2vaB7Nt9RrQGPXgfrhpQyVkUGvbYA0rwWmUYDywT0uiIGv4CilAkBZeGXFod3VFlb6jA3\\njHtK3\/IbQRm\/WDTKhnAPTIrCZga7ItR5\/5MShGmotaOivjVErebD1e9kpTDnWjR4+V1\/\/6loF0fl\\n1ytre3TDYBvEhs23YaeEOrYy7lao4YmXV195FQRey8jhzhYm\/5WWoopKqVM+sfxpfU0nD0TLKBdv\\nMr\/eYd4wuCSA\/\/7HxdMamyPv2AiLTYe3juo4fj8GMX0VHOv3fdS7T28nJ+d+If2HjxhRP3PZtIn\/\\nngh3vQJl276ssTDHPZ8RjaL6LPCEs2lFQgrtZZpQu6oaKCqlzlhpOWuicE6kSEt87vJKyva9pueu\\nsRA60+qPE5fx6QDMtoD1nRU2Z\/Kh1KNP9d2hY1a+cLzbuvfL2Qvnfbt2BQBUVlRu2rjx\/Llzrd42\\nOkz87f+Izr0jkYLpyy3TsnRwgTThqKMnCXsFyrt1VjTNNWVyEJvIuBvLOH6J3dTttgrms8KbCwIG\\nemLmd46lsAprpQE\/PK4U6dX70GvynW5taPWjhYsXWfB4D+7de3D\/wfvCN0SMEq39BGVuEj1RqcC0\\nZZbP0tG417JZag5bU1tHUSiQtk\/YCmtyoO3hKb5YlYaxsAAAB+NLph9N06cESCF1O78EXes\/anB9\\nW4UwdQIeCITQvC94yai0pSsYCsvRjJG4MsjGBEuzMXZ9mdbDXk\/HZQ2NqTBB46Havats\/QoyVfXa\\no1rz84ZqF0eSs5Loyh8TO2OrKuyFBQDYPdHbxgS9c8jrHUOdN9XtrFXbv6phGMAhD0uuZuuqWiaj\\n3QRaig6yH6HjKWckYC8saxO6nr211FK3jSoqrNn6ZQ2Payg5bbt0Uq6cS\/5hQCR4WDJ\/Ht8Jj5Jx\\nSe8x2Iu3JswV9eMS68463T9ppDjQ17B6n4jR4uAALPMP4AGLRjkzyw95kD6dwCtvzBeDXAd7otwZ\\nUHJ0aJmteaolM9E3D2K913SFoubDusY\/EkwB\/50vYNB1a0RRZKHShz2TvLvq4cqnHbyExaLB+6N8\\nEGaKaoHCxFbBQrqgMm+y0NwUZSf4LI02bIbNzYfo\/5z7jnPGz7fKL6rXE9X2L6rN701faeeOytFh\\nYuRF+XTpcvn6tb79dPAF0ofFfR0n43OMrwEcM105mjPPzOrGoeteBQQpTBC5eXHNVJGjdfjjNaeq\\nBprzP15lNbz0K4vLt3V2ddJowE\/7TbbtMisqpc5cYQm438Cmk2CzaVT7kwBqbAbmRgnbdM5pILhX\\nr5gTx52dnXfv3dvFF8v1pFbp78b9YYwXrlXgm0Ktq73J4Sm+KFYAxPbdkNw2f4oQdRI4SwvNgqkN\\n7vDQp99YfL7ZHHmA\/tIKSvSnvN8PNbqyzJwoBMI\/gaq6vhPkDKc5xQF6vf2OdupxQ9vua1d+9tnh\\nmKNsNhsAEPvoURbOkbfdecwzs7rSYXz\/9Ljn5hvja71lpM7uiFKee5v3UGHN+GF6DZFmR4g+ia6D\\nKfWNyoUb7CFTbY6cY\/PrtL0IxWWUnftMhs+wefyM8bpt1Xy+iD9jghgoX8oLBwNlaf13SvdgOMfC\\n1jsAzWPCiPc2qAwGY9z48ddu3li4eFHDlevXri9esFD\/7D1asDGh3VoUaMnBfWEG45X39\/H5xawt\\nt9veoH2DRuN8ZRVNrC0+0eC+kh3rMUizdvwia\/OvbzwITDjqIf2lQX4ydxcVk6GhUTVCMSSRQk+e\\n0VMy6HfjGE1beEyGZv3y2pGDm83+qB40+yMURlOGerVKeOnbHxLjnyQIBHXVVdVMJtPaxsbe3r5P\\nv77Dhg+3sXlzGuXk8RNfrVmDPK8JipV3Dh2+Pt+\/t+6hY1BAkLAAAFGHUmN02aK2jt9vlv9Ayw2b\\n\/lszZgg2U\/rMHOoXW8wzX+rwHnu5KTZ8Wuvb6R3vF8iEarkR5s5B7jnC5\/N\/3Pb9oYMHdbBYd2Gx\\naZQjU33H+L7XeQRbiBOWSg0iDyafTEYaJI1TmGAX99v7P9c8vVyqq0eoFtRqcOEm6\/eDJs2dClvF\\n3kY5f4pwzBCJltoheleq5UYKZ5B2eUml0nNnzn6\/bVt1lc6HNXQSFgRAzDTfSf6YnnzRCnFH1WAK\\nODzFV6ZMuZCG6PiDyMFPyeRSpa13dl07KzBUVf3AiALGhElGDpQkZ9Cu32dm5VKT0uhiSeMYlGeu\\n8vFSuDsrB\/aRBXaVtznw1chTFCVjANUVNp0EMfwlmkEmpm+8QIsKC+Pi4uJiY2\/duFlTo+\/R+zaB\\nADg+3XeCH3GqIlRYAAA6lXJiRtfIgylnUxFoi0IV2fuZ595t9cPOHrgstcMwCOiiCOiiaGjDauvq\\nR1cmHA3KpTJlnqrmu\/pZwnLLogpnCx5PrVaXFBfX1RHn2ANB4ECkD8GqImJW2AIGlXJierepyJbm\\nhI7d3\/cRujhHOkGhAB5X42inRr0A24S7s7K0tDQ9LS0zI4NIVVEp0MnpXVFHjNEHooX1epkAOjCl\\ny+ygtn9bqa2Pktn6OYIunoa1Oagdax4JIbtM6PDfc\/zQxePTHxKE1VDrngifTcPbXqyq9Rza6nU3\\np\/YRzbYB4q3lsakPlnQP0yWtEraQI6wGvhjktjfCm03TZkOdW381pZWBoLmZoTjJIMEWVRA51Pg7\\nmCStDPZzJO6M67uQKSwAwKwgh2vzA7Q4BmpoLIFTL2KNwh5TDnGvwfDOltfnBzhxiQv00CokCwsA\\n0NeNm\/xpr27v99+o6TJWQ9QRKJwgzGF69WDXC3P8rbD2M0YB+cKq7ylM6XGf9JzXq\/U4qioWV+Dc\\nvhstKf5hablM6rlZ3TYM86AYxjtoEMJ6HXuS8sdE74OTfXjsVkZUtZ2Ga6C3TMVzoxZ7qmqw99JU\\nN\/vbBTmbJa4IGk3Udg0SDEVYDUztbv9gSY8+72TcVJjZ891Cm18pLDWItFgIKSzBQVg0VkMnu3qw\\n662FgW6ofCrxw7CE9TqrOefhxz02hLvT4bfa9NrOw1W0N2EFMnIw3dDBmZcF2O9wqJjm7pbM+4u7\\nbxjmwcEu2g9WGJywGlgd5pa0Mrhvs6ZLxbKo7jK26b\/J6e1JWDV8zL9naNmIwLT\/9u6rX0BH\/DBQ\\nYTU0XQ8+7vHLeK+mUON1riEy08YBfjGBATn1J+4pltM0H7+A0xcurJ8\/kYFDqH2sMFzLGljU1+n5\\nZ8GzguxhCgRgakXgdA1UL6l\/4gk90KIPFVUUvgCb18CCx1v99cbz505369oFkwLxw9CF9XoxgrE3\\nwufhku4DO1rIrDrWufYDAEhklLQsQl0zUHMvDoN3gMnmTJkx89qt2zOnT8HCKNxpH38bAECwi\/nN\\nhYE3s6qXnYD5RzLpwtIzV9g+nuQEltGJK\/f0WgSnUKnhI0Z99tlKpw46pzklEeI8SLFCrQaXHzzZ\\ntW1TWUHC3RPlZJvTBlU1lJCJNuiCp8FU2pjx42fPndvJE5vwjUTS\/oTVgFqtvnX7jrPJZhfLe2Tb\\noo29xzjf79Yt\/3t9P0JnTIyMmhU9y90V+yxrxNBehdWEVJSqqNpKkZykwgaXplClAn3H2wpEOgxk\\nbRw6RERGzps\/l2UIoXP0oN0LqwGVUiSu+AkSHaSBHLJteUPMedaGncjWmSAoZOCgydOmDx7QH3ez\\nCOEDEVYTgpoEWH5Jzf+FRiE6DGkLpDIQPs2morqNhQbvbv6TJ0cNGxbONde5xzRkPjRhNVFXeRWS\\n3wOSK1TVc9TH8PVhy2+mf51q3RfIxNSsq5\/f4LCwoeHhdrYGtHOMIR+ssJoQ8p8D6TW1+AakTKND\\nJcRU+iKXGrnYqnl8dogCe3h6+gd2Dw7uGRYWZsLBJpy6wfLhC6s5UmGyVBgPSf6mQ3lKSTqdhst4\\nXyyBJi2yzC2gmfOsnJw6dPMPCAsb7Onp+aE2Tq3y7xJWc6RSPlWTIxZmaqSJGlkCiy4W8DNNWSij\\njKhUQKp2YHNsJQrH\/MretfJe9ra2HTu6w6R0wwbAv1dYrVInKGFSSqmwhi8oA6pKJk0sEpZSNLVQ\\ns8yoSjWDzrCm0y3FcjbLxJFJ54hldCrTncn4wHs3nTAKywgu\/EsbaiN4YxSWEVwwCssILhiFZQQX\\njMIyggtGYRnBBaOwjOCCUVhGcOH\/AgAA\/\/+S+QX6OV9s5wAAAABJRU5ErkJggpQu\\n\",\"__pickled_extra_fields__\":[\"thumbnail\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ @@ -199,11 +199,11 @@ "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" ] }, { - "update": "Update(callback_query=CallbackQuery(chat_instance='-1', data='hash', from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), id='9', message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 22, 19, 53, 42, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Bot', id=6601664672, is_bot=True, username='dff_bot'), group_chat_created=False, location=Location(latitude=58.43162, longitude=27.792879), message_id=10, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), supergroup_chat_created=False)), update_id=10)", + "update": "Update(callback_query=CallbackQuery(chat_instance='-1', data='hash', from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), id='9', message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 22, 19, 53, 42, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Bot', id=6601664672, is_bot=True, username='dff_bot'), group_chat_created=False, location=Location(latitude=58.43162, longitude=27.792879), message_id=10, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), supergroup_chat_created=False)), update_id=10)", "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"hash\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":\"Alright! Now send me a message with data attachment (audio, video, animation, image, sticker or document)!\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ @@ -213,9 +213,9 @@ { "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 22, 20, 12, 23, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=12, sticker=Sticker(api_kwargs={'thumb': {'file_id': 'AAMCAgADGQEAAgS0Zk5Rpz6PkZu6Ef3Ef7GwnxAZYocAAiwCAAJHBkMF0YsKl8FLgq8BAAdtAAM1BA', 'file_unique_id': 'AQADLAIAAkcGQwVy', 'file_size': 5416, 'width': 128, 'height': 128}}, emoji='👨', file_id='CAACAgIAAxkBAAIEtGZOUac-j5GbuhH9xH-xsJ8QGWKHAAIsAgACRwZDBdGLCpfBS4KvNQQ', file_size=29492, file_unique_id='AgADLAIAAkcGQwU', height=512, is_animated=False, is_video=False, set_name='citati_prosto', thumbnail=PhotoSize(file_id='AAMCAgADGQEAAgS0Zk5Rpz6PkZu6Ef3Ef7GwnxAZYocAAiwCAAJHBkMF0YsKl8FLgq8BAAdtAAM1BA', file_size=5416, file_unique_id='AQADLAIAAkcGQwVy', height=128, width=128), type=StickerType.REGULAR, width=512), supergroup_chat_created=False), update_id=11)", "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"source\":null,\"cached_filename\":null,\"id\":\"CAACAgIAAxkBAAIEtGZOUac-j5GbuhH9xH-xsJ8QGWKHAAIsAgACRwZDBdGLCpfBS4KvNQQ\",\"title\":null,\"use_cache\":true,\"dff_attachment_type\":\"sticker\",\"is_animated\":false,\"is_video\":false,\"type\":\"regular\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", - "response_message": "{\"text\":\"Here's your previous request hash: `75681742958b21061e6d5ebb80340d62de4c7348e71fb8c1d2c356a2239b2334`!\\nRun \/start command again to restart.\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", + "response_message": "{\"text\":\"Here's your previous request first attachment sha256 hash: `75681742958b21061e6d5ebb80340d62de4c7348e71fb8c1d2c356a2239b2334`!\\nRun \/start command again to restart.\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_message(42, \"Here's your previous request hash: `75681742958b21061e6d5ebb80340d62de4c7348e71fb8c1d2c356a2239b2334`!\\nRun /start command again to restart.\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" + "send_message(42, \"Here's your previous request first attachment sha256 hash: `75681742958b21061e6d5ebb80340d62de4c7348e71fb8c1d2c356a2239b2334`!\\nRun /start command again to restart.\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" ] }, { @@ -223,7 +223,7 @@ "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" ] }, { @@ -239,19 +239,19 @@ "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" ] }, { - "update": "Update(callback_query=CallbackQuery(chat_instance='-1', data='restart', from_user=User(first_name='AlexaTestnder', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), id='14', message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 9, 59, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Bot', id=16, is_bot=True, username='dff_bot'), group_chat_created=False, location=Location(latitude=58.43162, longitude=27.792879), message_id=15, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), supergroup_chat_created=False)), update_id=15)", + "update": "Update(callback_query=CallbackQuery(chat_instance='-1', data='restart', from_user=User(first_name='AlexaTestnder', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), id='14', message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 9, 59, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Bot', id=16, is_bot=True, username='dff_bot'), group_chat_created=False, location=Location(latitude=58.43162, longitude=27.792879), message_id=15, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), supergroup_chat_created=False)), update_id=15)", "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"restart\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" ] }, { - "update": "Update(callback_query=CallbackQuery(chat_instance='-1', data='quit', from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), id='15', message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 10, 1, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Bot', id=16, is_bot=True, username='dff_bot'), group_chat_created=False, location=Location(latitude=58.43162, longitude=27.792879), message_id=16, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='Attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), supergroup_chat_created=False)), update_id=16)", + "update": "Update(callback_query=CallbackQuery(chat_instance='-1', data='quit', from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), id='15', message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 21, 19, 10, 1, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Bot', id=16, is_bot=True, username='dff_bot'), group_chat_created=False, location=Location(latitude=58.43162, longitude=27.792879), message_id=16, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), supergroup_chat_created=False)), update_id=16)", "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"quit\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":\"Bot has entered unrecoverable state:\/\\nRun \/start command again to restart.\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ From 7e1d77c244ec877de3965c72dfa903f35022a4df Mon Sep 17 00:00:00 2001 From: Roman Zlobin Date: Tue, 25 Jun 2024 19:15:17 +0300 Subject: [PATCH 128/140] fix MessengerInterfaceWithAttachments usage in tests --- tests/script/core/test_message.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/script/core/test_message.py b/tests/script/core/test_message.py index a862c640a..9b87440b0 100644 --- a/tests/script/core/test_message.py +++ b/tests/script/core/test_message.py @@ -8,7 +8,7 @@ import pytest from pydantic import ValidationError, HttpUrl, FilePath -from dff.messengers.common.interface import MessengerInterface +from dff.messengers.common.interface import MessengerInterfaceWithAttachments from dff.messengers.console import CLIMessengerInterface from dff.script.core.message import ( Animation, @@ -42,11 +42,11 @@ def __eq__(self, value: object) -> bool: return False -class DFFCLIMessengerInterface(CLIMessengerInterface): +class DFFCLIMessengerInterface(CLIMessengerInterface, MessengerInterfaceWithAttachments): supported_response_attachment_types = {Document} def __init__(self, attachments_directory: Optional[Path] = None): - MessengerInterface.__init__(self, attachments_directory) + MessengerInterfaceWithAttachments.__init__(self, attachments_directory) self._ctx_id: Optional[Hashable] = None self._intro: Optional[str] = None self._prompt_request: str = "request: " From dfbccf7389df2d801b3b435313c30dea18d98cec Mon Sep 17 00:00:00 2001 From: pseusys Date: Wed, 26 Jun 2024 10:14:50 +0200 Subject: [PATCH 129/140] cached filename check added --- dff/script/core/message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dff/script/core/message.py b/dff/script/core/message.py index c1973aede..b4b37fbef 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -184,7 +184,7 @@ async def get_bytes(self, from_interface: MessengerInterfaceWithAttachments) -> if isinstance(self.source, Path): with open(self.source, "rb") as file: return file.read() - elif self.use_cache and self.cached_filename is not None: + elif self.use_cache and self.cached_filename is not None and self.cached_filename.exists(): with open(self.cached_filename, "rb") as file: return file.read() elif isinstance(self.source, Url): From 037a5d3052297ad812809b8ba3780b7856cbe622 Mon Sep 17 00:00:00 2001 From: pseusys Date: Wed, 26 Jun 2024 10:54:42 +0200 Subject: [PATCH 130/140] telegram API connections updated --- dff/messengers/telegram/abstract.py | 56 ++++++++++++++++++----------- poetry.lock | 9 ++--- pyproject.toml | 2 +- 3 files changed, 42 insertions(+), 25 deletions(-) diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index b47bc8549..08cbac509 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -311,6 +311,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes "protect_content", "reply_markup", "message_effect_id", + "reply_to_message_id", ], ), ) @@ -322,7 +323,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes attachment.last_name, **grab_extra_fields( attachment, - ["vcard", "disable_notification", "protect_content", "reply_markup", "message_effect_id"], + ["vcard", "disable_notification", "protect_content", "reply_markup", "message_effect_id", "reply_to_message_id"], ), ) if isinstance(attachment, Poll): @@ -344,7 +345,9 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes "disable_notification", "protect_content", "reply_markup", + "question_parse_mode", "message_effect_id", + "reply_to_message_id", ], ), ) @@ -355,7 +358,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes sticker, **grab_extra_fields( attachment, - ["disable_notification", "protect_content", "reply_markup", "emoji", "message_effect_id"], + ["emoji", "disable_notification", "protect_content", "reply_markup", "message_effect_id", "reply_to_message_id"], ), ) if isinstance(attachment, Audio): @@ -379,13 +382,16 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes **grab_extra_fields( attachment, [ + "parse_mode", "performer", "title", "disable_notification", + "protect_content", "reply_markup", - "parse_mode", "thumbnail", "message_effect_id", + "reply_to_message_id", + "filename", ], ), ) @@ -418,14 +424,17 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes **grab_extra_fields( attachment, [ - "disable_notification", - "reply_markup", "parse_mode", "supports_streaming", + "disable_notification", + "protect_content", + "reply_markup", "has_spoiler", "thumbnail", - "filename", "message_effect_id", + "show_caption_above_media", + "reply_to_message_id", + "filename", ], ), ) @@ -452,11 +461,14 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes [ "parse_mode", "disable_notification", + "protect_content", "reply_markup", "has_spoiler", "thumbnail", - "filename", "message_effect_id", + "show_caption_above_media", + "reply_to_message_id", + "filename", ], ), ) @@ -481,12 +493,14 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes **grab_extra_fields( attachment, [ + "parse_mode", "disable_notification", + "protect_content", "reply_markup", - "parse_mode", "has_spoiler", - "filename", "message_effect_id", + "reply_to_message_id", + "filename", ], ), ) @@ -518,12 +532,14 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes **grab_extra_fields( attachment, [ + "parse_mode", "disable_notification", + "protect_content", "reply_markup", - "parse_mode", "thumbnail", - "filename", "message_effect_id", + "reply_to_message_id", + "filename", ], ), ) @@ -538,12 +554,13 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes **grab_extra_fields( attachment, [ - "disable_notification", - "reply_markup", "parse_mode", - "filename", + "disable_notification", "protect_content", + "reply_markup", "message_effect_id", + "reply_to_message_id", + "filename", ], ), ) @@ -554,17 +571,16 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes await bot.send_video_note( chat_id, attachment_bytes, - caption=message.text, **grab_extra_fields( attachment, [ "disable_notification", + "protect_content", "reply_markup", - "parse_mode", "thumbnail", - "filename", - "protect_content", "message_effect_id", + "reply_to_message_id", + "filename", ], ), ) @@ -574,7 +590,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes chat_id, files, caption=message.text, - **grab_extra_fields(message, ["disable_notification", "protect_content", "message_effect_id"]), + **grab_extra_fields(message, ["disable_notification", "protect_content", "message_effect_id", "reply_to_message_id", "parse_mode"]), ) message_text_covered = True if message.text is not None and not message_text_covered: @@ -583,7 +599,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes message.text, **grab_extra_fields( message, - ["parse_mode", "disable_notification", "protect_content", "reply_markup", "message_effect_id"], + ["parse_mode", "disable_notification", "protect_content", "reply_markup", "message_effect_id", "reply_to_message_id", "disable_web_page_preview"], ), ) diff --git a/poetry.lock b/poetry.lock index 0c223de15..bc9c5f1f0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "accessible-pygments" @@ -1821,8 +1821,8 @@ files = [ [package.dependencies] cffi = {version = ">=1.12.2", markers = "platform_python_implementation == \"CPython\" and sys_platform == \"win32\""} greenlet = [ - {version = ">=3.0rc3", markers = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\""}, {version = ">=2.0.0", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""}, + {version = ">=3.0rc3", markers = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\""}, ] "zope.event" = "*" "zope.interface" = "*" @@ -3880,8 +3880,8 @@ files = [ [package.dependencies] numpy = [ - {version = ">=1.23.2", markers = "python_version >= \"3.11\""}, {version = ">=1.20.3", markers = "python_version < \"3.10\""}, + {version = ">=1.23.2", markers = "python_version >= \"3.11\""}, {version = ">=1.21.0", markers = "python_version >= \"3.10\" and python_version < \"3.11\""}, ] python-dateutil = ">=2.8.2" @@ -5041,6 +5041,7 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -7262,4 +7263,4 @@ ydb = ["six", "ydb"] [metadata] lock-version = "2.0" python-versions = "^3.8.1,!=3.9.7" -content-hash = "bc75732b813f1a509fca36ad4ed2eff6d5320e69e9d750f39be3926e6d0961ec" +content-hash = "0fea55ff020381487754e65f4630de8bfaedc65c37b6d071763d427f1667b7b3" diff --git a/pyproject.toml b/pyproject.toml index 7744f0f11..a7ac29fb4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,7 +73,7 @@ aiosqlite = { version = "*", optional = true } omegaconf = { version = "*", optional = true } cryptography = { version = "*", optional = true } requests = { version = "*", optional = true } -python-telegram-bot = { version = "*", extras = ["all"], optional = true } +python-telegram-bot = { version = "~=21.3", extras = ["all"], optional = true } opentelemetry-instrumentation = { version = "*", optional = true } sqlalchemy = { version = "*", extras = ["asyncio"], optional = true } opentelemetry-exporter-otlp = { version = ">=1.20.0", optional = true } # log body serialization is required From 4313477c44c37253925cef81098d1a65cee7c3e5 Mon Sep 17 00:00:00 2001 From: pseusys Date: Wed, 26 Jun 2024 11:40:02 +0200 Subject: [PATCH 131/140] media group attachment introduced --- dff/messengers/telegram/abstract.py | 404 +++++++++++++--------------- dff/script/core/message.py | 21 ++ 2 files changed, 205 insertions(+), 220 deletions(-) diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index 08cbac509..f034e5302 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -30,6 +30,7 @@ Video, VideoMessage, VoiceMessage, + MediaGroup, ) try: @@ -54,21 +55,6 @@ telegram_available = False -def _is_attachment_mediagroup_combinable(attachment: Attachment): - """ - Return true if the attachment can be sent in a mediagroup, false otherwise. - - :param attachment: Attachment to check. - :return: If the attachment can belong to a mediagroup. - """ - - return ( - isinstance(attachment, DataAttachment) - and not isinstance(attachment, VoiceMessage) - and not isinstance(attachment, VideoMessage) - ) - - class _AbstractTelegramInterface(MessengerInterfaceWithAttachments): """ Messenger interface mixin for Telegram API usage. @@ -100,6 +86,7 @@ class _AbstractTelegramInterface(MessengerInterfaceWithAttachments): Document, VoiceMessage, VideoMessage, + MediaGroup, } def __init__(self, token: str, attachments_directory: Optional[Path] = None) -> None: @@ -291,12 +278,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes :param message: DFF message that will be processed into Telegram updates. """ - message_text_covered = False if message.attachments is not None: - files = list() - media_group_attachments_num = len( - [att for att in message.attachments if _is_attachment_mediagroup_combinable(att)] - ) for attachment in message.attachments: if isinstance(attachment, Location): await bot.send_location( @@ -315,7 +297,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes ], ), ) - if isinstance(attachment, Contact): + elif isinstance(attachment, Contact): await bot.send_contact( chat_id, attachment.phone_number, @@ -326,7 +308,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes ["vcard", "disable_notification", "protect_content", "reply_markup", "message_effect_id", "reply_to_message_id"], ), ) - if isinstance(attachment, Poll): + elif isinstance(attachment, Poll): await bot.send_poll( chat_id, attachment.question, @@ -351,209 +333,139 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes ], ), ) - if isinstance(attachment, Sticker): - sticker = await attachment.get_bytes(self) if attachment.id is None else attachment.id - await bot.send_sticker( - chat_id, - sticker, - **grab_extra_fields( - attachment, - ["emoji", "disable_notification", "protect_content", "reply_markup", "message_effect_id", "reply_to_message_id"], - ), - ) - if isinstance(attachment, Audio): + elif isinstance(attachment, Audio): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: - if media_group_attachments_num != 1: - files += [ - InputMediaAudio( - attachment_bytes, - **grab_extra_fields( - attachment, - ["filename", "caption", "parse_mode", "performer", "title", "thumbnail"], - ), - ), - ] - else: - await bot.send_audio( - chat_id, - attachment_bytes, - caption=message.text, - **grab_extra_fields( - attachment, - [ - "parse_mode", - "performer", - "title", - "disable_notification", - "protect_content", - "reply_markup", - "thumbnail", - "message_effect_id", - "reply_to_message_id", - "filename", - ], - ), - ) - message_text_covered = True - if isinstance(attachment, Video): + await bot.send_audio( + chat_id, + attachment_bytes, + **grab_extra_fields( + attachment, + [ + "caption", + "parse_mode", + "performer", + "title", + "disable_notification", + "protect_content", + "reply_markup", + "thumbnail", + "message_effect_id", + "reply_to_message_id", + "filename", + ], + ), + ) + elif isinstance(attachment, Video): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: - if media_group_attachments_num != 1: - files += [ - InputMediaVideo( - attachment_bytes, - **grab_extra_fields( - attachment, - [ - "filename", - "caption", - "parse_mode", - "supports_streaming", - "has_spoiler", - "thumbnail", - ], - ), - ), - ] - else: - await bot.send_video( - chat_id, - attachment_bytes, - caption=message.text, - **grab_extra_fields( - attachment, - [ - "parse_mode", - "supports_streaming", - "disable_notification", - "protect_content", - "reply_markup", - "has_spoiler", - "thumbnail", - "message_effect_id", - "show_caption_above_media", - "reply_to_message_id", - "filename", - ], - ), - ) - message_text_covered = True - if isinstance(attachment, Animation): + await bot.send_video( + chat_id, + attachment_bytes, + **grab_extra_fields( + attachment, + [ + "caption", + "parse_mode", + "supports_streaming", + "disable_notification", + "protect_content", + "reply_markup", + "has_spoiler", + "thumbnail", + "message_effect_id", + "show_caption_above_media", + "reply_to_message_id", + "filename", + ], + ), + ) + elif isinstance(attachment, Animation): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: - if media_group_attachments_num != 1: - files += [ - InputMediaAnimation( - attachment_bytes, - **grab_extra_fields( - attachment, ["filename", "caption", "parse_mode", "has_spoiler", "thumbnail"] - ), - ), - ] - else: - await bot.send_animation( - chat_id, - attachment_bytes, - caption=message.text, - **grab_extra_fields( - attachment, - [ - "parse_mode", - "disable_notification", - "protect_content", - "reply_markup", - "has_spoiler", - "thumbnail", - "message_effect_id", - "show_caption_above_media", - "reply_to_message_id", - "filename", - ], - ), - ) - message_text_covered = True - if isinstance(attachment, Image): + await bot.send_animation( + chat_id, + attachment_bytes, + **grab_extra_fields( + attachment, + [ + "caption", + "parse_mode", + "disable_notification", + "protect_content", + "reply_markup", + "has_spoiler", + "thumbnail", + "message_effect_id", + "show_caption_above_media", + "reply_to_message_id", + "filename", + ], + ), + ) + elif isinstance(attachment, Image): attachment_bytes = await attachment.get_bytes(self) - if attachment_bytes is not None: - if media_group_attachments_num != 1: - files += [ - InputMediaPhoto( - attachment_bytes, - **grab_extra_fields( - attachment, ["filename", "caption", "parse_mode", "has_spoiler"] - ), - ), - ] - else: - await bot.send_photo( - chat_id, - attachment_bytes, - caption=message.text, - **grab_extra_fields( - attachment, - [ - "parse_mode", - "disable_notification", - "protect_content", - "reply_markup", - "has_spoiler", - "message_effect_id", - "reply_to_message_id", - "filename", - ], - ), - ) - message_text_covered = True - if isinstance(attachment, Document): + if attachment_bytes is not None: + await bot.send_photo( + chat_id, + attachment_bytes, + **grab_extra_fields( + attachment, + [ + "caption", + "parse_mode", + "disable_notification", + "protect_content", + "reply_markup", + "has_spoiler", + "message_effect_id", + "reply_to_message_id", + "filename", + ], + ), + ) + elif isinstance(attachment, Sticker): + sticker = await attachment.get_bytes(self) if attachment.id is None else attachment.id + if sticker is not None: + await bot.send_sticker( + chat_id, + sticker, + **grab_extra_fields( + attachment, + ["emoji", "disable_notification", "protect_content", "reply_markup", "message_effect_id", "reply_to_message_id"], + ), + ) + elif isinstance(attachment, Document): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: - if media_group_attachments_num != 1: - files += [ - InputMediaDocument( - attachment_bytes, - **grab_extra_fields( - attachment, - [ - "filename", - "caption", - "parse_mode", - "disable_content_type_detection", - "thumbnail", - ], - ), - ), - ] - else: - await bot.send_document( - chat_id, - attachment_bytes, - caption=message.text, - **grab_extra_fields( - attachment, - [ - "parse_mode", - "disable_notification", - "protect_content", - "reply_markup", - "thumbnail", - "message_effect_id", - "reply_to_message_id", - "filename", - ], - ), - ) - message_text_covered = True - if isinstance(attachment, VoiceMessage): + await bot.send_document( + chat_id, + attachment_bytes, + **grab_extra_fields( + attachment, + [ + "caption", + "parse_mode", + "disable_notification", + "protect_content", + "reply_markup", + "thumbnail", + "message_effect_id", + "reply_to_message_id", + "filename", + ], + ), + ) + elif isinstance(attachment, VoiceMessage): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: await bot.send_voice( chat_id, attachment_bytes, - caption=message.text, **grab_extra_fields( attachment, [ + "caption", "parse_mode", "disable_notification", "protect_content", @@ -564,8 +476,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes ], ), ) - message_text_covered = True - if isinstance(attachment, VideoMessage): + elif isinstance(attachment, VideoMessage): attachment_bytes = await attachment.get_bytes(self) if attachment_bytes is not None: await bot.send_video_note( @@ -584,16 +495,69 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes ], ), ) - message_text_covered = True - if len(files) > 0: - await bot.send_media_group( - chat_id, - files, - caption=message.text, - **grab_extra_fields(message, ["disable_notification", "protect_content", "message_effect_id", "reply_to_message_id", "parse_mode"]), - ) - message_text_covered = True - if message.text is not None and not message_text_covered: + elif isinstance(attachment, MediaGroup): + files = list() + for media in attachment.media: + if isinstance(media, Image): + files += [ + InputMediaPhoto( + attachment_bytes, + **grab_extra_fields( + attachment, ["filename", "caption", "parse_mode", "has_spoiler", "show_caption_above_media"] + ), + ), + ] + elif isinstance(media, Video): + files += [ + InputMediaVideo( + attachment_bytes, + **grab_extra_fields( + attachment, + [ + "filename", + "caption", + "parse_mode", + "supports_streaming", + "has_spoiler", + "thumbnail", + "show_caption_above_media", + ], + ), + ), + ] + elif isinstance(media, Animation): + files += [ + InputMediaAnimation( + attachment_bytes, + **grab_extra_fields( + attachment, ["filename", "caption", "parse_mode", "has_spoiler", "thumbnail", "show_caption_above_media"] + ), + ), + ] + elif isinstance(media, Audio): + files += [ + InputMediaAudio( + attachment_bytes, + **grab_extra_fields(attachment, ["filename", "caption", "parse_mode", "performer", "title", "thumbnail"]), + ), + ] + elif isinstance(media, Document): + files += [ + InputMediaDocument( + attachment_bytes, + **grab_extra_fields(attachment, ["filename", "caption", "parse_mode", "thumbnail"]), + ), + ] + else: + raise ValueError(f"Attachment {type(media).__name__} can not be sent in a media group!") + await bot.send_media_group( + chat_id, + files, + **grab_extra_fields(attachment, ["caption", "disable_notification", "protect_content", "message_effect_id", "reply_to_message_id", "parse_mode"]), + ) + else: + raise ValueError(f"Attachment {type(attachment).__name__} is not supported!") + if message.text is not None: await bot.send_message( chat_id, message.text, diff --git a/dff/script/core/message.py b/dff/script/core/message.py index b4b37fbef..0cb241662 100644 --- a/dff/script/core/message.py +++ b/dff/script/core/message.py @@ -272,6 +272,25 @@ class VideoMessage(DataAttachment): dff_attachment_type: Literal["video_message"] = "video_message" +class MediaGroup(DataModel): + """ + Represents a group of media attachments. + """ + + group: List[Union[Audio, Video, Animation, Image, Document]] = Field(default_factory=list) + dff_attachment_type: Literal["media_group"] = "media_group" + + def __eq__(self, other): + if isinstance(other, MediaGroup): + if len(self.group) != len(other.group): + return False + for attachment1, attachment2 in zip(self.group, other.group): + if attachment1 != attachment2: + return False + return True + return NotImplemented + + class Message(DataModel): """ Class representing a message and contains several @@ -301,6 +320,7 @@ class level variables to store message information. Document, VoiceMessage, VideoMessage, + MediaGroup, ] ] ] = None @@ -328,6 +348,7 @@ def __init__( Document, VoiceMessage, VideoMessage, + MediaGroup, ] ] ] = None, From 7b894c75710294183e7ca678082b36350d40e42c Mon Sep 17 00:00:00 2001 From: pseusys Date: Wed, 26 Jun 2024 12:02:18 +0200 Subject: [PATCH 132/140] pickle validator parameter removed --- dff/utils/devel/json_serialization.py | 1 - 1 file changed, 1 deletion(-) diff --git a/dff/utils/devel/json_serialization.py b/dff/utils/devel/json_serialization.py index 552d94823..5eccefb68 100644 --- a/dff/utils/devel/json_serialization.py +++ b/dff/utils/devel/json_serialization.py @@ -111,7 +111,6 @@ def json_pickle_validator(model: Serializable) -> Serializable: and if it is, validates it using pickle. :param model: Pydantic model object or a dictionary. - :original_serializer: Original serializer function for model. :return: model with all the fields serialized to JSON. """ From 64fbef0e85ece81e45ce44c2aaebd89f11786d17 Mon Sep 17 00:00:00 2001 From: pseusys Date: Wed, 26 Jun 2024 15:17:01 +0200 Subject: [PATCH 133/140] Type definition sorted --- dff/utils/devel/json_serialization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dff/utils/devel/json_serialization.py b/dff/utils/devel/json_serialization.py index 5eccefb68..7ad4d70c9 100644 --- a/dff/utils/devel/json_serialization.py +++ b/dff/utils/devel/json_serialization.py @@ -36,7 +36,7 @@ This key is used in :py:data:`~.JSONSerializableDict` to remember pickled items. """ -Serializable: TypeAlias = Dict[str, Union[JsonValue, Any, List[Any], Dict[str, Any]]] +Serializable: TypeAlias = Dict[str, Union[JsonValue, List[Any], Dict[str, Any], Any]] class _WrapperModel(RootModel): From 2042186109d592633180f870fb127fa83531f1e1 Mon Sep 17 00:00:00 2001 From: pseusys Date: Thu, 27 Jun 2024 12:21:20 +0200 Subject: [PATCH 134/140] mediagroup tests added --- dff/messengers/telegram/abstract.py | 17 +- poetry.lock | 423 +++++++++--------- .../messengers/telegram/test_happy_paths.json | 94 ++-- tests/messengers/telegram/test_tutorials.py | 1 - tests/messengers/telegram/utils.py | 10 +- .../messengers/telegram/2_attachments.py | 19 + 6 files changed, 300 insertions(+), 264 deletions(-) diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index f034e5302..b4ae85b77 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -497,20 +497,22 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes ) elif isinstance(attachment, MediaGroup): files = list() - for media in attachment.media: + for media in attachment.group: if isinstance(media, Image): + media_bytes = await media.get_bytes(self) files += [ InputMediaPhoto( - attachment_bytes, + media_bytes, **grab_extra_fields( attachment, ["filename", "caption", "parse_mode", "has_spoiler", "show_caption_above_media"] ), ), ] elif isinstance(media, Video): + media_bytes = await media.get_bytes(self) files += [ InputMediaVideo( - attachment_bytes, + media_bytes, **grab_extra_fields( attachment, [ @@ -526,25 +528,28 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes ), ] elif isinstance(media, Animation): + media_bytes = await media.get_bytes(self) files += [ InputMediaAnimation( - attachment_bytes, + media_bytes, **grab_extra_fields( attachment, ["filename", "caption", "parse_mode", "has_spoiler", "thumbnail", "show_caption_above_media"] ), ), ] elif isinstance(media, Audio): + media_bytes = await media.get_bytes(self) files += [ InputMediaAudio( - attachment_bytes, + media_bytes, **grab_extra_fields(attachment, ["filename", "caption", "parse_mode", "performer", "title", "thumbnail"]), ), ] elif isinstance(media, Document): + media_bytes = await media.get_bytes(self) files += [ InputMediaDocument( - attachment_bytes, + media_bytes, **grab_extra_fields(attachment, ["filename", "caption", "parse_mode", "thumbnail"]), ), ] diff --git a/poetry.lock b/poetry.lock index bc9c5f1f0..5f1b51b77 100644 --- a/poetry.lock +++ b/poetry.lock @@ -35,13 +35,13 @@ httpx-speedups = ["ciso8601 (>=2.3.0)", "httpx"] [[package]] name = "aiofiles" -version = "23.2.1" +version = "24.1.0" description = "File support for asyncio." optional = true -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "aiofiles-23.2.1-py3-none-any.whl", hash = "sha256:19297512c647d4b27a2cf7c34caa7e405c0d60b5560618a29a9fe027b18b0107"}, - {file = "aiofiles-23.2.1.tar.gz", hash = "sha256:84ec2218d8419404abcb9f0c02df3f34c6e0a68ed41072acfb1cef5cbc29051a"}, + {file = "aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5"}, + {file = "aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c"}, ] [[package]] @@ -1142,63 +1142,63 @@ files = [ [[package]] name = "coverage" -version = "7.5.3" +version = "7.5.4" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"}, - {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"}, - {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"}, - {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"}, - {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"}, - {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"}, - {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"}, - {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"}, - {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"}, - {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"}, - {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"}, - {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"}, - {file = "coverage-7.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb"}, - {file = "coverage-7.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0"}, - {file = "coverage-7.5.3-cp38-cp38-win32.whl", hash = "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485"}, - {file = "coverage-7.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56"}, - {file = "coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85"}, - {file = "coverage-7.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd"}, - {file = "coverage-7.5.3-cp39-cp39-win32.whl", hash = "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d"}, - {file = "coverage-7.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0"}, - {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"}, - {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"}, + {file = "coverage-7.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6cfb5a4f556bb51aba274588200a46e4dd6b505fb1a5f8c5ae408222eb416f99"}, + {file = "coverage-7.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2174e7c23e0a454ffe12267a10732c273243b4f2d50d07544a91198f05c48f47"}, + {file = "coverage-7.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2214ee920787d85db1b6a0bd9da5f8503ccc8fcd5814d90796c2f2493a2f4d2e"}, + {file = "coverage-7.5.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1137f46adb28e3813dec8c01fefadcb8c614f33576f672962e323b5128d9a68d"}, + {file = "coverage-7.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b385d49609f8e9efc885790a5a0e89f2e3ae042cdf12958b6034cc442de428d3"}, + {file = "coverage-7.5.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b4a474f799456e0eb46d78ab07303286a84a3140e9700b9e154cfebc8f527016"}, + {file = "coverage-7.5.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5cd64adedf3be66f8ccee418473c2916492d53cbafbfcff851cbec5a8454b136"}, + {file = "coverage-7.5.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e564c2cf45d2f44a9da56f4e3a26b2236504a496eb4cb0ca7221cd4cc7a9aca9"}, + {file = "coverage-7.5.4-cp310-cp310-win32.whl", hash = "sha256:7076b4b3a5f6d2b5d7f1185fde25b1e54eb66e647a1dfef0e2c2bfaf9b4c88c8"}, + {file = "coverage-7.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:018a12985185038a5b2bcafab04ab833a9a0f2c59995b3cec07e10074c78635f"}, + {file = "coverage-7.5.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:db14f552ac38f10758ad14dd7b983dbab424e731588d300c7db25b6f89e335b5"}, + {file = "coverage-7.5.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3257fdd8e574805f27bb5342b77bc65578e98cbc004a92232106344053f319ba"}, + {file = "coverage-7.5.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a6612c99081d8d6134005b1354191e103ec9705d7ba2754e848211ac8cacc6b"}, + {file = "coverage-7.5.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d45d3cbd94159c468b9b8c5a556e3f6b81a8d1af2a92b77320e887c3e7a5d080"}, + {file = "coverage-7.5.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed550e7442f278af76d9d65af48069f1fb84c9f745ae249c1a183c1e9d1b025c"}, + {file = "coverage-7.5.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7a892be37ca35eb5019ec85402c3371b0f7cda5ab5056023a7f13da0961e60da"}, + {file = "coverage-7.5.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8192794d120167e2a64721d88dbd688584675e86e15d0569599257566dec9bf0"}, + {file = "coverage-7.5.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:820bc841faa502e727a48311948e0461132a9c8baa42f6b2b84a29ced24cc078"}, + {file = "coverage-7.5.4-cp311-cp311-win32.whl", hash = "sha256:6aae5cce399a0f065da65c7bb1e8abd5c7a3043da9dceb429ebe1b289bc07806"}, + {file = "coverage-7.5.4-cp311-cp311-win_amd64.whl", hash = "sha256:d2e344d6adc8ef81c5a233d3a57b3c7d5181f40e79e05e1c143da143ccb6377d"}, + {file = "coverage-7.5.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:54317c2b806354cbb2dc7ac27e2b93f97096912cc16b18289c5d4e44fc663233"}, + {file = "coverage-7.5.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:042183de01f8b6d531e10c197f7f0315a61e8d805ab29c5f7b51a01d62782747"}, + {file = "coverage-7.5.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6bb74ed465d5fb204b2ec41d79bcd28afccf817de721e8a807d5141c3426638"}, + {file = "coverage-7.5.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3d45ff86efb129c599a3b287ae2e44c1e281ae0f9a9bad0edc202179bcc3a2e"}, + {file = "coverage-7.5.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5013ed890dc917cef2c9f765c4c6a8ae9df983cd60dbb635df8ed9f4ebc9f555"}, + {file = "coverage-7.5.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1014fbf665fef86cdfd6cb5b7371496ce35e4d2a00cda501cf9f5b9e6fced69f"}, + {file = "coverage-7.5.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3684bc2ff328f935981847082ba4fdc950d58906a40eafa93510d1b54c08a66c"}, + {file = "coverage-7.5.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:581ea96f92bf71a5ec0974001f900db495488434a6928a2ca7f01eee20c23805"}, + {file = "coverage-7.5.4-cp312-cp312-win32.whl", hash = "sha256:73ca8fbc5bc622e54627314c1a6f1dfdd8db69788f3443e752c215f29fa87a0b"}, + {file = "coverage-7.5.4-cp312-cp312-win_amd64.whl", hash = "sha256:cef4649ec906ea7ea5e9e796e68b987f83fa9a718514fe147f538cfeda76d7a7"}, + {file = "coverage-7.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdd31315fc20868c194130de9ee6bfd99755cc9565edff98ecc12585b90be882"}, + {file = "coverage-7.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:02ff6e898197cc1e9fa375581382b72498eb2e6d5fc0b53f03e496cfee3fac6d"}, + {file = "coverage-7.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d05c16cf4b4c2fc880cb12ba4c9b526e9e5d5bb1d81313d4d732a5b9fe2b9d53"}, + {file = "coverage-7.5.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5986ee7ea0795a4095ac4d113cbb3448601efca7f158ec7f7087a6c705304e4"}, + {file = "coverage-7.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5df54843b88901fdc2f598ac06737f03d71168fd1175728054c8f5a2739ac3e4"}, + {file = "coverage-7.5.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ab73b35e8d109bffbda9a3e91c64e29fe26e03e49addf5b43d85fc426dde11f9"}, + {file = "coverage-7.5.4-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:aea072a941b033813f5e4814541fc265a5c12ed9720daef11ca516aeacd3bd7f"}, + {file = "coverage-7.5.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:16852febd96acd953b0d55fc842ce2dac1710f26729b31c80b940b9afcd9896f"}, + {file = "coverage-7.5.4-cp38-cp38-win32.whl", hash = "sha256:8f894208794b164e6bd4bba61fc98bf6b06be4d390cf2daacfa6eca0a6d2bb4f"}, + {file = "coverage-7.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:e2afe743289273209c992075a5a4913e8d007d569a406ffed0bd080ea02b0633"}, + {file = "coverage-7.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b95c3a8cb0463ba9f77383d0fa8c9194cf91f64445a63fc26fb2327e1e1eb088"}, + {file = "coverage-7.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3d7564cc09dd91b5a6001754a5b3c6ecc4aba6323baf33a12bd751036c998be4"}, + {file = "coverage-7.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44da56a2589b684813f86d07597fdf8a9c6ce77f58976727329272f5a01f99f7"}, + {file = "coverage-7.5.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e16f3d6b491c48c5ae726308e6ab1e18ee830b4cdd6913f2d7f77354b33f91c8"}, + {file = "coverage-7.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbc5958cb471e5a5af41b0ddaea96a37e74ed289535e8deca404811f6cb0bc3d"}, + {file = "coverage-7.5.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a04e990a2a41740b02d6182b498ee9796cf60eefe40cf859b016650147908029"}, + {file = "coverage-7.5.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ddbd2f9713a79e8e7242d7c51f1929611e991d855f414ca9996c20e44a895f7c"}, + {file = "coverage-7.5.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b1ccf5e728ccf83acd313c89f07c22d70d6c375a9c6f339233dcf792094bcbf7"}, + {file = "coverage-7.5.4-cp39-cp39-win32.whl", hash = "sha256:56b4eafa21c6c175b3ede004ca12c653a88b6f922494b023aeb1e836df953ace"}, + {file = "coverage-7.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:65e528e2e921ba8fd67d9055e6b9f9e34b21ebd6768ae1c1723f4ea6ace1234d"}, + {file = "coverage-7.5.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:79b356f3dd5b26f3ad23b35c75dbdaf1f9e2450b6bcefc6d0825ea0aa3f86ca5"}, + {file = "coverage-7.5.4.tar.gz", hash = "sha256:a44963520b069e12789d0faea4e9fdb1e410cdc4aab89d94f7f55cbb7fef0353"}, ] [package.dependencies] @@ -1274,33 +1274,33 @@ test-randomorder = ["pytest-randomly"] [[package]] name = "debugpy" -version = "1.8.1" +version = "1.8.2" description = "An implementation of the Debug Adapter Protocol for Python" optional = false python-versions = ">=3.8" files = [ - {file = "debugpy-1.8.1-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:3bda0f1e943d386cc7a0e71bfa59f4137909e2ed947fb3946c506e113000f741"}, - {file = "debugpy-1.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dda73bf69ea479c8577a0448f8c707691152e6c4de7f0c4dec5a4bc11dee516e"}, - {file = "debugpy-1.8.1-cp310-cp310-win32.whl", hash = "sha256:3a79c6f62adef994b2dbe9fc2cc9cc3864a23575b6e387339ab739873bea53d0"}, - {file = "debugpy-1.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:7eb7bd2b56ea3bedb009616d9e2f64aab8fc7000d481faec3cd26c98a964bcdd"}, - {file = "debugpy-1.8.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:016a9fcfc2c6b57f939673c874310d8581d51a0fe0858e7fac4e240c5eb743cb"}, - {file = "debugpy-1.8.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd97ed11a4c7f6d042d320ce03d83b20c3fb40da892f994bc041bbc415d7a099"}, - {file = "debugpy-1.8.1-cp311-cp311-win32.whl", hash = "sha256:0de56aba8249c28a300bdb0672a9b94785074eb82eb672db66c8144fff673146"}, - {file = "debugpy-1.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:1a9fe0829c2b854757b4fd0a338d93bc17249a3bf69ecf765c61d4c522bb92a8"}, - {file = "debugpy-1.8.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3ebb70ba1a6524d19fa7bb122f44b74170c447d5746a503e36adc244a20ac539"}, - {file = "debugpy-1.8.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2e658a9630f27534e63922ebf655a6ab60c370f4d2fc5c02a5b19baf4410ace"}, - {file = "debugpy-1.8.1-cp312-cp312-win32.whl", hash = "sha256:caad2846e21188797a1f17fc09c31b84c7c3c23baf2516fed5b40b378515bbf0"}, - {file = "debugpy-1.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:edcc9f58ec0fd121a25bc950d4578df47428d72e1a0d66c07403b04eb93bcf98"}, - {file = "debugpy-1.8.1-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:7a3afa222f6fd3d9dfecd52729bc2e12c93e22a7491405a0ecbf9e1d32d45b39"}, - {file = "debugpy-1.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d915a18f0597ef685e88bb35e5d7ab968964b7befefe1aaea1eb5b2640b586c7"}, - {file = "debugpy-1.8.1-cp38-cp38-win32.whl", hash = "sha256:92116039b5500633cc8d44ecc187abe2dfa9b90f7a82bbf81d079fcdd506bae9"}, - {file = "debugpy-1.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:e38beb7992b5afd9d5244e96ad5fa9135e94993b0c551ceebf3fe1a5d9beb234"}, - {file = "debugpy-1.8.1-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:bfb20cb57486c8e4793d41996652e5a6a885b4d9175dd369045dad59eaacea42"}, - {file = "debugpy-1.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efd3fdd3f67a7e576dd869c184c5dd71d9aaa36ded271939da352880c012e703"}, - {file = "debugpy-1.8.1-cp39-cp39-win32.whl", hash = "sha256:58911e8521ca0c785ac7a0539f1e77e0ce2df753f786188f382229278b4cdf23"}, - {file = "debugpy-1.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:6df9aa9599eb05ca179fb0b810282255202a66835c6efb1d112d21ecb830ddd3"}, - {file = "debugpy-1.8.1-py2.py3-none-any.whl", hash = "sha256:28acbe2241222b87e255260c76741e1fbf04fdc3b6d094fcf57b6c6f75ce1242"}, - {file = "debugpy-1.8.1.zip", hash = "sha256:f696d6be15be87aef621917585f9bb94b1dc9e8aced570db1b8a6fc14e8f9b42"}, + {file = "debugpy-1.8.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:7ee2e1afbf44b138c005e4380097d92532e1001580853a7cb40ed84e0ef1c3d2"}, + {file = "debugpy-1.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f8c3f7c53130a070f0fc845a0f2cee8ed88d220d6b04595897b66605df1edd6"}, + {file = "debugpy-1.8.2-cp310-cp310-win32.whl", hash = "sha256:f179af1e1bd4c88b0b9f0fa153569b24f6b6f3de33f94703336363ae62f4bf47"}, + {file = "debugpy-1.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:0600faef1d0b8d0e85c816b8bb0cb90ed94fc611f308d5fde28cb8b3d2ff0fe3"}, + {file = "debugpy-1.8.2-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:8a13417ccd5978a642e91fb79b871baded925d4fadd4dfafec1928196292aa0a"}, + {file = "debugpy-1.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acdf39855f65c48ac9667b2801234fc64d46778021efac2de7e50907ab90c634"}, + {file = "debugpy-1.8.2-cp311-cp311-win32.whl", hash = "sha256:2cbd4d9a2fc5e7f583ff9bf11f3b7d78dfda8401e8bb6856ad1ed190be4281ad"}, + {file = "debugpy-1.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:d3408fddd76414034c02880e891ea434e9a9cf3a69842098ef92f6e809d09afa"}, + {file = "debugpy-1.8.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:5d3ccd39e4021f2eb86b8d748a96c766058b39443c1f18b2dc52c10ac2757835"}, + {file = "debugpy-1.8.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62658aefe289598680193ff655ff3940e2a601765259b123dc7f89c0239b8cd3"}, + {file = "debugpy-1.8.2-cp312-cp312-win32.whl", hash = "sha256:bd11fe35d6fd3431f1546d94121322c0ac572e1bfb1f6be0e9b8655fb4ea941e"}, + {file = "debugpy-1.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:15bc2f4b0f5e99bf86c162c91a74c0631dbd9cef3c6a1d1329c946586255e859"}, + {file = "debugpy-1.8.2-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:5a019d4574afedc6ead1daa22736c530712465c0c4cd44f820d803d937531b2d"}, + {file = "debugpy-1.8.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40f062d6877d2e45b112c0bbade9a17aac507445fd638922b1a5434df34aed02"}, + {file = "debugpy-1.8.2-cp38-cp38-win32.whl", hash = "sha256:c78ba1680f1015c0ca7115671fe347b28b446081dada3fedf54138f44e4ba031"}, + {file = "debugpy-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:cf327316ae0c0e7dd81eb92d24ba8b5e88bb4d1b585b5c0d32929274a66a5210"}, + {file = "debugpy-1.8.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:1523bc551e28e15147815d1397afc150ac99dbd3a8e64641d53425dba57b0ff9"}, + {file = "debugpy-1.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e24ccb0cd6f8bfaec68d577cb49e9c680621c336f347479b3fce060ba7c09ec1"}, + {file = "debugpy-1.8.2-cp39-cp39-win32.whl", hash = "sha256:7f8d57a98c5a486c5c7824bc0b9f2f11189d08d73635c326abef268f83950326"}, + {file = "debugpy-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:16c8dcab02617b75697a0a925a62943e26a0330da076e2a10437edd9f0bf3755"}, + {file = "debugpy-1.8.2-py2.py3-none-any.whl", hash = "sha256:16e16df3a98a35c63c3ab1e4d19be4cbc7fdda92d9ddc059294f18910928e0ca"}, + {file = "debugpy-1.8.2.zip", hash = "sha256:95378ed08ed2089221896b9b3a8d021e642c24edc8fef20e5d4342ca8be65c00"}, ] [[package]] @@ -1473,13 +1473,13 @@ pgp = ["gpg"] [[package]] name = "email-validator" -version = "2.1.2" +version = "2.2.0" description = "A robust email address syntax and deliverability validation library." optional = false python-versions = ">=3.8" files = [ - {file = "email_validator-2.1.2-py3-none-any.whl", hash = "sha256:d89f6324e13b1e39889eab7f9ca2f91dc9aebb6fa50a6d8bd4329ab50f251115"}, - {file = "email_validator-2.1.2.tar.gz", hash = "sha256:14c0f3d343c4beda37400421b39fa411bbe33a75df20825df73ad53e06a9f04c"}, + {file = "email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631"}, + {file = "email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7"}, ] [package.dependencies] @@ -1588,13 +1588,13 @@ devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benc [[package]] name = "filelock" -version = "3.15.3" +version = "3.15.4" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.15.3-py3-none-any.whl", hash = "sha256:0151273e5b5d6cf753a61ec83b3a9b7d8821c39ae9af9d7ecf2f9e2f17404103"}, - {file = "filelock-3.15.3.tar.gz", hash = "sha256:e1199bf5194a2277273dacd50269f0d87d0682088a3c561c15674ea9005d8635"}, + {file = "filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7"}, + {file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"}, ] [package.extras] @@ -1821,8 +1821,8 @@ files = [ [package.dependencies] cffi = {version = ">=1.12.2", markers = "platform_python_implementation == \"CPython\" and sys_platform == \"win32\""} greenlet = [ - {version = ">=2.0.0", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""}, {version = ">=3.0rc3", markers = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\""}, + {version = ">=2.0.0", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""}, ] "zope.event" = "*" "zope.interface" = "*" @@ -1991,17 +1991,17 @@ test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", [[package]] name = "googleapis-common-protos" -version = "1.63.1" +version = "1.63.2" description = "Common protobufs used in Google APIs" optional = true python-versions = ">=3.7" files = [ - {file = "googleapis-common-protos-1.63.1.tar.gz", hash = "sha256:c6442f7a0a6b2a80369457d79e6672bb7dcbaab88e0848302497e3ec80780a6a"}, - {file = "googleapis_common_protos-1.63.1-py2.py3-none-any.whl", hash = "sha256:0e1c2cdfcbc354b76e4a211a35ea35d6926a835cba1377073c4861db904a1877"}, + {file = "googleapis-common-protos-1.63.2.tar.gz", hash = "sha256:27c5abdffc4911f28101e635de1533fb4cfd2c37fbaa9174587c799fac90aa87"}, + {file = "googleapis_common_protos-1.63.2-py2.py3-none-any.whl", hash = "sha256:27a2499c7e8aff199665b22741997e485eccc8645aa9176c7c988e6fae507945"}, ] [package.dependencies] -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0.dev0" +protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0.dev0" [package.extras] grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] @@ -2826,13 +2826,13 @@ test = ["jupyter-server (>=2.0.0)", "pytest (>=7.0)", "pytest-jupyter[server] (> [[package]] name = "jupyterlab" -version = "4.2.2" +version = "4.2.3" description = "JupyterLab computational environment" optional = false python-versions = ">=3.8" files = [ - {file = "jupyterlab-4.2.2-py3-none-any.whl", hash = "sha256:59ee9b839f43308c3dfd55d72d1f1a299ed42a7f91f2d1afe9c12a783f9e525f"}, - {file = "jupyterlab-4.2.2.tar.gz", hash = "sha256:a534b6a25719a92a40d514fb133a9fe8f0d9981b0bbce5d8a5fcaa33344a3038"}, + {file = "jupyterlab-4.2.3-py3-none-any.whl", hash = "sha256:0b59d11808e84bb84105c73364edfa867dd475492429ab34ea388a52f2e2e596"}, + {file = "jupyterlab-4.2.3.tar.gz", hash = "sha256:df6e46969ea51d66815167f23d92f105423b7f1f06fa604d4f44aeb018c82c7b"}, ] [package.dependencies] @@ -3176,13 +3176,13 @@ files = [ [[package]] name = "motor" -version = "3.4.0" +version = "3.5.0" description = "Non-blocking MongoDB driver for Tornado or asyncio" optional = true -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "motor-3.4.0-py3-none-any.whl", hash = "sha256:4b1e1a0cc5116ff73be2c080a72da078f2bb719b53bc7a6bb9e9a2f7dcd421ed"}, - {file = "motor-3.4.0.tar.gz", hash = "sha256:c89b4e4eb2e711345e91c7c9b122cb68cce0e5e869ed0387dd0acb10775e3131"}, + {file = "motor-3.5.0-py3-none-any.whl", hash = "sha256:e8f1d7a3370e8dd30eb4c68aaaee46dc608fbac70a757e58f3e828124f5e7693"}, + {file = "motor-3.5.0.tar.gz", hash = "sha256:2b38e405e5a0c52d499edb8d23fa029debdf0158da092c21b44d92cac7f59942"}, ] [package.dependencies] @@ -3190,12 +3190,12 @@ pymongo = ">=4.5,<5" [package.extras] aws = ["pymongo[aws] (>=4.5,<5)"] +docs = ["aiohttp", "readthedocs-sphinx-search (>=0.3,<1.0)", "sphinx (>=5.3,<8)", "sphinx-rtd-theme (>=2,<3)", "tornado"] encryption = ["pymongo[encryption] (>=4.5,<5)"] gssapi = ["pymongo[gssapi] (>=4.5,<5)"] ocsp = ["pymongo[ocsp] (>=4.5,<5)"] snappy = ["pymongo[snappy] (>=4.5,<5)"] -srv = ["pymongo[srv] (>=4.5,<5)"] -test = ["aiohttp (!=3.8.6)", "mockupdb", "motor[encryption]", "pytest (>=7)", "tornado (>=5)"] +test = ["aiohttp (!=3.8.6)", "mockupdb", "pymongo[encryption] (>=4.5,<5)", "pytest (>=7)", "tornado (>=5)"] zstd = ["pymongo[zstd] (>=4.5,<5)"] [[package]] @@ -3364,38 +3364,38 @@ files = [ [[package]] name = "mypy" -version = "1.10.0" +version = "1.10.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da1cbf08fb3b851ab3b9523a884c232774008267b1f83371ace57f412fe308c2"}, - {file = "mypy-1.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:12b6bfc1b1a66095ab413160a6e520e1dc076a28f3e22f7fb25ba3b000b4ef99"}, - {file = "mypy-1.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e36fb078cce9904c7989b9693e41cb9711e0600139ce3970c6ef814b6ebc2b2"}, - {file = "mypy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2b0695d605ddcd3eb2f736cd8b4e388288c21e7de85001e9f85df9187f2b50f9"}, - {file = "mypy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:cd777b780312ddb135bceb9bc8722a73ec95e042f911cc279e2ec3c667076051"}, - {file = "mypy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3be66771aa5c97602f382230165b856c231d1277c511c9a8dd058be4784472e1"}, - {file = "mypy-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b2cbaca148d0754a54d44121b5825ae71868c7592a53b7292eeb0f3fdae95ee"}, - {file = "mypy-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ec404a7cbe9fc0e92cb0e67f55ce0c025014e26d33e54d9e506a0f2d07fe5de"}, - {file = "mypy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e22e1527dc3d4aa94311d246b59e47f6455b8729f4968765ac1eacf9a4760bc7"}, - {file = "mypy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:a87dbfa85971e8d59c9cc1fcf534efe664d8949e4c0b6b44e8ca548e746a8d53"}, - {file = "mypy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a781f6ad4bab20eef8b65174a57e5203f4be627b46291f4589879bf4e257b97b"}, - {file = "mypy-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b808e12113505b97d9023b0b5e0c0705a90571c6feefc6f215c1df9381256e30"}, - {file = "mypy-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f55583b12156c399dce2df7d16f8a5095291354f1e839c252ec6c0611e86e2e"}, - {file = "mypy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cf18f9d0efa1b16478c4c129eabec36148032575391095f73cae2e722fcf9d5"}, - {file = "mypy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc6ac273b23c6b82da3bb25f4136c4fd42665f17f2cd850771cb600bdd2ebeda"}, - {file = "mypy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9fd50226364cd2737351c79807775136b0abe084433b55b2e29181a4c3c878c0"}, - {file = "mypy-1.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f90cff89eea89273727d8783fef5d4a934be2fdca11b47def50cf5d311aff727"}, - {file = "mypy-1.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcfc70599efde5c67862a07a1aaf50e55bce629ace26bb19dc17cece5dd31ca4"}, - {file = "mypy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:075cbf81f3e134eadaf247de187bd604748171d6b79736fa9b6c9685b4083061"}, - {file = "mypy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:3f298531bca95ff615b6e9f2fc0333aae27fa48052903a0ac90215021cdcfa4f"}, - {file = "mypy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa7ef5244615a2523b56c034becde4e9e3f9b034854c93639adb667ec9ec2976"}, - {file = "mypy-1.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3236a4c8f535a0631f85f5fcdffba71c7feeef76a6002fcba7c1a8e57c8be1ec"}, - {file = "mypy-1.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a2b5cdbb5dd35aa08ea9114436e0d79aceb2f38e32c21684dcf8e24e1e92821"}, - {file = "mypy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92f93b21c0fe73dc00abf91022234c79d793318b8a96faac147cd579c1671746"}, - {file = "mypy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:28d0e038361b45f099cc086d9dd99c15ff14d0188f44ac883010e172ce86c38a"}, - {file = "mypy-1.10.0-py3-none-any.whl", hash = "sha256:f8c083976eb530019175aabadb60921e73b4f45736760826aa1689dda8208aee"}, - {file = "mypy-1.10.0.tar.gz", hash = "sha256:3d087fcbec056c4ee34974da493a826ce316947485cef3901f511848e687c131"}, + {file = "mypy-1.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e36f229acfe250dc660790840916eb49726c928e8ce10fbdf90715090fe4ae02"}, + {file = "mypy-1.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:51a46974340baaa4145363b9e051812a2446cf583dfaeba124af966fa44593f7"}, + {file = "mypy-1.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:901c89c2d67bba57aaaca91ccdb659aa3a312de67f23b9dfb059727cce2e2e0a"}, + {file = "mypy-1.10.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0cd62192a4a32b77ceb31272d9e74d23cd88c8060c34d1d3622db3267679a5d9"}, + {file = "mypy-1.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:a2cbc68cb9e943ac0814c13e2452d2046c2f2b23ff0278e26599224cf164e78d"}, + {file = "mypy-1.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bd6f629b67bb43dc0d9211ee98b96d8dabc97b1ad38b9b25f5e4c4d7569a0c6a"}, + {file = "mypy-1.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a1bbb3a6f5ff319d2b9d40b4080d46cd639abe3516d5a62c070cf0114a457d84"}, + {file = "mypy-1.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8edd4e9bbbc9d7b79502eb9592cab808585516ae1bcc1446eb9122656c6066f"}, + {file = "mypy-1.10.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6166a88b15f1759f94a46fa474c7b1b05d134b1b61fca627dd7335454cc9aa6b"}, + {file = "mypy-1.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bb9cd11c01c8606a9d0b83ffa91d0b236a0e91bc4126d9ba9ce62906ada868e"}, + {file = "mypy-1.10.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d8681909f7b44d0b7b86e653ca152d6dff0eb5eb41694e163c6092124f8246d7"}, + {file = "mypy-1.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:378c03f53f10bbdd55ca94e46ec3ba255279706a6aacaecac52ad248f98205d3"}, + {file = "mypy-1.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bacf8f3a3d7d849f40ca6caea5c055122efe70e81480c8328ad29c55c69e93e"}, + {file = "mypy-1.10.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:701b5f71413f1e9855566a34d6e9d12624e9e0a8818a5704d74d6b0402e66c04"}, + {file = "mypy-1.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:3c4c2992f6ea46ff7fce0072642cfb62af7a2484efe69017ed8b095f7b39ef31"}, + {file = "mypy-1.10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:604282c886497645ffb87b8f35a57ec773a4a2721161e709a4422c1636ddde5c"}, + {file = "mypy-1.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37fd87cab83f09842653f08de066ee68f1182b9b5282e4634cdb4b407266bade"}, + {file = "mypy-1.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8addf6313777dbb92e9564c5d32ec122bf2c6c39d683ea64de6a1fd98b90fe37"}, + {file = "mypy-1.10.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5cc3ca0a244eb9a5249c7c583ad9a7e881aa5d7b73c35652296ddcdb33b2b9c7"}, + {file = "mypy-1.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:1b3a2ffce52cc4dbaeee4df762f20a2905aa171ef157b82192f2e2f368eec05d"}, + {file = "mypy-1.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe85ed6836165d52ae8b88f99527d3d1b2362e0cb90b005409b8bed90e9059b3"}, + {file = "mypy-1.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c2ae450d60d7d020d67ab440c6e3fae375809988119817214440033f26ddf7bf"}, + {file = "mypy-1.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6be84c06e6abd72f960ba9a71561c14137a583093ffcf9bbfaf5e613d63fa531"}, + {file = "mypy-1.10.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2189ff1e39db399f08205e22a797383613ce1cb0cb3b13d8bcf0170e45b96cc3"}, + {file = "mypy-1.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:97a131ee36ac37ce9581f4220311247ab6cba896b4395b9c87af0675a13a755f"}, + {file = "mypy-1.10.1-py3-none-any.whl", hash = "sha256:71d8ac0b906354ebda8ef1673e5fde785936ac1f29ff6987c7483cfbd5a4235a"}, + {file = "mypy-1.10.1.tar.gz", hash = "sha256:1f8f492d7db9e3593ef42d4f115f04e556130f2819ad33ab84551403e97dd4c0"}, ] [package.dependencies] @@ -3880,8 +3880,8 @@ files = [ [package.dependencies] numpy = [ - {version = ">=1.20.3", markers = "python_version < \"3.10\""}, {version = ">=1.23.2", markers = "python_version >= \"3.11\""}, + {version = ">=1.20.3", markers = "python_version < \"3.10\""}, {version = ">=1.21.0", markers = "python_version >= \"3.10\" and python_version < \"3.11\""}, ] python-dateutil = ">=2.8.2" @@ -4172,13 +4172,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "poethepoet" -version = "0.26.1" +version = "0.27.0" description = "A task runner that works well with poetry." optional = false python-versions = ">=3.8" files = [ - {file = "poethepoet-0.26.1-py3-none-any.whl", hash = "sha256:aa43b443fec5d17d7e76771cccd484e5285805301721a74f059c483ad3276edd"}, - {file = "poethepoet-0.26.1.tar.gz", hash = "sha256:aaad8541f6072617a60bcff2562d00779b58b353bd0f1847b06d8d0f2b6dc192"}, + {file = "poethepoet-0.27.0-py3-none-any.whl", hash = "sha256:0032d980a623b96e26dc7450ae200b0998be523f27d297d799b97510fe252a24"}, + {file = "poethepoet-0.27.0.tar.gz", hash = "sha256:907ab4dc1bc6326be5a3b10d2aa39d1acc0ca12024317d9506fbe9c0cdc912c9"}, ] [package.dependencies] @@ -4628,71 +4628,61 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pymongo" -version = "4.7.3" +version = "4.8.0" description = "Python driver for MongoDB " optional = true -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pymongo-4.7.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e9580b4537b3cc5d412070caabd1dabdf73fdce249793598792bac5782ecf2eb"}, - {file = "pymongo-4.7.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:517243b2b189c98004570dd8fc0e89b1a48363d5578b3b99212fa2098b2ea4b8"}, - {file = "pymongo-4.7.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23b1e9dabd61da1c7deb54d888f952f030e9e35046cebe89309b28223345b3d9"}, - {file = "pymongo-4.7.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03e0f9901ad66c6fb7da0d303461377524d61dab93a4e4e5af44164c5bb4db76"}, - {file = "pymongo-4.7.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9a870824aa54453aee030bac08c77ebcf2fe8999400f0c2a065bebcbcd46b7f8"}, - {file = "pymongo-4.7.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfd7b3d3f4261bddbb74a332d87581bc523353e62bb9da4027cc7340f6fcbebc"}, - {file = "pymongo-4.7.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4d719a643ea6da46d215a3ba51dac805a773b611c641319558d8576cbe31cef8"}, - {file = "pymongo-4.7.3-cp310-cp310-win32.whl", hash = "sha256:d8b1e06f361f3c66ee694cb44326e1a2e4f93bc9c3a4849ae8547889fca71154"}, - {file = "pymongo-4.7.3-cp310-cp310-win_amd64.whl", hash = "sha256:c450ab2f9397e2d5caa7fddeb4feb30bf719c47c13ae02c0bbb3b71bf4099c1c"}, - {file = "pymongo-4.7.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79cc6459209e885ba097779eaa0fe7f2fa049db39ab43b1731cf8d065a4650e8"}, - {file = "pymongo-4.7.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6e2287f1e2cc35e73cd74a4867e398a97962c5578a3991c730ef78d276ca8e46"}, - {file = "pymongo-4.7.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:413506bd48d8c31ee100645192171e4773550d7cb940b594d5175ac29e329ea1"}, - {file = "pymongo-4.7.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cc1febf17646d52b7561caa762f60bdfe2cbdf3f3e70772f62eb624269f9c05"}, - {file = "pymongo-4.7.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8dfcf18a49955d50a16c92b39230bd0668ffc9c164ccdfe9d28805182b48fa72"}, - {file = "pymongo-4.7.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89872041196c008caddf905eb59d3dc2d292ae6b0282f1138418e76f3abd3ad6"}, - {file = "pymongo-4.7.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3ed97b89de62ea927b672ad524de0d23f3a6b4a01c8d10e3d224abec973fbc3"}, - {file = "pymongo-4.7.3-cp311-cp311-win32.whl", hash = "sha256:d2f52b38151e946011d888a8441d3d75715c663fc5b41a7ade595e924e12a90a"}, - {file = "pymongo-4.7.3-cp311-cp311-win_amd64.whl", hash = "sha256:4a4cc91c28e81c0ce03d3c278e399311b0af44665668a91828aec16527082676"}, - {file = "pymongo-4.7.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cb30c8a78f5ebaca98640943447b6a0afcb146f40b415757c9047bf4a40d07b4"}, - {file = "pymongo-4.7.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9cf2069f5d37c398186453589486ea98bb0312214c439f7d320593b61880dc05"}, - {file = "pymongo-4.7.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3564f423958fced8a8c90940fd2f543c27adbcd6c7c6ed6715d847053f6200a0"}, - {file = "pymongo-4.7.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7a8af8a38fa6951fff73e6ff955a6188f829b29fed7c5a1b739a306b4aa56fe8"}, - {file = "pymongo-4.7.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a0e81c8dba6d825272867d487f18764cfed3c736d71d7d4ff5b79642acbed42"}, - {file = "pymongo-4.7.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88fc1d146feabac4385ea8ddb1323e584922922641303c8bf392fe1c36803463"}, - {file = "pymongo-4.7.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4225100b2c5d1f7393d7c5d256ceb8b20766830eecf869f8ae232776347625a6"}, - {file = "pymongo-4.7.3-cp312-cp312-win32.whl", hash = "sha256:5f3569ed119bf99c0f39ac9962fb5591eff02ca210fe80bb5178d7a1171c1b1e"}, - {file = "pymongo-4.7.3-cp312-cp312-win_amd64.whl", hash = "sha256:eb383c54c0c8ba27e7712b954fcf2a0905fee82a929d277e2e94ad3a5ba3c7db"}, - {file = "pymongo-4.7.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a46cffe91912570151617d866a25d07b9539433a32231ca7e7cf809b6ba1745f"}, - {file = "pymongo-4.7.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c3cba427dac50944c050c96d958c5e643c33a457acee03bae27c8990c5b9c16"}, - {file = "pymongo-4.7.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7a5fd893edbeb7fa982f8d44b6dd0186b6cd86c89e23f6ef95049ff72bffe46"}, - {file = "pymongo-4.7.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c168a2fadc8b19071d0a9a4f85fe38f3029fe22163db04b4d5c046041c0b14bd"}, - {file = "pymongo-4.7.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c59c2c9e70f63a7f18a31e367898248c39c068c639b0579623776f637e8f482"}, - {file = "pymongo-4.7.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d08165fd82c89d372e82904c3268bd8fe5de44f92a00e97bb1db1785154397d9"}, - {file = "pymongo-4.7.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:397fed21afec4fdaecf72f9c4344b692e489756030a9c6d864393e00c7e80491"}, - {file = "pymongo-4.7.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:f903075f8625e2d228f1b9b9a0cf1385f1c41e93c03fd7536c91780a0fb2e98f"}, - {file = "pymongo-4.7.3-cp37-cp37m-win32.whl", hash = "sha256:8ed1132f58c38add6b6138b771d0477a3833023c015c455d9a6e26f367f9eb5c"}, - {file = "pymongo-4.7.3-cp37-cp37m-win_amd64.whl", hash = "sha256:8d00a5d8fc1043a4f641cbb321da766699393f1b6f87c70fae8089d61c9c9c54"}, - {file = "pymongo-4.7.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9377b868c38700c7557aac1bc4baae29f47f1d279cc76b60436e547fd643318c"}, - {file = "pymongo-4.7.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:da4a6a7b4f45329bb135aa5096823637bd5f760b44d6224f98190ee367b6b5dd"}, - {file = "pymongo-4.7.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:487e2f9277f8a63ac89335ec4f1699ae0d96ebd06d239480d69ed25473a71b2c"}, - {file = "pymongo-4.7.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db3d608d541a444c84f0bfc7bad80b0b897e0f4afa580a53f9a944065d9b633"}, - {file = "pymongo-4.7.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e90af2ad3a8a7c295f4d09a2fbcb9a350c76d6865f787c07fe843b79c6e821d1"}, - {file = "pymongo-4.7.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e28feb18dc559d50ededba27f9054c79f80c4edd70a826cecfe68f3266807b3"}, - {file = "pymongo-4.7.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f21ecddcba2d9132d5aebd8e959de8d318c29892d0718420447baf2b9bccbb19"}, - {file = "pymongo-4.7.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:26140fbb3f6a9a74bd73ed46d0b1f43d5702e87a6e453a31b24fad9c19df9358"}, - {file = "pymongo-4.7.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:94baa5fc7f7d22c3ce2ac7bd92f7e03ba7a6875f2480e3b97a400163d6eaafc9"}, - {file = "pymongo-4.7.3-cp38-cp38-win32.whl", hash = "sha256:92dd247727dd83d1903e495acc743ebd757f030177df289e3ba4ef8a8c561fad"}, - {file = "pymongo-4.7.3-cp38-cp38-win_amd64.whl", hash = "sha256:1c90c848a5e45475731c35097f43026b88ef14a771dfd08f20b67adc160a3f79"}, - {file = "pymongo-4.7.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f598be401b416319a535c386ac84f51df38663f7a9d1071922bda4d491564422"}, - {file = "pymongo-4.7.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:35ba90477fae61c65def6e7d09e8040edfdd3b7fd47c3c258b4edded60c4d625"}, - {file = "pymongo-4.7.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9aa8735955c70892634d7e61b0ede9b1eefffd3cd09ccabee0ffcf1bdfe62254"}, - {file = "pymongo-4.7.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:82a97d8f7f138586d9d0a0cff804a045cdbbfcfc1cd6bba542b151e284fbbec5"}, - {file = "pymongo-4.7.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de3b9db558930efab5eaef4db46dcad8bf61ac3ddfd5751b3e5ac6084a25e366"}, - {file = "pymongo-4.7.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0e149217ef62812d3c2401cf0e2852b0c57fd155297ecc4dcd67172c4eca402"}, - {file = "pymongo-4.7.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3a8a1ef4a824f5feb793b3231526d0045eadb5eb01080e38435dfc40a26c3e5"}, - {file = "pymongo-4.7.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d14e5e89a4be1f10efc3d9dcb13eb7a3b2334599cb6bb5d06c6a9281b79c8e22"}, - {file = "pymongo-4.7.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c6bfa29f032fd4fd7b129520f8cdb51ab71d88c2ba0567cccd05d325f963acb5"}, - {file = "pymongo-4.7.3-cp39-cp39-win32.whl", hash = "sha256:1421d0bd2ce629405f5157bd1aaa9b83f12d53a207cf68a43334f4e4ee312b66"}, - {file = "pymongo-4.7.3-cp39-cp39-win_amd64.whl", hash = "sha256:f7ee974f8b9370a998919c55b1050889f43815ab588890212023fecbc0402a6d"}, - {file = "pymongo-4.7.3.tar.gz", hash = "sha256:6354a66b228f2cd399be7429685fb68e07f19110a3679782ecb4fdb68da03831"}, + {file = "pymongo-4.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f2b7bec27e047e84947fbd41c782f07c54c30c76d14f3b8bf0c89f7413fac67a"}, + {file = "pymongo-4.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3c68fe128a171493018ca5c8020fc08675be130d012b7ab3efe9e22698c612a1"}, + {file = "pymongo-4.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:920d4f8f157a71b3cb3f39bc09ce070693d6e9648fb0e30d00e2657d1dca4e49"}, + {file = "pymongo-4.8.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52b4108ac9469febba18cea50db972605cc43978bedaa9fea413378877560ef8"}, + {file = "pymongo-4.8.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:180d5eb1dc28b62853e2f88017775c4500b07548ed28c0bd9c005c3d7bc52526"}, + {file = "pymongo-4.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aec2b9088cdbceb87e6ca9c639d0ff9b9d083594dda5ca5d3c4f6774f4c81b33"}, + {file = "pymongo-4.8.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0cf61450feadca81deb1a1489cb1a3ae1e4266efd51adafecec0e503a8dcd84"}, + {file = "pymongo-4.8.0-cp310-cp310-win32.whl", hash = "sha256:8b18c8324809539c79bd6544d00e0607e98ff833ca21953df001510ca25915d1"}, + {file = "pymongo-4.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e5df28f74002e37bcbdfdc5109799f670e4dfef0fb527c391ff84f078050e7b5"}, + {file = "pymongo-4.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6b50040d9767197b77ed420ada29b3bf18a638f9552d80f2da817b7c4a4c9c68"}, + {file = "pymongo-4.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:417369ce39af2b7c2a9c7152c1ed2393edfd1cbaf2a356ba31eb8bcbd5c98dd7"}, + {file = "pymongo-4.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf821bd3befb993a6db17229a2c60c1550e957de02a6ff4dd0af9476637b2e4d"}, + {file = "pymongo-4.8.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9365166aa801c63dff1a3cb96e650be270da06e3464ab106727223123405510f"}, + {file = "pymongo-4.8.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cc8b8582f4209c2459b04b049ac03c72c618e011d3caa5391ff86d1bda0cc486"}, + {file = "pymongo-4.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e5019f75f6827bb5354b6fef8dfc9d6c7446894a27346e03134d290eb9e758"}, + {file = "pymongo-4.8.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b5802151fc2b51cd45492c80ed22b441d20090fb76d1fd53cd7760b340ff554"}, + {file = "pymongo-4.8.0-cp311-cp311-win32.whl", hash = "sha256:4bf58e6825b93da63e499d1a58de7de563c31e575908d4e24876234ccb910eba"}, + {file = "pymongo-4.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:b747c0e257b9d3e6495a018309b9e0c93b7f0d65271d1d62e572747f4ffafc88"}, + {file = "pymongo-4.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e6a720a3d22b54183352dc65f08cd1547204d263e0651b213a0a2e577e838526"}, + {file = "pymongo-4.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:31e4d21201bdf15064cf47ce7b74722d3e1aea2597c6785882244a3bb58c7eab"}, + {file = "pymongo-4.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6b804bb4f2d9dc389cc9e827d579fa327272cdb0629a99bfe5b83cb3e269ebf"}, + {file = "pymongo-4.8.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f2fbdb87fe5075c8beb17a5c16348a1ea3c8b282a5cb72d173330be2fecf22f5"}, + {file = "pymongo-4.8.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd39455b7ee70aabee46f7399b32ab38b86b236c069ae559e22be6b46b2bbfc4"}, + {file = "pymongo-4.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:940d456774b17814bac5ea7fc28188c7a1338d4a233efbb6ba01de957bded2e8"}, + {file = "pymongo-4.8.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:236bbd7d0aef62e64caf4b24ca200f8c8670d1a6f5ea828c39eccdae423bc2b2"}, + {file = "pymongo-4.8.0-cp312-cp312-win32.whl", hash = "sha256:47ec8c3f0a7b2212dbc9be08d3bf17bc89abd211901093e3ef3f2adea7de7a69"}, + {file = "pymongo-4.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:e84bc7707492f06fbc37a9f215374d2977d21b72e10a67f1b31893ec5a140ad8"}, + {file = "pymongo-4.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:519d1bab2b5e5218c64340b57d555d89c3f6c9d717cecbf826fb9d42415e7750"}, + {file = "pymongo-4.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:87075a1feb1e602e539bdb1ef8f4324a3427eb0d64208c3182e677d2c0718b6f"}, + {file = "pymongo-4.8.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f53429515d2b3e86dcc83dadecf7ff881e538c168d575f3688698a8707b80a"}, + {file = "pymongo-4.8.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fdc20cd1e1141b04696ffcdb7c71e8a4a665db31fe72e51ec706b3bdd2d09f36"}, + {file = "pymongo-4.8.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:284d0717d1a7707744018b0b6ee7801b1b1ff044c42f7be7a01bb013de639470"}, + {file = "pymongo-4.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5bf0eb8b6ef40fa22479f09375468c33bebb7fe49d14d9c96c8fd50355188b0"}, + {file = "pymongo-4.8.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ecd71b9226bd1d49416dc9f999772038e56f415a713be51bf18d8676a0841c8"}, + {file = "pymongo-4.8.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0061af6e8c5e68b13f1ec9ad5251247726653c5af3c0bbdfbca6cf931e99216"}, + {file = "pymongo-4.8.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:658d0170f27984e0d89c09fe5c42296613b711a3ffd847eb373b0dbb5b648d5f"}, + {file = "pymongo-4.8.0-cp38-cp38-win32.whl", hash = "sha256:3ed1c316718a2836f7efc3d75b4b0ffdd47894090bc697de8385acd13c513a70"}, + {file = "pymongo-4.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:7148419eedfea9ecb940961cfe465efaba90595568a1fb97585fb535ea63fe2b"}, + {file = "pymongo-4.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e8400587d594761e5136a3423111f499574be5fd53cf0aefa0d0f05b180710b0"}, + {file = "pymongo-4.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af3e98dd9702b73e4e6fd780f6925352237f5dce8d99405ff1543f3771201704"}, + {file = "pymongo-4.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de3a860f037bb51f968de320baef85090ff0bbb42ec4f28ec6a5ddf88be61871"}, + {file = "pymongo-4.8.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0fc18b3a093f3db008c5fea0e980dbd3b743449eee29b5718bc2dc15ab5088bb"}, + {file = "pymongo-4.8.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18c9d8f975dd7194c37193583fd7d1eb9aea0c21ee58955ecf35362239ff31ac"}, + {file = "pymongo-4.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:408b2f8fdbeca3c19e4156f28fff1ab11c3efb0407b60687162d49f68075e63c"}, + {file = "pymongo-4.8.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6564780cafd6abeea49759fe661792bd5a67e4f51bca62b88faab497ab5fe89"}, + {file = "pymongo-4.8.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d18d86bc9e103f4d3d4f18b85a0471c0e13ce5b79194e4a0389a224bb70edd53"}, + {file = "pymongo-4.8.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9097c331577cecf8034422956daaba7ec74c26f7b255d718c584faddd7fa2e3c"}, + {file = "pymongo-4.8.0-cp39-cp39-win32.whl", hash = "sha256:d5428dbcd43d02f6306e1c3c95f692f68b284e6ee5390292242f509004c9e3a8"}, + {file = "pymongo-4.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:ef7225755ed27bfdb18730c68f6cb023d06c28f2b734597480fb4c0e500feb6f"}, + {file = "pymongo-4.8.0.tar.gz", hash = "sha256:454f2295875744dc70f1881e4b2eb99cdad008a33574bc8aaf120530f66c0cde"}, ] [package.dependencies] @@ -4700,6 +4690,7 @@ dnspython = ">=1.16.0,<3.0.0" [package.extras] aws = ["pymongo-auth-aws (>=1.1.0,<2.0.0)"] +docs = ["furo (==2023.9.10)", "readthedocs-sphinx-search (>=0.3,<1.0)", "sphinx (>=5.3,<8)", "sphinx-rtd-theme (>=2,<3)", "sphinxcontrib-shellcheck (>=1,<2)"] encryption = ["certifi", "pymongo-auth-aws (>=1.1.0,<2.0.0)", "pymongocrypt (>=1.6.0,<2.0.0)"] gssapi = ["pykerberos", "winkerberos (>=0.5.0)"] ocsp = ["certifi", "cryptography (>=2.5)", "pyopenssl (>=17.2.0)", "requests (<3.0.0)", "service-identity (>=18.1.0)"] @@ -5325,13 +5316,13 @@ full = ["numpy"] [[package]] name = "redis" -version = "5.0.6" +version = "5.0.7" description = "Python client for Redis database and key-value store" optional = true python-versions = ">=3.7" files = [ - {file = "redis-5.0.6-py3-none-any.whl", hash = "sha256:c0d6d990850c627bbf7be01c5c4cbaadf67b48593e913bb71c9819c30df37eee"}, - {file = "redis-5.0.6.tar.gz", hash = "sha256:38473cd7c6389ad3e44a91f4c3eaf6bcb8a9f746007f29bf4fb20824ff0b2197"}, + {file = "redis-5.0.7-py3-none-any.whl", hash = "sha256:0e479e24da960c690be5d9b96d21f7b918a98c0cf49af3b6fafaa0753f93a0db"}, + {file = "redis-5.0.7.tar.gz", hash = "sha256:8f611490b93c8109b50adc317b31bfd84fff31def3475b92e7e80bf39f48175b"}, ] [package.dependencies] @@ -5600,13 +5591,13 @@ win32 = ["pywin32"] [[package]] name = "setuptools" -version = "70.1.0" +version = "70.1.1" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-70.1.0-py3-none-any.whl", hash = "sha256:d9b8b771455a97c8a9f3ab3448ebe0b29b5e105f1228bba41028be116985a267"}, - {file = "setuptools-70.1.0.tar.gz", hash = "sha256:01a1e793faa5bd89abc851fa15d0a0db26f160890c7102cd8dce643e886b47f5"}, + {file = "setuptools-70.1.1-py3-none-any.whl", hash = "sha256:a58a8fde0541dab0419750bcc521fbdf8585f6e5cb41909df3a472ef7b81ca95"}, + {file = "setuptools-70.1.1.tar.gz", hash = "sha256:937a48c7cdb7a21eb53cd7f9b59e525503aa8abaf3584c730dc5f7a5bec3a650"}, ] [package.extras] @@ -6076,13 +6067,13 @@ full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7 [[package]] name = "streamlit" -version = "1.35.0" +version = "1.36.0" description = "A faster way to build and share data apps" optional = false python-versions = "!=3.9.7,>=3.8" files = [ - {file = "streamlit-1.35.0-py2.py3-none-any.whl", hash = "sha256:e17d1d86830a0d7687c37faf2fe47bffa752d0c95a306e96d7749bd3faa72a5b"}, - {file = "streamlit-1.35.0.tar.gz", hash = "sha256:679d55bb6189743f606abf0696623df0bfd223a6d0c8d96b8d60678d4891d2d6"}, + {file = "streamlit-1.36.0-py2.py3-none-any.whl", hash = "sha256:3399a33ea5faa26c05dd433d142eefe68ade67e9189a9e1d47a1731ae30a1c42"}, + {file = "streamlit-1.36.0.tar.gz", hash = "sha256:a12af9f0eb61ab5832f438336257b1ec20eb29d8e0e0c6b40a79116ba939bc9c"}, ] [package.dependencies] @@ -6091,11 +6082,11 @@ blinker = ">=1.0.0,<2" cachetools = ">=4.0,<6" click = ">=7.0,<9" gitpython = ">=3.0.7,<3.1.19 || >3.1.19,<4" -numpy = ">=1.19.3,<2" -packaging = ">=16.8,<25" +numpy = ">=1.20,<3" +packaging = ">=20,<25" pandas = ">=1.3.0,<3" pillow = ">=7.1.0,<11" -protobuf = ">=3.20,<5" +protobuf = ">=3.20,<6" pyarrow = ">=7.0" pydeck = ">=0.8.0b4,<1" requests = ">=2.27,<3" @@ -6104,7 +6095,7 @@ tenacity = ">=8.1.0,<9" toml = ">=0.10.1,<2" tornado = ">=6.0.3,<7" typing-extensions = ">=4.3.0,<5" -watchdog = {version = ">=2.1.5", markers = "platform_system != \"Darwin\""} +watchdog = {version = ">=2.1.5,<5", markers = "platform_system != \"Darwin\""} [package.extras] snowflake = ["snowflake-connector-python (>=2.8.0)", "snowflake-snowpark-python (>=0.9.0)"] @@ -6142,13 +6133,13 @@ cryptg = ["cryptg"] [[package]] name = "tenacity" -version = "8.4.1" +version = "8.4.2" description = "Retry code until it succeeds" optional = false python-versions = ">=3.8" files = [ - {file = "tenacity-8.4.1-py3-none-any.whl", hash = "sha256:28522e692eda3e1b8f5e99c51464efcc0b9fc86933da92415168bc1c4e2308fa"}, - {file = "tenacity-8.4.1.tar.gz", hash = "sha256:54b1412b878ddf7e1f1577cd49527bad8cdef32421bd599beac0c6c3f10582fd"}, + {file = "tenacity-8.4.2-py3-none-any.whl", hash = "sha256:9e6f7cf7da729125c7437222f8a522279751cdfbe6b67bfe64f75d3a348661b2"}, + {file = "tenacity-8.4.2.tar.gz", hash = "sha256:cd80a53a79336edba8489e767f729e4f391c896956b57140b5d7511a64bbd3ef"}, ] [package.extras] @@ -6575,13 +6566,13 @@ test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)" [[package]] name = "virtualenv" -version = "20.26.2" +version = "20.26.3" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.26.2-py3-none-any.whl", hash = "sha256:a624db5e94f01ad993d476b9ee5346fdf7b9de43ccaee0e0197012dc838a0e9b"}, - {file = "virtualenv-20.26.2.tar.gz", hash = "sha256:82bf0f4eebbb78d36ddaee0283d43fe5736b53880b8a8cdcd37390a07ac3741c"}, + {file = "virtualenv-20.26.3-py3-none-any.whl", hash = "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589"}, + {file = "virtualenv-20.26.3.tar.gz", hash = "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a"}, ] [package.dependencies] @@ -7143,13 +7134,13 @@ multidict = ">=4.0" [[package]] name = "ydb" -version = "3.12.2" +version = "3.12.3" description = "YDB Python SDK" optional = true python-versions = "*" files = [ - {file = "ydb-3.12.2-py2.py3-none-any.whl", hash = "sha256:41256a3221e5659cb4bcbbc89a92057f0c08d05a777a15fbd3837ed0251be3fb"}, - {file = "ydb-3.12.2.tar.gz", hash = "sha256:2e4b51c1a0293adaf8b21af91348fe3bdcd3504b4ba54c89f1d95f2e6c15321f"}, + {file = "ydb-3.12.3-py2.py3-none-any.whl", hash = "sha256:0bb1094d471c47c3da773dc607ae47129899becdcca5756d199e343140599799"}, + {file = "ydb-3.12.3.tar.gz", hash = "sha256:6895e97218d464cb6e46fedebb8e855e385740e61bd700fd26983c9daeb9ba74"}, ] [package.dependencies] diff --git a/tests/messengers/telegram/test_happy_paths.json b/tests/messengers/telegram/test_happy_paths.json index ecb04d199..f0f1090cd 100644 --- a/tests/messengers/telegram/test_happy_paths.json +++ b/tests/messengers/telegram/test_happy_paths.json @@ -5,7 +5,7 @@ "received_message": "{\"text\":\"/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Hi\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_message(42, 'Hi', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" + "send_message(42, 'Hi', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" ] }, { @@ -13,7 +13,7 @@ "received_message": "{\"text\":\"Hi\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Hi\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_message(42, 'Hi', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" + "send_message(42, 'Hi', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" ] } ], @@ -23,7 +23,7 @@ "received_message": "{\"text\":\"/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Type \\\"location\\\", \\\"contact\\\", \\\"poll\\\", \\\"sticker\\\" \\\"audio\\\", \\\"video\\\", \\\"animation\\\", \\\"image\\\", \\\"document\\\" or to receive a corresponding attachment!\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_message(42, 'Type \"location\", \"contact\", \"poll\", \"sticker\" \"audio\", \"video\", \"animation\", \"image\", \"document\" or to receive a corresponding attachment!', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" + "send_message(42, 'Type \"location\", \"contact\", \"poll\", \"sticker\" \"audio\", \"video\", \"animation\", \"image\", \"document\" or to receive a corresponding attachment!', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" ] }, { @@ -31,8 +31,8 @@ "received_message": "{\"text\":\"location\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your location!\",\"commands\":null,\"attachments\":[{\"longitude\":3.916667,\"latitude\":50.65,\"dff_attachment_type\":\"location\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_location(42, 50.65, 3.916667, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)", - "send_message(42, \"Here's your location!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" + "send_location(42, 50.65, 3.916667, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None)", + "send_message(42, \"Here's your location!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" ] }, { @@ -40,8 +40,8 @@ "received_message": "{\"text\":\"contact\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your contact!\",\"commands\":null,\"attachments\":[{\"phone_number\":\"8-900-555-35-35\",\"first_name\":\"Hope\",\"last_name\":\"Credit\",\"dff_attachment_type\":\"contact\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_contact(42, '8-900-555-35-35', 'Hope', 'Credit', vcard=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)", - "send_message(42, \"Here's your contact!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" + "send_contact(42, '8-900-555-35-35', 'Hope', 'Credit', vcard=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None)", + "send_message(42, \"Here's your contact!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" ] }, { @@ -49,8 +49,8 @@ "received_message": "{\"text\":\"poll\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your poll!\",\"commands\":null,\"attachments\":[{\"question\":\"What is the poll question?\",\"options\":[{\"text\":\"This one!\",\"votes\":0,\"dff_attachment_type\":\"poll_option\"},{\"text\":\"Not this one :(\",\"votes\":0,\"dff_attachment_type\":\"poll_option\"}],\"dff_attachment_type\":\"poll\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_poll(42, 'What is the poll question?', ['This one!', 'Not this one :('], is_anonymous=None, type=None, allows_multiple_answers=None, correct_option_id=None, explanation=None, explanation_parse_mode=None, open_period=None, is_closed=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)", - "send_message(42, \"Here's your poll!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" + "send_poll(42, 'What is the poll question?', ['This one!', 'Not this one :('], is_anonymous=None, type=None, allows_multiple_answers=None, correct_option_id=None, explanation=None, explanation_parse_mode=None, open_period=None, is_closed=None, disable_notification=None, protect_content=None, reply_markup=None, question_parse_mode=None, message_effect_id=None, reply_to_message_id=None)", + "send_message(42, \"Here's your poll!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" ] }, { @@ -58,8 +58,8 @@ "received_message": "{\"text\":\"sticker\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your sticker!\",\"commands\":null,\"attachments\":[{\"source\":null,\"cached_filename\":null,\"id\":\"CAACAgIAAxkBAAErAAFXZibO5ksphCKSXSe1CYiw5588yqsAAkEAAzyKVxogmx2BPCogYDQE\",\"title\":\"A sticker I've just found\",\"use_cache\":true,\"dff_attachment_type\":\"sticker\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_sticker(42, 'CAACAgIAAxkBAAErAAFXZibO5ksphCKSXSe1CYiw5588yqsAAkEAAzyKVxogmx2BPCogYDQE', disable_notification=None, protect_content=None, reply_markup=None, emoji=None, message_effect_id=None)", - "send_message(42, \"Here's your sticker!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" + "send_sticker(42, 'CAACAgIAAxkBAAErAAFXZibO5ksphCKSXSe1CYiw5588yqsAAkEAAzyKVxogmx2BPCogYDQE', emoji=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None)", + "send_message(42, \"Here's your sticker!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" ] }, { @@ -67,7 +67,8 @@ "received_message": "{\"text\":\"audio\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your audio!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/separation-william-king.mp3\",\"cached_filename\":null,\"id\":null,\"title\":\"Separation melody by William King\",\"use_cache\":true,\"dff_attachment_type\":\"audio\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_audio(42, '75ff19e65eef262114e1a6d93794ed8b4af6135a0cd0620f58023fd5b57204f4', caption=\"Here's your audio!\", performer=None, title=None, disable_notification=None, reply_markup=None, parse_mode=None, thumbnail=None, message_effect_id=None)" + "send_audio(42, '75ff19e65eef262114e1a6d93794ed8b4af6135a0cd0620f58023fd5b57204f4', caption=None, parse_mode=None, performer=None, title=None, disable_notification=None, protect_content=None, reply_markup=None, thumbnail=None, message_effect_id=None, reply_to_message_id=None, filename=None)", + "send_message(42, \"Here's your audio!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" ] }, { @@ -75,7 +76,8 @@ "received_message": "{\"text\":\"video\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your video!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/crownfall-lags-nkognit0.mp4\",\"cached_filename\":null,\"id\":null,\"title\":\"Epic Dota2 gameplay by Nkognit0\",\"use_cache\":true,\"dff_attachment_type\":\"video\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_video(42, '11d3472ad8534d632beca6a452e2dc03b7ec9f4aba19fbb3346da43bac7389d6', caption=\"Here's your video!\", disable_notification=None, reply_markup=None, parse_mode=None, supports_streaming=None, has_spoiler=None, thumbnail=None, filename=None, message_effect_id=None)" + "send_video(42, '11d3472ad8534d632beca6a452e2dc03b7ec9f4aba19fbb3346da43bac7389d6', caption=None, parse_mode=None, supports_streaming=None, disable_notification=None, protect_content=None, reply_markup=None, has_spoiler=None, thumbnail=None, message_effect_id=None, show_caption_above_media=None, reply_to_message_id=None, filename=None)", + "send_message(42, \"Here's your video!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" ] }, { @@ -83,7 +85,8 @@ "received_message": "{\"text\":\"animation\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your animation!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/hong-kong-simplyart4794.gif\",\"cached_filename\":null,\"id\":null,\"title\":\"Hong Kong skyscraper views by Simplyart4794\",\"use_cache\":true,\"dff_attachment_type\":\"animation\",\"filename\":\"hk.gif\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_animation(42, '033391bab1eaefd6d8aa6ceb667b5dbaada7c4d6b1649a726541924a74539dc5', caption=\"Here's your animation!\", parse_mode=None, disable_notification=None, reply_markup=None, has_spoiler=None, thumbnail=None, filename='hk.gif', message_effect_id=None)" + "send_animation(42, '033391bab1eaefd6d8aa6ceb667b5dbaada7c4d6b1649a726541924a74539dc5', caption=None, parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, has_spoiler=None, thumbnail=None, message_effect_id=None, show_caption_above_media=None, reply_to_message_id=None, filename='hk.gif')", + "send_message(42, \"Here's your animation!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" ] }, { @@ -91,7 +94,8 @@ "received_message": "{\"text\":\"image\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your image!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/deeppavlov.png\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov logo\",\"use_cache\":true,\"dff_attachment_type\":\"image\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_photo(42, '9f39f64560e6415032325cae1fec1ca06b3cc1a3549208a8a35564c0d3749062', caption=\"Here's your image!\", disable_notification=None, reply_markup=None, parse_mode=None, has_spoiler=None, filename=None, message_effect_id=None)" + "send_photo(42, '9f39f64560e6415032325cae1fec1ca06b3cc1a3549208a8a35564c0d3749062', caption=None, parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, has_spoiler=None, message_effect_id=None, reply_to_message_id=None, filename=None)", + "send_message(42, \"Here's your image!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" ] }, { @@ -99,7 +103,8 @@ "received_message": "{\"text\":\"document\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your document!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/deeppavlov-article.pdf\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov article\",\"use_cache\":true,\"dff_attachment_type\":\"document\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_document(42, '08a7a36671950baba722d94a87dea286f03d7a6f7d0c32d5707d877cd211739a', caption=\"Here's your document!\", disable_notification=None, reply_markup=None, parse_mode=None, thumbnail=None, filename=None, message_effect_id=None)" + "send_document(42, '08a7a36671950baba722d94a87dea286f03d7a6f7d0c32d5707d877cd211739a', caption=None, parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, thumbnail=None, message_effect_id=None, reply_to_message_id=None, filename=None)", + "send_message(42, \"Here's your document!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" ] }, { @@ -107,7 +112,8 @@ "received_message": "{\"text\":\"voice message\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your voice message!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/separation-william-king.mp3\",\"cached_filename\":null,\"id\":null,\"title\":null,\"use_cache\":true,\"dff_attachment_type\":\"voice_message\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_voice(42, '75ff19e65eef262114e1a6d93794ed8b4af6135a0cd0620f58023fd5b57204f4', caption=\"Here's your voice message!\", disable_notification=None, reply_markup=None, parse_mode=None, filename=None, protect_content=None, message_effect_id=None)" + "send_voice(42, '75ff19e65eef262114e1a6d93794ed8b4af6135a0cd0620f58023fd5b57204f4', caption=None, parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, filename=None)", + "send_message(42, \"Here's your voice message!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" ] }, { @@ -115,15 +121,25 @@ "received_message": "{\"text\":\"video message\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your video message!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/crownfall-lags-nkognit0.mp4\",\"cached_filename\":null,\"id\":null,\"title\":null,\"use_cache\":true,\"dff_attachment_type\":\"video_message\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_video_note(42, '11d3472ad8534d632beca6a452e2dc03b7ec9f4aba19fbb3346da43bac7389d6', caption=\"Here's your video message!\", disable_notification=None, reply_markup=None, parse_mode=None, thumbnail=None, filename=None, protect_content=None, message_effect_id=None)" + "send_video_note(42, '11d3472ad8534d632beca6a452e2dc03b7ec9f4aba19fbb3346da43bac7389d6', disable_notification=None, protect_content=None, reply_markup=None, thumbnail=None, message_effect_id=None, reply_to_message_id=None, filename=None)", + "send_message(42, \"Here's your video message!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" ] }, { - "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 2, 32, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=13, supergroup_chat_created=False, text='something else'), update_id=13)", + "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 2, 32, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=13, supergroup_chat_created=False, text='media group'), update_id=13)", + "received_message": "{\"text\":\"media group\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_message": "{\"text\":\"Here's your media group!\",\"commands\":null,\"attachments\":[{\"group\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/deeppavlov.png\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov logo\",\"use_cache\":true,\"dff_attachment_type\":\"image\"},{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/crownfall-lags-nkognit0.mp4\",\"cached_filename\":null,\"id\":null,\"title\":\"Epic Dota2 gameplay by Nkognit0\",\"use_cache\":true,\"dff_attachment_type\":\"video\"},{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/hong-kong-simplyart4794.gif\",\"cached_filename\":null,\"id\":null,\"title\":\"Hong Kong skyscraper views by Simplyart4794\",\"use_cache\":true,\"dff_attachment_type\":\"animation\",\"filename\":\"hk.gif\"},{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/separation-william-king.mp3\",\"cached_filename\":null,\"id\":null,\"title\":\"Separation melody by William King\",\"use_cache\":true,\"dff_attachment_type\":\"audio\"}],\"dff_attachment_type\":\"media_group\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", + "response_functions": [ + "send_media_group(42, [InputMediaPhoto(data='9f39f64560e6415032325cae1fec1ca06b3cc1a3549208a8a35564c0d3749062', type=), InputMediaVideo(data='11d3472ad8534d632beca6a452e2dc03b7ec9f4aba19fbb3346da43bac7389d6', type=), InputMediaAnimation(data='033391bab1eaefd6d8aa6ceb667b5dbaada7c4d6b1649a726541924a74539dc5', type=), InputMediaAudio(data='75ff19e65eef262114e1a6d93794ed8b4af6135a0cd0620f58023fd5b57204f4', type=)], caption=None, disable_notification=None, protect_content=None, message_effect_id=None, reply_to_message_id=None, parse_mode=None)", + "send_message(42, \"Here's your media group!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" + ] + }, + { + "update": "Update(message=Message(channel_chat_created=False, chat=Chat(first_name='Test', id=42, last_name='User', type=ChatType.PRIVATE, username='test_user'), date=datetime.datetime(2024, 5, 15, 19, 2, 46, tzinfo=UTC), delete_chat_photo=False, from_user=User(first_name='Test', id=42, is_bot=False, language_code='en', last_name='User', username='test_user'), group_chat_created=False, message_id=14, supergroup_chat_created=False, text='something else'), update_id=14)", "received_message": "{\"text\":\"something else\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Unknown attachment type, try again! Supported attachments are: \\\"location\\\", \\\"contact\\\", \\\"poll\\\", \\\"sticker\\\", \\\"audio\\\", \\\"video\\\", \\\"animation\\\", \\\"image\\\" and \\\"document\\\".\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_message(42, 'Unknown attachment type, try again! Supported attachments are: \"location\", \"contact\", \"poll\", \"sticker\", \"audio\", \"video\", \"animation\", \"image\" and \"document\".', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" + "send_message(42, 'Unknown attachment type, try again! Supported attachments are: \"location\", \"contact\", \"poll\", \"sticker\", \"audio\", \"video\", \"animation\", \"image\" and \"document\".', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" ] } ], @@ -133,7 +149,7 @@ "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None, reply_to_message_id=None)" ] }, { @@ -141,7 +157,7 @@ "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"formatted\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":\"\\nHere's your formatted text\\\\!\\nYou can see **text in bold** and _text in italic_\\\\.\\n\\\\> Here's a [link](https:\/\/deeppavlov.ai\/) in a quote\\\\.\\nRun \/start command again to restart\\\\.\\n\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null,\"parse_mode\":\"MarkdownV2\",\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_message(42, \"\\nHere's your formatted text\\\\!\\nYou can see **text in bold** and _text in italic_\\\\.\\n\\\\> Here's a [link](https:\/\/deeppavlov.ai\/) in a quote\\\\.\\nRun /start command again to restart\\\\.\\n\", parse_mode=, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" + "send_message(42, \"\\nHere's your formatted text\\\\!\\nYou can see **text in bold** and _text in italic_\\\\.\\n\\\\> Here's a [link](https:\/\/deeppavlov.ai\/) in a quote\\\\.\\nRun /start command again to restart\\\\.\\n\", parse_mode=, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" ] }, { @@ -149,7 +165,7 @@ "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None, reply_to_message_id=None)" ] }, { @@ -157,9 +173,9 @@ "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"attachments\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":\"Here's your message with multiple attachments (a location and a sticker)!\\nRun \/start command again to restart.\",\"commands\":null,\"attachments\":[{\"longitude\":30.3141,\"latitude\":59.9386,\"dff_attachment_type\":\"location\",\"__pickled_extra_fields__\":[]},{\"source\":null,\"cached_filename\":null,\"id\":\"CAACAgIAAxkBAAErBZ1mKAbZvEOmhscojaIL5q0u8vgp1wACRygAAiSjCUtLa7RHZy76ezQE\",\"title\":null,\"use_cache\":true,\"dff_attachment_type\":\"sticker\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_location(42, 59.9386, 30.3141, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)", - "send_sticker(42, 'CAACAgIAAxkBAAErBZ1mKAbZvEOmhscojaIL5q0u8vgp1wACRygAAiSjCUtLa7RHZy76ezQE', disable_notification=None, protect_content=None, reply_markup=None, emoji=None, message_effect_id=None)", - "send_message(42, \"Here's your message with multiple attachments (a location and a sticker)!\\nRun /start command again to restart.\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" + "send_location(42, 59.9386, 30.3141, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None)", + "send_sticker(42, 'CAACAgIAAxkBAAErBZ1mKAbZvEOmhscojaIL5q0u8vgp1wACRygAAiSjCUtLa7RHZy76ezQE', emoji=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None)", + "send_message(42, \"Here's your message with multiple attachments (a location and a sticker)!\\nRun /start command again to restart.\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" ] }, { @@ -167,7 +183,7 @@ "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None, reply_to_message_id=None)" ] }, { @@ -175,7 +191,8 @@ "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"secret\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":\"Here's your secret image! Run \/start command again to restart.\",\"commands\":null,\"attachments\":[{\"source\":\"https:\/\/github.com\/deeppavlov\/dialog_flow_framework\/wiki\/example_attachments\/deeppavlov.png\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov logo\",\"use_cache\":true,\"dff_attachment_type\":\"image\",\"has_spoiler\":true,\"filename\":\"deeppavlov_logo.png\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_photo(42, '9f39f64560e6415032325cae1fec1ca06b3cc1a3549208a8a35564c0d3749062', caption=\"Here's your secret image! Run /start command again to restart.\", disable_notification=None, reply_markup=None, parse_mode=None, has_spoiler=True, filename='deeppavlov_logo.png', message_effect_id=None)" + "send_photo(42, '9f39f64560e6415032325cae1fec1ca06b3cc1a3549208a8a35564c0d3749062', caption=None, parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, has_spoiler=True, message_effect_id=None, reply_to_message_id=None, filename='deeppavlov_logo.png')", + "send_message(42, \"Here's your secret image! Run /start command again to restart.\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" ] }, { @@ -183,7 +200,7 @@ "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None, reply_to_message_id=None)" ] }, { @@ -191,7 +208,8 @@ "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"thumbnail\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":\"Here's your document with tumbnail! Run \/start command again to restart.\",\"commands\":null,\"attachments\":[{\"source\":\"https:\/\/github.com\/deeppavlov\/dialog_flow_framework\/wiki\/example_attachments\/deeppavlov-article.pdf\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov article\",\"use_cache\":true,\"dff_attachment_type\":\"document\",\"filename\":\"deeppavlov_article.pdf\",\"thumbnail\":\"gASVySsAAAAAAABCwisAAIlQTkcNChoKAAAADUlIRFIAAADIAAAAyAgCAAAAIjo5yQAAK4lJREFU\\neJzsnXdcFFfXx+\/sbF\/KsnSQLiiIUlSwgo+KYi9RAbvYS4wleZ7EqNFojEaTqOlqLLFh77F3jaCA\\nCEgREJDeWbb394PwIiIus7NTFrPfj3\/g7My9h+U3t557DlWj0QAjRrCGQrYBRj5MjMIyggtGYRnB\\nBaOwjOCCUVhGcMEoLCO4YBSWEVwwCssILhiFZQQXqGQbgBIlf6+qYhHZVuAMbEOzP0NhdifbDjS0\\n1xZLVbmSbBPwR1WuEsSQbQRK2quwgEZCtgWEoBGTbQFK2q2wjBg2RmEZwQWjsIzgglFYRnDBKCw0\\n8GXsEhGPL2PrU4hcRX1a4Y6dUYZFe13H0s7DEu9tTycxYflgp8Ro76sQhOipx2Ve3yZEMWH5KNfY\\nyZ1ua7nzUn7Q7ucjulrm7gz5FbWRNwv9l91fkDl1DuoSDJkPU1ilYh6DIt8QvD\/65qdcunCoS8LG\\nJ5OFCtZnAcddzCrOvex9raB7oHXWbJ+rmxPq9ZfF77Cgy8VyiYVGAzb13jvk3OYulnnPq10flnSh\\nQqqvgw8czw515FQOd31yLCuExxBM9rrDl5mk1zg3VLcrddizKo\/edmmhDsm704Z9HXywWMj74\/mI\\n9cEHm1t1JPM\/D0u7MGH5V0EHM2s6XMzrNanjPZK+Idz5YLtCGqzqyC0Jc46\/Vej\/6YO5tqyavvap\\n824vyxdYr4md8XHXs\/vShqZVO5\/L7ZNe4+zFLVjxYD4AQAMgCqQGQEOnKDtxC77scVitATFZoQo1\\nvDF+MgDgh6cfmdFFzSs6md3vyIuBEzzurX88tUzC\/Tsv6G6R76X8IImS3sKkblYvV\/c4nFdnczwr\\n1MO8JNz5ybGsAcR+K8TxYbZYTYgVTAum8GZhQHKVuylNYsuqiSvtXCs3+eT+Ig5NSgFqAMB837\/N\\naKIfnk4AAMSWek++umplwClvXsHauGl3Cv3rFGwf3qto76urHkUnlHfkyznBdi+aV5FV6xhkm\/Gf\\nDsn97FOTq9wjve5cyO39qNRnR8gvDTeoNRAF0mg0YF\/6kCflncrEFiGOqTym0Ncyj6RvhQg+WGEV\\nCS1\/SxlxIifk0sjVpSIelyGY3OkOC5axqPX\/1vQ8DEEaJ9MKAEB6tVNiRcchzvEAgP4OqSeHbWgo\\n4VhW6Ibgv\/akhQMAmFTFoq7nh1345que9b3bS75dvsCmTMzN4dt15BZtSYi8VeB37VWPdUH1n\/Y5\\nucOGXdPdOhsAcDqnz+K7S4tnRQoVzN3Ph18Yufa\/D+sHVeVi82eV7jIV7XFZpyDbTLK\/LeyB161b\\nR7YNOpNUJLBWbtVyA5WigiDIhCZdH3zQw7w0xDGlQGjzoMS3g0mln3Wuv1XO+dzeVVKzYNuMvzLD\\nxEpmgHXOZ4EnmLDcgV3dyaKwoZBO3MLYMu+JHvd9eK\/sODU+vPwqqfmSbufYNHlcWSeJitnBpJIC\\naUa7xVox6+4U+23svd+HV8BliJmwfIRrnA+vAADAospMaeJ+DmkMWOlrmXe\/2HdOlyte3CKJkpFY\\n4dXLLqNAaNPbLv19v0iB2FvNGsamwzh8i\/gCta9zhQkFdRuu515IqxQvGIFJgb1Pbv8l9OfA162L\\nAbInLXz14+VL+nb4dICLOas9dS\/txtbcasmmG3kH4ksUKizfhEsjV5vSDXo\/u1ai3Hgjb09c8ReD\\nXJf2dyLbHKS0g1mhUqVefTnHc9M\/e+KKsVUVAIDHFNIoKmzLxINSgfyTsy\/cNj688aKabFsQYegt\\n1vnUioWnMorr5GQbYhDk1UjD\/ng6pBNvX4SPgzmDbHO0YbgtVoVQPn5f8ph9yUZVteBaZnWnzY9+\\neVBAtiHaMFBh7Xtc7L0l9kxqBdmGGChCuWrJmRcDfk14Xiok25bWMThhVYrkkw+mRh9LrxIrtNym\\n1iDb\/2vnKFTaFhru5tQG73iy61GRSk2gTcgwLGHdf1nTdWvc0aSyNu88n9uLEIvIRKGCrxcEar9H\\nJFfPP5kxdt+zaq3vIfEY0DrWqr+zt95+pVQjsodNlYY5JVix6vC3ixw0GpBY4ZlU2RHh\/Xam9PPR\\n3Xo6m+NsF1IMQlg1YsWMo2kX0irJNqR9w6RSNg33WB7qTLYhwCCElVkuGrsvOaO8vR5HMTSW9u+w\\nbZQnDSZ5kEOysO69rBn95zO+tB0sUbYj+rtzL0T7kbsFRKaw9sYVLziZoUA2qDKiE64WzDuLAl14\\nLLIMIEdYagDWXc7ZcONDdkgiHWcu88QM3yCShvMkCEupUn95Oee7268IrvdfiCWbejbar58bl\/iq\\nSRDWsrOZO+4XElzpvxZTBnw+2m9ARwuC6yVaWPNPpO+KLSayRiMsGuXvOf4Ea4vQSemS0xlGVRGP\\nRKEevifpQW4NkZUSJ6z\/Xcz65WERYdUZaY5EoR79Z3JCAXEbFQQJ69ubucbROrnUSJRDdydllImI\\nqY4IYR14Urzq75cEVGREO1UixcDfEssEMgLqwl1Yj1\/xF5z8AI83tVNKBPLwXUkiOe5bHfgKK7Nc\\nNHx3klRpeO5C\/2KSioXzTqTLcfbhwlFYApky8mBqlViJXxVG0HEksWzLLXy3PfASlkoN5p\/ISCo2\\nUMdZI19dyb2agaOfEl7C+vFe\/tGnbTuCGiELDQBRh57nVuF1phIXYT15Vbf6cg4eJRvBkBqJctJf\\nKTg5l2AvLIlCFXUoVaY0OsO0A+ILBeuv4tIEYC+sZWdf5ODWwBrBnK+v5z1+xce8WIyFdTWjyrgb\\n2O6IPJgqUWC8soWlsKpE8jnH3xuRx4jBklstXXEuC9sysXSbWXAi\/Y\/22FypVeySZJqwTOQYqDSx\\naf4JpJSZ5j9kVuXAMoGKaS4zcxS49FEzzcizFS9gCDxe1jOwA2a\/GmbCupdTM+j3pwhPBRoU1k\/2\\nmL2KrZ+BQ3DRgM9lPLfGD9Qqu39+4pSlNr9ZRWOV95gtdvAnx1Y88XcwebS0B5OGTZA3bLpChUq9\\n7NyL9qgqWFRl+lpVr18ylVnOraaPOMWJLVRVf79CYvfoF0algQZq04ekYuEvDzHz7MXmhNAfj4qe\\nFrXLRXYNBQYQBWgaN86cLU1H9LTvwGX06GBWEF+5Pe7Nnc4uLr6+vjeuX5fL5X1Lz2\/9JkahUqeW\\nCp8VCwv5sttZNbXSdr95tf5a7pRAOzszDAIkYdAVVork3ltiK0WGFTsAOc4pR2gv6hsqFpt1\/NQp\\nb2\/vhusymWxu9Ox\/Hj4EAMyZO3fFZ5\/S6fSE+PiICRMBAIdjjgb3ehM\/Qq5SpxQLb2fX3M+tvZxR\\nhXmAOMJY3Nfx5\/Gd9S8HA2GtbZ8HubhMamSA7YI+jn4OpokJiTk52UOHDjUzb3lYKutFFpfLtbax\\nbroya\/qM+\/furV67ZmZ0dKslK1TqI4mlx5LKr2RUtUd9PV0R5O9oqmch+naFxXzZd3fy9SyEYAId\\nTRf1dZzZ07HpGHpg98DA7q3HdfH08mxxxdHREQCQnfXeYRYNpszo6TCjp0OZQLb19qtdsUUCWXs6\\n6r3+2sszs\/z0LETfFuvj05k\/Yzfiw5t+buabhnv0d9frvMqd27dTklM6enYcNnw4kvsVKvWeuOLV\\nl3Oq248HUeKKoAD9Gi29hFUqkDl9\/bBdTAaDnM2+Dncf2smSLANEctX2e6++u51f1x4CVQzsaHFz\\nYRuhubSjl7AWn8r49R9DP3hjxoQ3hnss6ONIegAWAMCrGunnl7JjnpYZ\/rv4bGVQNwf0jRZ6YfGl\\nSpu19+SGPf0Z2om3P9IHk\/kzhlxKq1xwMqOQT8ShBtREBdgemeqL+nH0L\/E313MNWVVUCrRrYucr\\n8wIMTVUAgBE+Vokrg0Z3sSLbEG0cfVqWVYE+aBlKYSnVmt1xBrMtqFZRheX0mnxGTV7DP0+W9OHH\\n3ef2ciTbsvdizaGfi\/bbPMKDUfuKKqoAakMceP3+CP04B2VXeCSxdMrh56hr1R8av4hdlsoue04V\\nV9FElZDmzR+mi2+XP\/fvt7Iy6PagiatXrny2YqVIKpOb2Co51mIbH7G9n5JjEMYzqJSqDSEcVCmi\\nUAor8Ic4cvZw1ErT\/Fjuiyt0YWmrn\/fr33\/vgf0UCvnjdOQ8S3q2cN688vLypisSS8+aTsMk9t1I\\ntaue3z7qtKBPBxQPohHW00JB4I+PUVSmJ5xXsVYpJ6jS97o7jhk7dsu2rVSqoedxeZfCgoIJ48ZX\\nVr51bEbKdan0j5JZIg2cjAe9XMweLe2J4kE0b\/aBeKJHV1Rhuf397+2e7NGiqtFjxnz3\/bb2qCoA\\nQAcnp1Nnzzg4ODS\/yKzNd7yzhffsGKQkLelLbH7do7xaFA\/qLCylWnOW2EwkjKoch3tb2eXafFND\\nBwzYvPU7GG5\/CSObcOzQ4fc9u1tsVkJAY5F93fbRzxQ5ac4jp5LR\/Ll1Ftbl9Mr8GuIWYFhlaQ73\\nttIk2mI7+fr6bv9pJ53eMrl3u8PHx+fnX39lMpktrnPK0+zv\/0iRCUix6nRKOYrj+DoL62Ryuc6V\\noIVZnmH\/z06KWtsWm42NzW+7d5ma6rsbbyD06dtn9Vdr373OrM23f7gDaP0qcCK3WhpfoPMxHt2E\\nJVOqL6dX6VoHOmiCUtvHu6C2vsodP\/9kb29PjEnEEBkVNSli0rvXmTV5No93k2ER2BWr84KWbsJK\\nKKirIMahT6O2jt9HlbURge6T5ct7BgURYQ+xrFm3zt3D493rpkUJJv\/vSE0kt7J1DjOpm7CQ5OXC\\nBIv0i6zqNk7oevv4fPzJUmLsIRgWi7Xz558gqJXUedaJByGllGB78qqlz4p0G+HpJqzrmUQkJKbI\\nBOY5N9u4h0LZvnMHAcaQRWdv70VLlrx7naKScTOvEG\/PZR1D0+ggrIwyUaYeu5LI4WZegeVthMpc\\nsGihR0cyVw4JYM68ue7u7u9et3hxBaiIPmHwIFe38bsOwnpCSMxdSCExzX+o\/R4nZ+ely5YRYAy5\\nmJqafva\/\/757HVIrTfP\/IdiY6y+qq0Q6rNPqICxiFhpYFZnUthYD5y9c0E5X2HUlbOjQ3n36vHud\\nU5RIsCVyleZ5qQ4Rl3UQVjIh4fk4JUnab+js7T1u\/HgCLDEQlq9c8e5FZlU28b3h5QwdVpqQCiu9\\nVJRXg\/9kRKNhad26AQDMmh3NYBic7x5+BHbv7u\/f8kQ\/rJKxKl8QbEkaHi3Wy2oiQl5RheU0sbbX\\nwsbGZviIEQRYYlAsXd7KgJImIjrT8a2cGhniANhIhXU3h4hMLLCijVlnRFQki0Vackey6NO3r5OT\\nU4uLlLYmzpgjlKmQJ7ZAKqzCWiIW5WCtm80wDE+KiCDADEODSqV+NHFCi4uwhIg1xRZkI47ViHRu\\ndVP3RX0UwFrngz2Dguzf9lgiHpXghFp0UcsNsNl0CnsQ5vWOHT9+5\/YdavWbngjSkJCW4V5OzUfd\\nbJDciUhYLyvF5UIi5iCQVnfWEaNGEmCDdjTyFLXwmJYbKKw+AGAvrA4dOvQMCoqLfbNR2Mp2D\/5U\\nId4pRiSsKjH5kWRodNrgsDCyrQAAdoAYAVpvwOscxKDBg5oLixSQZ4RAJCxDiILs69vV2tqabCsA\\nlbsAcBeQUnW\/kBAAviGl6iYyKjAdvCcUkuO72JzQ0FCyTSAZLy8vNptNrg0qNRAgiy+HqMWqM4BY\\ndZ28MYgGpj+qugMqgbYxFsxdBHPwGguGDR1y7sxZnApHSEa5qKdzyyhi74JIWHyyhUWj0VrdMiMe\\njSJHI7ml7Q6TMfjVHhgYSLqwXlZJMBPWS7LHWB09PU1MTMi1oRGqE8QM1nYDbItf5W6tedEQDMIB\\nd\/sQlo0NorUTAqCazwXmc8mqPSBQr5hVmFCAbKkc0eCd9GSW3fzIP2xOInm5uc+Skhpclkk\/O4kw\\nzF77iHFga4tj\/2KwSCSSixcuTI2aPPg\/A9PT0houurm5kWuVGllMhvbhLkej0cg2gVAqKipOHj8R\\nc\/RoUWHL+K5OLs7Z2WSmL0AY6rJtYUnwz3jeJtYGM8bCG4lE8v3WrTFHjkqlrQ9l7OxIPkRZK8Fo\\nHYv0tQYAAJfLJdsE3MnLy1uz6stH\/7R0Znd3d1+\/cUPTaguX2\/ZUH1cQ6qFtYRlCNEhDWWvAAZVK\\ndfP6jZijR+\/dvdv8OgzDYUOGRERF9g8JaX7dhOxgAgj10D7GWKRPhfCgurr67OnTMUdjXua8dTSX\\nx+ONHjt2yrSprY7T28tX0T6EhTq085d\/Z1\/F9JDtwj6Os4P1DW2anZW1e9euy5f+Fovf8pj19PKa\\nO2\/ekPChWlpoDPNL4kr7EJZIhNINN7daiu0OeqkAfQw0pVJ58fyFYzFH45\/EN9cHlUodNnz4lGlT\\nu3fvDrUV5FIkJNojGR1tC8sEVWxTbOHzsc+GTSTV1dVHDh0+deJEQUFB8+s8Hi8iKnJSZOS7Lu3v\\ng89HE18PQxDqoW1hmTLJb9XKyvQNRjKzpz266L8NJBTWxeajPAi+etWqvy9eqqt763Hfrl0jo6KG\\njxxhZqZbttzSUoLisrwPHhuRHsgXDRJk71nUQc6m4R72emQS2HwrD7WwYo4cbfoZgqCh4eFz58\/3\\n80eZXutVPsm51syRNTTtQ1jV1SScSMEWM3Pz2XPmTJ4y2YLH06ecFlNI4oEpiLztEQnLnAnzSc1Z\\nlfwsmcTa9aRnUM\/IqKiRo0frv1Igk8kUCpLPH7QWtKsVEAnL3ZJFbsrn4iKUuTc6mDO62HIaUuvo\\nY4AVh9ZQjhVH513Lo8eP61N1c1JTUrAqCjUOyEYUiITlYckmV1g5OTkymQxFyIbvRnl+N6plilQU\\nzAl2nKP38pX+vMjMJNsE4GGJ6CQ6ImEhnAjgh0wmi330KHTAAMxLlkqlL3NyGg6C0mg0Ty8vAtKl\\nFBYW1tY0HgB2cHTkIR51paam4mkXItwxFBabRv5SVkZ6OrbCevjgwYF9+588fiwQvFlBtbGxCRkQ\\nOmPWrKZc9hhSXV198MCBy39fzs7KaroIQZB\/QMC48eMmToqg0dvoZ69eJiFIZAu8bThIbkP0dvo7\\nkr8HfO\/uPayKqigvX7p4yYyp027dvCkQCKhUqourq6ubG5VKLS8vP3n8xISx47Z8++37HFfQcfHC\\nhWFhQ37asbNBVRYWFp5enjweT6PRPE1MXLt6zbgxY5KfPWv12ZI6WWM7V0vy6igEAJeNaJSJqMXy\\nsUUkUlx5lpRUV1en63Liu7zMyZkxdVpJSQmDwZgwaeL0mTPNzMzOnjmjUqpGjRktkUgOHvjr+LGY\\n3X\/sSkxI\/H3XH3quDjTw7Tff\/Ll7DwDAz98\/Iipy7LhxTXk0FArF6VOnYg4fSUlJmTBu\/A87to8c\\nNar5s2dSyiMPpu6N8FanthHwlwA6WiEN9YOoxTIjb\/F9dqTgt01VENBIpdLrV6\/pWVpeXl7kpIiS\\nkpKQ0NCbd++s37DBxcUlcuLELZu+3fbddyPCh1lZWa3f8PWtu3eDe\/dKiI+fEhkll+ubIOnrdev+\\n3L2HzeHs+PmnU2fPTIqIaJ6dhUajRURGnrlwftPmzVQqddnHS4\/HxDR9uvZKzvj9KUq1Rq7S3Lh2\\nXU9L9KeHE1KnHUTC8rThcFkkaGvx9LoVc4QhQfIfv6qBIM2li9rCvCCBTqezmMyZ0bP2HthvZ2fX\\nMNLKz2tcyxYKBNevXgUA2Nvb\/3Xo0JixYyEKRf8O0dLS0tzc\/PS5syNGajvIOiky4ujxY6ampqrX\\nmWvUAET8lbLhep4ZA742P2CkKzU+Pl5PS\/THgoV0tQWRXCiv8+WfTiE06dfi6XWLpjfu5Hu5KV0c\\nlU8eP66qrLTUI3Wqg4PDqbNnrJrFgHBxdaXT6U3NkrOLS8MPMAxv+\/EHkUikv4\/h4o8\/njJ1KtfC\\nos07\/fz9H8Q+4nA4ZQL58N1JiUUCPweTmGm+nW04e\/7YJZORn5y8rytS\/1WkU2snLqFhP0eHiRdO\\na1RVVS20aLVFXiFNIpFc1LvRsno7soirq+vylSs4HA6DwZg1Ozq4V6+mjyAIwspzFYmqGuBw6oez\\n4\/clJxYJLNnUMzO7dX49Czt96hQmluhJZ8SjbaTCCvVA+tXoj7uzYu0n\/IatA6kMTF9umVfY2AIf\\n2Lcf8z2NufPnP05MiE96+uWaNdiWjJqTM7t6WLKqxMqPDiQX82WJCYkvXhAdyvZdGFTIG3NheVoR\\nFOcEhjVbV9Wy\/j9l3+qt5nkFb\/r1V\/n5N65jP4ZlMBgGFdrU3oyRsCJopLfl0yJhjx8ff\/bjfrIt\\nqmeAhwUL8YomUmF525o4mhGRaXJ0mKRzx8ZzILcfMS7faSnovXv2NI+Y+KFizqQem951RahTiUB+\\n2zJc0AFNYmZs8bXTYdUJqbBgCvDRpVx0MBia6EmNm5JyOdj6eyuT26eJT69euYq3JYYAmw5\/P9or\\nnJaloVCVLOKGIu9jaCdL5DfrsC\/2UVfcT42G9hC6Ozf658RcYOcXtT65\/XnnTrwtMRD+efgwK2aL\\n85UvLLL0XcPTEwgCvvY6TGV0EFZvxFNN1Ezo9ibfyZkr7x30ZGZk7NlFTq5RIlEoFN9u\/AYAQCMj\\n8nYLBna00MkFVwdhdXMwdeW1zIONITCkGuka1\/BzYirtRa62Id3O7dtLiovxM8YQ2L93X3p6Gwlg\\nCKOfm26H0XVbT\/+Ph8W+6hIdTUJKkE0GldI4Kk\/JaGOFVywWf7pi5eGYozgZQwxqeYZaeF6jyNYo\\nXwF1HQTbAKoDhdkLZg8qKFbt+PFHsg18w0gf3daldRPW1O52+57gJaxQxzf+x0+etT0DjYuNPX3q\\n1PiPPsLJHlxRS5+oqjepxZebX2w4aqiu26uE6JkJ7mymSCol32Hp9fIH3d9Rt+1\/3ZzaermYmzPx\\n+lU7mLzJOlRWiaiWLz\/\/orlvUztBoaz8QlE4sIWq3kIj7x+YcWlfxZAQ8gOh17\/z7lyqju6Put3O\\npsNhXhi4kbQO9N7\/vA+FQrFk0eKKCkI3MfVCLZUXR6lqfwSg7f0DM1PN1i9royeRHwt9fm+d3bJ1\\ndsNFmEqFMLKzslYuW65Ukh9rCQmKypUasQ7bnVQYLJstDA8ls91yMKP3cdU5jJTOwprgZ2tnSsQS\\nPHL+efhw7ZerybaibVSCE+q6P3V9CobBuhV8SwvSjt+N6WJN17UjRCEsKgUa44tLuhil+o0xpia6\\nfY\/Hjx37et06HIzCDrVIWbUW3aOmHM2SGaR1iONR9VFoTqRMCbRD8VSbpFa9CQfVK0Bnv82\/9h\/Y\\ntuU7rI3CDJXwIlDmon58fLjEzpqE7r6LLWcwqlE1GmH1d7fwssbeF+Dqq+5NP3u6ofkSf\/\/tt7Wr\\n16hV5AdNfReV6Lw+j1OpYEAvEhz95gSjTBCJ8gzd8hBndA9qoUBoWyZuHCSGBMnsrNHo48ihQ4sX\\nLRIKyTxe2yptJEpBgI8X0YfrYQhM64Eyli5KYc3sac\/UfUDXJmdeNoZwhWEQPgDlVOj61WsfjR2n\\nf+QjjFHrm6LWxZHornB2sIOl7iEFGkApDiYNnhOMfVzo\/Sn\/afp5yliRqQlKv6uc7OzhQ4ZeuqCX\\nH7NYrhLK6v\/pU0gDapVQqGAKFUypEn3AeksLor3QPu6HNBzcu6BvdT4f5IZ59ti0XM6dR41b6A62\\n6qjR6MMi8vn85Z98surzL1B3iyG\/JJiuumO66o5K7z9oeoXGas8pqz2nIq+uQl2IWEJott5hnXk6\\n+cm0AL2wHM0Zk\/wxXixV0Tnb95oq\/7+NmDdZ6OyAvv1Xq9XHY2LGjBx55\/Ztfayqk+o7uGnK5kCB\\n0Is0v5DQE3irBuuVW0WvcdK3Izrq8\/i7qBimWbm0\/ScaXVVZTLBjXTUV1itOcH5e\/pxZ0XOjZyPY\\nVVQCIH99pA+83r9q\/HKuvdDXHepKRlXDD\/Yc9EUVlRG3Id3L2UxXP5kW6CUsNx4rKgDT9ElUhgpm\\n7NxnmpLR+HZ6uat+2VjdPGy9h4fH77t37T1woE\/fvsgLvn3rVnjYkGUfLy0pafDOkKtEV5UVKxUl\\nkbK8TrJs5ut\/JrJsM1k2u\/7nHPN+vEMNzy46lXEtswr173QsqWzzrcYzsSH26ANc3biPozNcCzbr\\n3WRAesYNzygT+X0fJ1dhFnzc6fIXdHGFvY1y95ZqN6fGHuTvW8zV27gyef0g4+DhQ71fS0omk4X2\\n7VdZWYm8cEsLVXiodOFsDwtWUps5Fh6Vdh589juVprGdgCBgb0qnNAtoZ8aEfe1MhnlbTgm0o8GU\\nrArxTw8KnhYJCmplqtfJ19QaTUmdvKkaDlXyfMocOzaawB5PntFmrnxrw6POLaQicDqKotpkYEeL\\nmwv1TYyob7fd2ZYzr5fjzw9bJqlCjYrNBeKKknLq0rUWf2yudrCt75gCfOUOtsrc1+fAmP9\/TovB\\nYAwcNOj4MW0Zmpvo6KqYPEY8Llz8OlLQUySP9LbLmO1zedfzxnPxGg0ornt7P4AP0srEx5+Vv6yS\\nTPSz7fNTvPYp5Ajz8+hUpdaAXw8Sl+nkqyEYZK7DYC1q1WBXDGPBK9iN7+XLAtr8z3kl5ZQaPrRg\\nFS\/3\/08X7ty+XSJpXOKi0dveDre3UW78tPbUH5URo8RtxZ9qyQ\/9\/ljc9RyH2saK2jc38uafSNei\\nKopMwEs9lXn4ak4+mjf5xEXW4ySCTqJH+NuEYHE4GYOJhr0Z438DXdZceal\/UfXAb\/74LwtokxZa\\nmXA0r4rf2Hn\/3v2Tx49PmzGjzTDdNJpmyljR4ulCNgtlT02lqL\/vt+t\/gcfSa5wzapyqpGYaTWNX\\nqAGgSmp2vaB7Nt9RrQGPXgfrhpQyVkUGvbYA0rwWmUYDywT0uiIGv4CilAkBZeGXFod3VFlb6jA3\\njHtK3\/IbQRm\/WDTKhnAPTIrCZga7ItR5\/5MShGmotaOivjVErebD1e9kpTDnWjR4+V1\/\/6loF0fl\\n1ytre3TDYBvEhs23YaeEOrYy7lao4YmXV195FQRey8jhzhYm\/5WWoopKqVM+sfxpfU0nD0TLKBdv\\nMr\/eYd4wuCSA\/\/7HxdMamyPv2AiLTYe3juo4fj8GMX0VHOv3fdS7T28nJ+d+If2HjxhRP3PZtIn\/\\nngh3vQJl276ssTDHPZ8RjaL6LPCEs2lFQgrtZZpQu6oaKCqlzlhpOWuicE6kSEt87vJKyva9pueu\\nsRA60+qPE5fx6QDMtoD1nRU2Z\/Kh1KNP9d2hY1a+cLzbuvfL2Qvnfbt2BQBUVlRu2rjx\/Llzrd42\\nOkz87f+Izr0jkYLpyy3TsnRwgTThqKMnCXsFyrt1VjTNNWVyEJvIuBvLOH6J3dTttgrms8KbCwIG\\nemLmd46lsAprpQE\/PK4U6dX70GvynW5taPWjhYsXWfB4D+7de3D\/wfvCN0SMEq39BGVuEj1RqcC0\\nZZbP0tG417JZag5bU1tHUSiQtk\/YCmtyoO3hKb5YlYaxsAAAB+NLph9N06cESCF1O78EXes\/anB9\\nW4UwdQIeCITQvC94yai0pSsYCsvRjJG4MsjGBEuzMXZ9mdbDXk\/HZQ2NqTBB46Havats\/QoyVfXa\\no1rz84ZqF0eSs5Loyh8TO2OrKuyFBQDYPdHbxgS9c8jrHUOdN9XtrFXbv6phGMAhD0uuZuuqWiaj\\n3QRaig6yH6HjKWckYC8saxO6nr211FK3jSoqrNn6ZQ2Payg5bbt0Uq6cS\/5hQCR4WDJ\/Ht8Jj5Jx\\nSe8x2Iu3JswV9eMS68463T9ppDjQ17B6n4jR4uAALPMP4AGLRjkzyw95kD6dwCtvzBeDXAd7otwZ\\nUHJ0aJmteaolM9E3D2K913SFoubDusY\/EkwB\/50vYNB1a0RRZKHShz2TvLvq4cqnHbyExaLB+6N8\\nEGaKaoHCxFbBQrqgMm+y0NwUZSf4LI02bIbNzYfo\/5z7jnPGz7fKL6rXE9X2L6rN701faeeOytFh\\nYuRF+XTpcvn6tb79dPAF0ofFfR0n43OMrwEcM105mjPPzOrGoeteBQQpTBC5eXHNVJGjdfjjNaeq\\nBprzP15lNbz0K4vLt3V2ddJowE\/7TbbtMisqpc5cYQm438Cmk2CzaVT7kwBqbAbmRgnbdM5pILhX\\nr5gTx52dnXfv3dvFF8v1pFbp78b9YYwXrlXgm0Ktq73J4Sm+KFYAxPbdkNw2f4oQdRI4SwvNgqkN\\n7vDQp99YfL7ZHHmA\/tIKSvSnvN8PNbqyzJwoBMI\/gaq6vhPkDKc5xQF6vf2OdupxQ9vua1d+9tnh\\nmKNsNhsAEPvoURbOkbfdecwzs7rSYXz\/9Ljn5hvja71lpM7uiFKee5v3UGHN+GF6DZFmR4g+ia6D\\nKfWNyoUb7CFTbY6cY\/PrtL0IxWWUnftMhs+wefyM8bpt1Xy+iD9jghgoX8oLBwNlaf13SvdgOMfC\\n1jsAzWPCiPc2qAwGY9z48ddu3li4eFHDlevXri9esFD\/7D1asDGh3VoUaMnBfWEG45X39\/H5xawt\\nt9veoH2DRuN8ZRVNrC0+0eC+kh3rMUizdvwia\/OvbzwITDjqIf2lQX4ydxcVk6GhUTVCMSSRQk+e\\n0VMy6HfjGE1beEyGZv3y2pGDm83+qB40+yMURlOGerVKeOnbHxLjnyQIBHXVVdVMJtPaxsbe3r5P\\nv77Dhg+3sXlzGuXk8RNfrVmDPK8JipV3Dh2+Pt+\/t+6hY1BAkLAAAFGHUmN02aK2jt9vlv9Ayw2b\\n\/lszZgg2U\/rMHOoXW8wzX+rwHnu5KTZ8Wuvb6R3vF8iEarkR5s5B7jnC5\/N\/3Pb9oYMHdbBYd2Gx\\naZQjU33H+L7XeQRbiBOWSg0iDyafTEYaJI1TmGAX99v7P9c8vVyqq0eoFtRqcOEm6\/eDJs2dClvF\\n3kY5f4pwzBCJltoheleq5UYKZ5B2eUml0nNnzn6\/bVt1lc6HNXQSFgRAzDTfSf6YnnzRCnFH1WAK\\nODzFV6ZMuZCG6PiDyMFPyeRSpa13dl07KzBUVf3AiALGhElGDpQkZ9Cu32dm5VKT0uhiSeMYlGeu\\n8vFSuDsrB\/aRBXaVtznw1chTFCVjANUVNp0EMfwlmkEmpm+8QIsKC+Pi4uJiY2\/duFlTo+\/R+zaB\\nADg+3XeCH3GqIlRYAAA6lXJiRtfIgylnUxFoi0IV2fuZ595t9cPOHrgstcMwCOiiCOiiaGjDauvq\\nR1cmHA3KpTJlnqrmu\/pZwnLLogpnCx5PrVaXFBfX1RHn2ANB4ECkD8GqImJW2AIGlXJierepyJbm\\nhI7d3\/cRujhHOkGhAB5X42inRr0A24S7s7K0tDQ9LS0zI4NIVVEp0MnpXVFHjNEHooX1epkAOjCl\\ny+ygtn9bqa2Pktn6OYIunoa1Oagdax4JIbtM6PDfc\/zQxePTHxKE1VDrngifTcPbXqyq9Rza6nU3\\np\/YRzbYB4q3lsakPlnQP0yWtEraQI6wGvhjktjfCm03TZkOdW381pZWBoLmZoTjJIMEWVRA51Pg7\\nmCStDPZzJO6M67uQKSwAwKwgh2vzA7Q4BmpoLIFTL2KNwh5TDnGvwfDOltfnBzhxiQv00CokCwsA\\n0NeNm\/xpr27v99+o6TJWQ9QRKJwgzGF69WDXC3P8rbD2M0YB+cKq7ylM6XGf9JzXq\/U4qioWV+Dc\\nvhstKf5hablM6rlZ3TYM86AYxjtoEMJ6HXuS8sdE74OTfXjsVkZUtZ2Ga6C3TMVzoxZ7qmqw99JU\\nN\/vbBTmbJa4IGk3Udg0SDEVYDUztbv9gSY8+72TcVJjZ891Cm18pLDWItFgIKSzBQVg0VkMnu3qw\\n662FgW6ofCrxw7CE9TqrOefhxz02hLvT4bfa9NrOw1W0N2EFMnIw3dDBmZcF2O9wqJjm7pbM+4u7\\nbxjmwcEu2g9WGJywGlgd5pa0Mrhvs6ZLxbKo7jK26b\/J6e1JWDV8zL9naNmIwLT\/9u6rX0BH\/DBQ\\nYTU0XQ8+7vHLeK+mUON1riEy08YBfjGBATn1J+4pltM0H7+A0xcurJ8\/kYFDqH2sMFzLGljU1+n5\\nZ8GzguxhCgRgakXgdA1UL6l\/4gk90KIPFVUUvgCb18CCx1v99cbz505369oFkwLxw9CF9XoxgrE3\\nwufhku4DO1rIrDrWufYDAEhklLQsQl0zUHMvDoN3gMnmTJkx89qt2zOnT8HCKNxpH38bAECwi\/nN\\nhYE3s6qXnYD5RzLpwtIzV9g+nuQEltGJK\/f0WgSnUKnhI0Z99tlKpw46pzklEeI8SLFCrQaXHzzZ\\ntW1TWUHC3RPlZJvTBlU1lJCJNuiCp8FU2pjx42fPndvJE5vwjUTS\/oTVgFqtvnX7jrPJZhfLe2Tb\\noo29xzjf79Yt\/3t9P0JnTIyMmhU9y90V+yxrxNBehdWEVJSqqNpKkZykwgaXplClAn3H2wpEOgxk\\nbRw6RERGzps\/l2UIoXP0oN0LqwGVUiSu+AkSHaSBHLJteUPMedaGncjWmSAoZOCgydOmDx7QH3ez\\nCOEDEVYTgpoEWH5Jzf+FRiE6DGkLpDIQPs2morqNhQbvbv6TJ0cNGxbONde5xzRkPjRhNVFXeRWS\\n3wOSK1TVc9TH8PVhy2+mf51q3RfIxNSsq5\/f4LCwoeHhdrYGtHOMIR+ssJoQ8p8D6TW1+AakTKND\\nJcRU+iKXGrnYqnl8dogCe3h6+gd2Dw7uGRYWZsLBJpy6wfLhC6s5UmGyVBgPSf6mQ3lKSTqdhst4\\nXyyBJi2yzC2gmfOsnJw6dPMPCAsb7Onp+aE2Tq3y7xJWc6RSPlWTIxZmaqSJGlkCiy4W8DNNWSij\\njKhUQKp2YHNsJQrH\/MretfJe9ra2HTu6w6R0wwbAv1dYrVInKGFSSqmwhi8oA6pKJk0sEpZSNLVQ\\ns8yoSjWDzrCm0y3FcjbLxJFJ54hldCrTncn4wHs3nTAKywgu\/EsbaiN4YxSWEVwwCssILhiFZQQX\\njMIyggtGYRnBBaOwjOCCUVhGcOH\/AgAA\/\/+S+QX6OV9s5wAAAABJRU5ErkJggpQu\\n\",\"__pickled_extra_fields__\":[\"thumbnail\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_document(42, '08a7a36671950baba722d94a87dea286f03d7a6f7d0c32d5707d877cd211739a', caption=\"Here's your document with tumbnail! Run /start command again to restart.\", disable_notification=None, reply_markup=None, parse_mode=None, thumbnail='9f39f64560e6415032325cae1fec1ca06b3cc1a3549208a8a35564c0d3749062', filename='deeppavlov_article.pdf', message_effect_id=None)" + "send_document(42, '08a7a36671950baba722d94a87dea286f03d7a6f7d0c32d5707d877cd211739a', caption=None, parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, thumbnail='9f39f64560e6415032325cae1fec1ca06b3cc1a3549208a8a35564c0d3749062', message_effect_id=None, reply_to_message_id=None, filename='deeppavlov_article.pdf')", + "send_message(42, \"Here's your document with tumbnail! Run /start command again to restart.\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" ] }, { @@ -199,7 +217,7 @@ "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None, reply_to_message_id=None)" ] }, { @@ -207,7 +225,7 @@ "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"hash\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":\"Alright! Now send me a message with data attachment (audio, video, animation, image, sticker or document)!\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_message(42, 'Alright! Now send me a message with data attachment (audio, video, animation, image, sticker or document)!', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" + "send_message(42, 'Alright! Now send me a message with data attachment (audio, video, animation, image, sticker or document)!', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" ] }, { @@ -215,7 +233,7 @@ "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"source\":null,\"cached_filename\":null,\"id\":\"CAACAgIAAxkBAAIEtGZOUac-j5GbuhH9xH-xsJ8QGWKHAAIsAgACRwZDBdGLCpfBS4KvNQQ\",\"title\":null,\"use_cache\":true,\"dff_attachment_type\":\"sticker\",\"is_animated\":false,\"is_video\":false,\"type\":\"regular\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":\"Here's your previous request first attachment sha256 hash: `75681742958b21061e6d5ebb80340d62de4c7348e71fb8c1d2c356a2239b2334`!\\nRun \/start command again to restart.\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_message(42, \"Here's your previous request first attachment sha256 hash: `75681742958b21061e6d5ebb80340d62de4c7348e71fb8c1d2c356a2239b2334`!\\nRun /start command again to restart.\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" + "send_message(42, \"Here's your previous request first attachment sha256 hash: `75681742958b21061e6d5ebb80340d62de4c7348e71fb8c1d2c356a2239b2334`!\\nRun /start command again to restart.\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" ] }, { @@ -223,7 +241,7 @@ "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None, reply_to_message_id=None)" ] }, { @@ -231,7 +249,7 @@ "received_message": "{\"text\":\"some text\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":\"Bot has entered unrecoverable state:\/\\nRun \/start command again to restart.\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_message(42, 'Bot has entered unrecoverable state:/\\nRun /start command again to restart.', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" + "send_message(42, 'Bot has entered unrecoverable state:/\\nRun /start command again to restart.', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" ] }, { @@ -239,7 +257,7 @@ "received_message": "{\"text\":\"\/start\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None, reply_to_message_id=None)" ] }, { @@ -247,7 +265,7 @@ "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"restart\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"longitude\":27.792887,\"latitude\":58.43161,\"dff_attachment_type\":\"location\",\"reply_markup\":\"gASV3AMAAAAAAACMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJbmxp\\nbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFtLl9p\\nbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmBlH2U\\nKIwNY2FsbGJhY2tfZGF0YZSMCWZvcm1hdHRlZJSMDWNhbGxiYWNrX2dhbWWUTowJbG9naW5fdXJs\\nlE6MA3BheZROjBNzd2l0Y2hfaW5saW5lX3F1ZXJ5lE6MH3N3aXRjaF9pbmxpbmVfcXVlcnlfY2hv\\nc2VuX2NoYXSUTowgc3dpdGNoX2lubGluZV9xdWVyeV9jdXJyZW50X2NoYXSUTowEdGV4dJSMFEN1\\ndGUgZm9ybWF0dGVkIHRleHQhlIwDdXJslE6MB3dlYl9hcHCUTowHX2Zyb3plbpSIjAlfaWRfYXR0\\ncnOUKGgUTk5oDE5OTk5OdJSMCmFwaV9rd2FyZ3OUfZR1YoWUaAgpgZR9lChoC4wLYXR0YWNobWVu\\ndHOUaA1OaA5OaA9OaBBOaBFOaBJOaBOMFU11bHRpcGxlIGF0dGFjaG1lbnRzIZRoFU5oFk5oF4ho\\nGChoIE5OaB9OTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAZzZWNyZXSUaA1OaA5OaA9OaBBOaBFO\\naBJOaBOMDVNlY3JldCBpbWFnZSGUaBVOaBZOaBeIaBgoaCdOTmgmTk5OTk50lGgafZR1YoWUaAgp\\ngZR9lChoC4wJdGh1bWJuYWlslGgNTmgOTmgPTmgQTmgRTmgSTmgTjBhEb2N1bWVudCB3aXRoIHRo\\ndW1ibmFpbCGUaBVOaBZOaBeIaBgoaC5OTmgtTk5OTk50lGgafZR1YoWUaAgpgZR9lChoC4wEaGFz\\naJRoDU5oDk5oD05oEE5oEU5oEk5oE4wWQXR0YWNobWVudCBieXRlcyBoYXNoIZRoFU5oFk5oF4ho\\nGChoNU5OaDROTk5OTnSUaBp9lHVihZRoCCmBlH2UKGgLjAdyZXN0YXJ0lGgNTmgOTmgPTmgQTmgR\\nTmgSTmgTjAhSZXN0YXJ0IZRoFU5oFk5oF4hoGChoPE5OaDtOTk5OTnSUaBp9lHViaAgpgZR9lCho\\nC4wEcXVpdJRoDU5oDk5oD05oEE5oEU5oEk5oE4wFUXVpdCGUaBVOaBZOaBeIaBgoaEJOTmhBTk5O\\nTk50lGgafZR1YoaUdJRoF4hoGGhGhZRoGn2UdWIu\\n\",\"__pickled_extra_fields__\":[\"reply_markup\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None)" + "send_location(42, 58.43161, 27.792887, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=InlineKeyboardMarkup(inline_keyboard=((InlineKeyboardButton(callback_data='formatted', text='Cute formatted text!'),), (InlineKeyboardButton(callback_data='attachments', text='Multiple attachments!'),), (InlineKeyboardButton(callback_data='secret', text='Secret image!'),), (InlineKeyboardButton(callback_data='thumbnail', text='Document with thumbnail!'),), (InlineKeyboardButton(callback_data='hash', text='First attachment bytes hash!'),), (InlineKeyboardButton(callback_data='restart', text='Restart!'), InlineKeyboardButton(callback_data='quit', text='Quit!')))), message_effect_id=None, reply_to_message_id=None)" ] }, { @@ -255,7 +273,7 @@ "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"quit\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":\"Bot has entered unrecoverable state:\/\\nRun \/start command again to restart.\",\"commands\":null,\"attachments\":null,\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_message(42, 'Bot has entered unrecoverable state:/\\nRun /start command again to restart.', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None)" + "send_message(42, 'Bot has entered unrecoverable state:/\\nRun /start command again to restart.', parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" ] } ] diff --git a/tests/messengers/telegram/test_tutorials.py b/tests/messengers/telegram/test_tutorials.py index 1d53a16e4..6b05dc23f 100644 --- a/tests/messengers/telegram/test_tutorials.py +++ b/tests/messengers/telegram/test_tutorials.py @@ -15,7 +15,6 @@ @pytest.mark.skipif(not telegram_available, reason="Telegram dependencies missing") -@pytest.mark.asyncio @pytest.mark.parametrize( "tutorial_module_name", ["1_basic", "2_attachments", "3_advanced"], diff --git a/tests/messengers/telegram/utils.py b/tests/messengers/telegram/utils.py index 0a2e18b0f..8317e5f64 100644 --- a/tests/messengers/telegram/utils.py +++ b/tests/messengers/telegram/utils.py @@ -5,7 +5,7 @@ from typing import Any, Dict, Hashable, Iterator, List, Optional, Tuple from pydantic import BaseModel -from telegram import Update +from telegram import InputFile, InputMedia, Update from typing_extensions import TypeAlias from dff.messengers.telegram.abstract import _AbstractTelegramInterface @@ -38,8 +38,12 @@ class MockBot(BaseModel, arbitrary_types_allowed=True): @staticmethod def representation(any: Any) -> str: if isinstance(any, bytes): - any = sha256(any).hexdigest() - return any.__repr__() + return sha256(any).hexdigest().__repr__() + elif isinstance(any, list): + lst = [f"{type(a).__name__}(data='{sha256(a.media.input_file_content).hexdigest() if isinstance(a.media, InputFile) else '<>'}', type={a.type.__repr__()})" if isinstance(a, InputMedia) else a.__repr__() for a in any] + return f"[{', '.join(lst)}]" + else: + return any.__repr__() def __getattribute__(self, name: str) -> Any: async def set_trace(*args, **kwargs): diff --git a/tutorials/messengers/telegram/2_attachments.py b/tutorials/messengers/telegram/2_attachments.py index b6ef0911c..c47f17933 100644 --- a/tutorials/messengers/telegram/2_attachments.py +++ b/tutorials/messengers/telegram/2_attachments.py @@ -30,6 +30,7 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) Document, Location, Image, + MediaGroup, Poll, PollOption, Sticker, @@ -139,6 +140,9 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) ("main_flow", "video_message_node"): cnd.exact_match( Message("video message") ), + ("main_flow", "media_group"): cnd.exact_match( + Message("media group") + ), } }, "main_flow": { @@ -226,6 +230,21 @@ class and [python-telegram-bot](https://docs.python-telegram-bot.org/) attachments=[VideoMessage(source=video_data["source"])], ), }, + "media_group": { + RESPONSE: Message( + "Here's your media group!", + attachments=[ + MediaGroup( + group=[ + Image(**image_data), + Video(**video_data), + Animation(**animation_data), + Audio(**audio_data), + ], + ) + ], + ), + }, "fallback_node": { RESPONSE: Message( "Unknown attachment type, try again! " From d6dfccd7bfdb5ac9dbe9096866330baf2c02f51d Mon Sep 17 00:00:00 2001 From: pseusys Date: Thu, 27 Jun 2024 12:36:46 +0200 Subject: [PATCH 135/140] lint fixed --- dff/messengers/telegram/abstract.py | 67 +++++++++++++++++++++++++---- tests/messengers/telegram/utils.py | 10 +++-- 2 files changed, 65 insertions(+), 12 deletions(-) diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index b4ae85b77..4e7b85c24 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -14,7 +14,6 @@ from dff.pipeline.types import PipelineRunnerFunction from dff.script.core.message import ( Animation, - Attachment, Audio, CallbackQuery, Contact, @@ -305,7 +304,14 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes attachment.last_name, **grab_extra_fields( attachment, - ["vcard", "disable_notification", "protect_content", "reply_markup", "message_effect_id", "reply_to_message_id"], + [ + "vcard", + "disable_notification", + "protect_content", + "reply_markup", + "message_effect_id", + "reply_to_message_id", + ], ), ) elif isinstance(attachment, Poll): @@ -405,7 +411,7 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes ) elif isinstance(attachment, Image): attachment_bytes = await attachment.get_bytes(self) - if attachment_bytes is not None: + if attachment_bytes is not None: await bot.send_photo( chat_id, attachment_bytes, @@ -432,7 +438,14 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes sticker, **grab_extra_fields( attachment, - ["emoji", "disable_notification", "protect_content", "reply_markup", "message_effect_id", "reply_to_message_id"], + [ + "emoji", + "disable_notification", + "protect_content", + "reply_markup", + "message_effect_id", + "reply_to_message_id", + ], ), ) elif isinstance(attachment, Document): @@ -504,7 +517,14 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes InputMediaPhoto( media_bytes, **grab_extra_fields( - attachment, ["filename", "caption", "parse_mode", "has_spoiler", "show_caption_above_media"] + attachment, + [ + "filename", + "caption", + "parse_mode", + "has_spoiler", + "show_caption_above_media", + ], ), ), ] @@ -533,7 +553,15 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes InputMediaAnimation( media_bytes, **grab_extra_fields( - attachment, ["filename", "caption", "parse_mode", "has_spoiler", "thumbnail", "show_caption_above_media"] + attachment, + [ + "filename", + "caption", + "parse_mode", + "has_spoiler", + "thumbnail", + "show_caption_above_media", + ], ), ), ] @@ -542,7 +570,10 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes files += [ InputMediaAudio( media_bytes, - **grab_extra_fields(attachment, ["filename", "caption", "parse_mode", "performer", "title", "thumbnail"]), + **grab_extra_fields( + attachment, + ["filename", "caption", "parse_mode", "performer", "title", "thumbnail"], + ), ), ] elif isinstance(media, Document): @@ -558,7 +589,17 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes await bot.send_media_group( chat_id, files, - **grab_extra_fields(attachment, ["caption", "disable_notification", "protect_content", "message_effect_id", "reply_to_message_id", "parse_mode"]), + **grab_extra_fields( + attachment, + [ + "caption", + "disable_notification", + "protect_content", + "message_effect_id", + "reply_to_message_id", + "parse_mode", + ], + ), ) else: raise ValueError(f"Attachment {type(attachment).__name__} is not supported!") @@ -568,7 +609,15 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes message.text, **grab_extra_fields( message, - ["parse_mode", "disable_notification", "protect_content", "reply_markup", "message_effect_id", "reply_to_message_id", "disable_web_page_preview"], + [ + "parse_mode", + "disable_notification", + "protect_content", + "reply_markup", + "message_effect_id", + "reply_to_message_id", + "disable_web_page_preview", + ], ), ) diff --git a/tests/messengers/telegram/utils.py b/tests/messengers/telegram/utils.py index 8317e5f64..db6c1fe04 100644 --- a/tests/messengers/telegram/utils.py +++ b/tests/messengers/telegram/utils.py @@ -37,11 +37,15 @@ class MockBot(BaseModel, arbitrary_types_allowed=True): @staticmethod def representation(any: Any) -> str: - if isinstance(any, bytes): + if isinstance(any, InputFile): + return sha256(any.input_file_content).hexdigest() + elif isinstance(any, InputMedia): + data = MockBot.representation(any.media) if isinstance(any.media, InputFile) else "<>" + return f"{type(any).__name__}(data='{data}', type={any.type.__repr__()})" + elif isinstance(any, bytes): return sha256(any).hexdigest().__repr__() elif isinstance(any, list): - lst = [f"{type(a).__name__}(data='{sha256(a.media.input_file_content).hexdigest() if isinstance(a.media, InputFile) else '<>'}', type={a.type.__repr__()})" if isinstance(a, InputMedia) else a.__repr__() for a in any] - return f"[{', '.join(lst)}]" + return f"[{', '.join([MockBot.representation(a) for a in any])}]" else: return any.__repr__() From b1131ef7c7709f30bbee75478ee5a0aa2de5f8e7 Mon Sep 17 00:00:00 2001 From: pseusys Date: Thu, 27 Jun 2024 14:34:47 +0200 Subject: [PATCH 136/140] message send order updated --- dff/messengers/telegram/abstract.py | 34 +++++------ .../messengers/telegram/test_happy_paths.json | 60 +++++++++---------- 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index 4e7b85c24..3299f152b 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -277,6 +277,23 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes :param message: DFF message that will be processed into Telegram updates. """ + if message.text is not None: + await bot.send_message( + chat_id, + message.text, + **grab_extra_fields( + message, + [ + "parse_mode", + "disable_notification", + "protect_content", + "reply_markup", + "message_effect_id", + "reply_to_message_id", + "disable_web_page_preview", + ], + ), + ) if message.attachments is not None: for attachment in message.attachments: if isinstance(attachment, Location): @@ -603,23 +620,6 @@ async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, mes ) else: raise ValueError(f"Attachment {type(attachment).__name__} is not supported!") - if message.text is not None: - await bot.send_message( - chat_id, - message.text, - **grab_extra_fields( - message, - [ - "parse_mode", - "disable_notification", - "protect_content", - "reply_markup", - "message_effect_id", - "reply_to_message_id", - "disable_web_page_preview", - ], - ), - ) async def _on_event(self, update: Update, _: Any, create_message: Callable[[Update], Message]) -> None: """ diff --git a/tests/messengers/telegram/test_happy_paths.json b/tests/messengers/telegram/test_happy_paths.json index f0f1090cd..6d7ba8c14 100644 --- a/tests/messengers/telegram/test_happy_paths.json +++ b/tests/messengers/telegram/test_happy_paths.json @@ -31,8 +31,8 @@ "received_message": "{\"text\":\"location\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your location!\",\"commands\":null,\"attachments\":[{\"longitude\":3.916667,\"latitude\":50.65,\"dff_attachment_type\":\"location\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_location(42, 50.65, 3.916667, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None)", - "send_message(42, \"Here's your location!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" + "send_message(42, \"Here's your location!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)", + "send_location(42, 50.65, 3.916667, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None)" ] }, { @@ -40,8 +40,8 @@ "received_message": "{\"text\":\"contact\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your contact!\",\"commands\":null,\"attachments\":[{\"phone_number\":\"8-900-555-35-35\",\"first_name\":\"Hope\",\"last_name\":\"Credit\",\"dff_attachment_type\":\"contact\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_contact(42, '8-900-555-35-35', 'Hope', 'Credit', vcard=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None)", - "send_message(42, \"Here's your contact!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" + "send_message(42, \"Here's your contact!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)", + "send_contact(42, '8-900-555-35-35', 'Hope', 'Credit', vcard=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None)" ] }, { @@ -49,8 +49,8 @@ "received_message": "{\"text\":\"poll\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your poll!\",\"commands\":null,\"attachments\":[{\"question\":\"What is the poll question?\",\"options\":[{\"text\":\"This one!\",\"votes\":0,\"dff_attachment_type\":\"poll_option\"},{\"text\":\"Not this one :(\",\"votes\":0,\"dff_attachment_type\":\"poll_option\"}],\"dff_attachment_type\":\"poll\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_poll(42, 'What is the poll question?', ['This one!', 'Not this one :('], is_anonymous=None, type=None, allows_multiple_answers=None, correct_option_id=None, explanation=None, explanation_parse_mode=None, open_period=None, is_closed=None, disable_notification=None, protect_content=None, reply_markup=None, question_parse_mode=None, message_effect_id=None, reply_to_message_id=None)", - "send_message(42, \"Here's your poll!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" + "send_message(42, \"Here's your poll!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)", + "send_poll(42, 'What is the poll question?', ['This one!', 'Not this one :('], is_anonymous=None, type=None, allows_multiple_answers=None, correct_option_id=None, explanation=None, explanation_parse_mode=None, open_period=None, is_closed=None, disable_notification=None, protect_content=None, reply_markup=None, question_parse_mode=None, message_effect_id=None, reply_to_message_id=None)" ] }, { @@ -58,8 +58,8 @@ "received_message": "{\"text\":\"sticker\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your sticker!\",\"commands\":null,\"attachments\":[{\"source\":null,\"cached_filename\":null,\"id\":\"CAACAgIAAxkBAAErAAFXZibO5ksphCKSXSe1CYiw5588yqsAAkEAAzyKVxogmx2BPCogYDQE\",\"title\":\"A sticker I've just found\",\"use_cache\":true,\"dff_attachment_type\":\"sticker\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_sticker(42, 'CAACAgIAAxkBAAErAAFXZibO5ksphCKSXSe1CYiw5588yqsAAkEAAzyKVxogmx2BPCogYDQE', emoji=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None)", - "send_message(42, \"Here's your sticker!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" + "send_message(42, \"Here's your sticker!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)", + "send_sticker(42, 'CAACAgIAAxkBAAErAAFXZibO5ksphCKSXSe1CYiw5588yqsAAkEAAzyKVxogmx2BPCogYDQE', emoji=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None)" ] }, { @@ -67,8 +67,8 @@ "received_message": "{\"text\":\"audio\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your audio!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/separation-william-king.mp3\",\"cached_filename\":null,\"id\":null,\"title\":\"Separation melody by William King\",\"use_cache\":true,\"dff_attachment_type\":\"audio\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_audio(42, '75ff19e65eef262114e1a6d93794ed8b4af6135a0cd0620f58023fd5b57204f4', caption=None, parse_mode=None, performer=None, title=None, disable_notification=None, protect_content=None, reply_markup=None, thumbnail=None, message_effect_id=None, reply_to_message_id=None, filename=None)", - "send_message(42, \"Here's your audio!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" + "send_message(42, \"Here's your audio!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)", + "send_audio(42, '75ff19e65eef262114e1a6d93794ed8b4af6135a0cd0620f58023fd5b57204f4', caption=None, parse_mode=None, performer=None, title=None, disable_notification=None, protect_content=None, reply_markup=None, thumbnail=None, message_effect_id=None, reply_to_message_id=None, filename=None)" ] }, { @@ -76,8 +76,8 @@ "received_message": "{\"text\":\"video\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your video!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/crownfall-lags-nkognit0.mp4\",\"cached_filename\":null,\"id\":null,\"title\":\"Epic Dota2 gameplay by Nkognit0\",\"use_cache\":true,\"dff_attachment_type\":\"video\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_video(42, '11d3472ad8534d632beca6a452e2dc03b7ec9f4aba19fbb3346da43bac7389d6', caption=None, parse_mode=None, supports_streaming=None, disable_notification=None, protect_content=None, reply_markup=None, has_spoiler=None, thumbnail=None, message_effect_id=None, show_caption_above_media=None, reply_to_message_id=None, filename=None)", - "send_message(42, \"Here's your video!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" + "send_message(42, \"Here's your video!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)", + "send_video(42, '11d3472ad8534d632beca6a452e2dc03b7ec9f4aba19fbb3346da43bac7389d6', caption=None, parse_mode=None, supports_streaming=None, disable_notification=None, protect_content=None, reply_markup=None, has_spoiler=None, thumbnail=None, message_effect_id=None, show_caption_above_media=None, reply_to_message_id=None, filename=None)" ] }, { @@ -85,8 +85,8 @@ "received_message": "{\"text\":\"animation\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your animation!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/hong-kong-simplyart4794.gif\",\"cached_filename\":null,\"id\":null,\"title\":\"Hong Kong skyscraper views by Simplyart4794\",\"use_cache\":true,\"dff_attachment_type\":\"animation\",\"filename\":\"hk.gif\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_animation(42, '033391bab1eaefd6d8aa6ceb667b5dbaada7c4d6b1649a726541924a74539dc5', caption=None, parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, has_spoiler=None, thumbnail=None, message_effect_id=None, show_caption_above_media=None, reply_to_message_id=None, filename='hk.gif')", - "send_message(42, \"Here's your animation!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" + "send_message(42, \"Here's your animation!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)", + "send_animation(42, '033391bab1eaefd6d8aa6ceb667b5dbaada7c4d6b1649a726541924a74539dc5', caption=None, parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, has_spoiler=None, thumbnail=None, message_effect_id=None, show_caption_above_media=None, reply_to_message_id=None, filename='hk.gif')" ] }, { @@ -94,8 +94,8 @@ "received_message": "{\"text\":\"image\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your image!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/deeppavlov.png\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov logo\",\"use_cache\":true,\"dff_attachment_type\":\"image\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_photo(42, '9f39f64560e6415032325cae1fec1ca06b3cc1a3549208a8a35564c0d3749062', caption=None, parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, has_spoiler=None, message_effect_id=None, reply_to_message_id=None, filename=None)", - "send_message(42, \"Here's your image!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" + "send_message(42, \"Here's your image!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)", + "send_photo(42, '9f39f64560e6415032325cae1fec1ca06b3cc1a3549208a8a35564c0d3749062', caption=None, parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, has_spoiler=None, message_effect_id=None, reply_to_message_id=None, filename=None)" ] }, { @@ -103,8 +103,8 @@ "received_message": "{\"text\":\"document\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your document!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/deeppavlov-article.pdf\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov article\",\"use_cache\":true,\"dff_attachment_type\":\"document\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_document(42, '08a7a36671950baba722d94a87dea286f03d7a6f7d0c32d5707d877cd211739a', caption=None, parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, thumbnail=None, message_effect_id=None, reply_to_message_id=None, filename=None)", - "send_message(42, \"Here's your document!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" + "send_message(42, \"Here's your document!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)", + "send_document(42, '08a7a36671950baba722d94a87dea286f03d7a6f7d0c32d5707d877cd211739a', caption=None, parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, thumbnail=None, message_effect_id=None, reply_to_message_id=None, filename=None)" ] }, { @@ -112,8 +112,8 @@ "received_message": "{\"text\":\"voice message\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your voice message!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/separation-william-king.mp3\",\"cached_filename\":null,\"id\":null,\"title\":null,\"use_cache\":true,\"dff_attachment_type\":\"voice_message\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_voice(42, '75ff19e65eef262114e1a6d93794ed8b4af6135a0cd0620f58023fd5b57204f4', caption=None, parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, filename=None)", - "send_message(42, \"Here's your voice message!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" + "send_message(42, \"Here's your voice message!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)", + "send_voice(42, '75ff19e65eef262114e1a6d93794ed8b4af6135a0cd0620f58023fd5b57204f4', caption=None, parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, filename=None)" ] }, { @@ -121,8 +121,8 @@ "received_message": "{\"text\":\"video message\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your video message!\",\"commands\":null,\"attachments\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/crownfall-lags-nkognit0.mp4\",\"cached_filename\":null,\"id\":null,\"title\":null,\"use_cache\":true,\"dff_attachment_type\":\"video_message\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_video_note(42, '11d3472ad8534d632beca6a452e2dc03b7ec9f4aba19fbb3346da43bac7389d6', disable_notification=None, protect_content=None, reply_markup=None, thumbnail=None, message_effect_id=None, reply_to_message_id=None, filename=None)", - "send_message(42, \"Here's your video message!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" + "send_message(42, \"Here's your video message!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)", + "send_video_note(42, '11d3472ad8534d632beca6a452e2dc03b7ec9f4aba19fbb3346da43bac7389d6', disable_notification=None, protect_content=None, reply_markup=None, thumbnail=None, message_effect_id=None, reply_to_message_id=None, filename=None)" ] }, { @@ -130,8 +130,8 @@ "received_message": "{\"text\":\"media group\",\"commands\":null,\"attachments\":[],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_message": "{\"text\":\"Here's your media group!\",\"commands\":null,\"attachments\":[{\"group\":[{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/deeppavlov.png\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov logo\",\"use_cache\":true,\"dff_attachment_type\":\"image\"},{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/crownfall-lags-nkognit0.mp4\",\"cached_filename\":null,\"id\":null,\"title\":\"Epic Dota2 gameplay by Nkognit0\",\"use_cache\":true,\"dff_attachment_type\":\"video\"},{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/hong-kong-simplyart4794.gif\",\"cached_filename\":null,\"id\":null,\"title\":\"Hong Kong skyscraper views by Simplyart4794\",\"use_cache\":true,\"dff_attachment_type\":\"animation\",\"filename\":\"hk.gif\"},{\"source\":\"https://github.com/deeppavlov/dialog_flow_framework/wiki/example_attachments/separation-william-king.mp3\",\"cached_filename\":null,\"id\":null,\"title\":\"Separation melody by William King\",\"use_cache\":true,\"dff_attachment_type\":\"audio\"}],\"dff_attachment_type\":\"media_group\"}],\"annotations\":null,\"misc\":null,\"original_message\":null}", "response_functions": [ - "send_media_group(42, [InputMediaPhoto(data='9f39f64560e6415032325cae1fec1ca06b3cc1a3549208a8a35564c0d3749062', type=), InputMediaVideo(data='11d3472ad8534d632beca6a452e2dc03b7ec9f4aba19fbb3346da43bac7389d6', type=), InputMediaAnimation(data='033391bab1eaefd6d8aa6ceb667b5dbaada7c4d6b1649a726541924a74539dc5', type=), InputMediaAudio(data='75ff19e65eef262114e1a6d93794ed8b4af6135a0cd0620f58023fd5b57204f4', type=)], caption=None, disable_notification=None, protect_content=None, message_effect_id=None, reply_to_message_id=None, parse_mode=None)", - "send_message(42, \"Here's your media group!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" + "send_message(42, \"Here's your media group!\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)", + "send_media_group(42, [InputMediaPhoto(data='9f39f64560e6415032325cae1fec1ca06b3cc1a3549208a8a35564c0d3749062', type=), InputMediaVideo(data='11d3472ad8534d632beca6a452e2dc03b7ec9f4aba19fbb3346da43bac7389d6', type=), InputMediaAnimation(data='033391bab1eaefd6d8aa6ceb667b5dbaada7c4d6b1649a726541924a74539dc5', type=), InputMediaAudio(data='75ff19e65eef262114e1a6d93794ed8b4af6135a0cd0620f58023fd5b57204f4', type=)], caption=None, disable_notification=None, protect_content=None, message_effect_id=None, reply_to_message_id=None, parse_mode=None)" ] }, { @@ -173,9 +173,9 @@ "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"attachments\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":\"Here's your message with multiple attachments (a location and a sticker)!\\nRun \/start command again to restart.\",\"commands\":null,\"attachments\":[{\"longitude\":30.3141,\"latitude\":59.9386,\"dff_attachment_type\":\"location\",\"__pickled_extra_fields__\":[]},{\"source\":null,\"cached_filename\":null,\"id\":\"CAACAgIAAxkBAAErBZ1mKAbZvEOmhscojaIL5q0u8vgp1wACRygAAiSjCUtLa7RHZy76ezQE\",\"title\":null,\"use_cache\":true,\"dff_attachment_type\":\"sticker\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ + "send_message(42, \"Here's your message with multiple attachments (a location and a sticker)!\\nRun /start command again to restart.\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" , "send_location(42, 59.9386, 30.3141, horizontal_accuracy=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None)", - "send_sticker(42, 'CAACAgIAAxkBAAErBZ1mKAbZvEOmhscojaIL5q0u8vgp1wACRygAAiSjCUtLa7RHZy76ezQE', emoji=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None)", - "send_message(42, \"Here's your message with multiple attachments (a location and a sticker)!\\nRun /start command again to restart.\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" + "send_sticker(42, 'CAACAgIAAxkBAAErBZ1mKAbZvEOmhscojaIL5q0u8vgp1wACRygAAiSjCUtLa7RHZy76ezQE', emoji=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None)" ] }, { @@ -191,8 +191,8 @@ "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"secret\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":\"Here's your secret image! Run \/start command again to restart.\",\"commands\":null,\"attachments\":[{\"source\":\"https:\/\/github.com\/deeppavlov\/dialog_flow_framework\/wiki\/example_attachments\/deeppavlov.png\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov logo\",\"use_cache\":true,\"dff_attachment_type\":\"image\",\"has_spoiler\":true,\"filename\":\"deeppavlov_logo.png\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_photo(42, '9f39f64560e6415032325cae1fec1ca06b3cc1a3549208a8a35564c0d3749062', caption=None, parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, has_spoiler=True, message_effect_id=None, reply_to_message_id=None, filename='deeppavlov_logo.png')", - "send_message(42, \"Here's your secret image! Run /start command again to restart.\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" + "send_message(42, \"Here's your secret image! Run /start command again to restart.\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)", + "send_photo(42, '9f39f64560e6415032325cae1fec1ca06b3cc1a3549208a8a35564c0d3749062', caption=None, parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, has_spoiler=True, message_effect_id=None, reply_to_message_id=None, filename='deeppavlov_logo.png')" ] }, { @@ -208,8 +208,8 @@ "received_message": "{\"text\":null,\"commands\":null,\"attachments\":[{\"query_string\":\"thumbnail\",\"dff_attachment_type\":\"callback_query\",\"__pickled_extra_fields__\":[]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_message": "{\"text\":\"Here's your document with tumbnail! Run \/start command again to restart.\",\"commands\":null,\"attachments\":[{\"source\":\"https:\/\/github.com\/deeppavlov\/dialog_flow_framework\/wiki\/example_attachments\/deeppavlov-article.pdf\",\"cached_filename\":null,\"id\":null,\"title\":\"DeepPavlov article\",\"use_cache\":true,\"dff_attachment_type\":\"document\",\"filename\":\"deeppavlov_article.pdf\",\"thumbnail\":\"gASVySsAAAAAAABCwisAAIlQTkcNChoKAAAADUlIRFIAAADIAAAAyAgCAAAAIjo5yQAAK4lJREFU\\neJzsnXdcFFfXx+\/sbF\/KsnSQLiiIUlSwgo+KYi9RAbvYS4wleZ7EqNFojEaTqOlqLLFh77F3jaCA\\nCEgREJDeWbb394PwIiIus7NTFrPfj3\/g7My9h+U3t557DlWj0QAjRrCGQrYBRj5MjMIyggtGYRnB\\nBaOwjOCCUVhGcMEoLCO4YBSWEVwwCssILhiFZQQXqGQbgBIlf6+qYhHZVuAMbEOzP0NhdifbDjS0\\n1xZLVbmSbBPwR1WuEsSQbQRK2quwgEZCtgWEoBGTbQFK2q2wjBg2RmEZwQWjsIzgglFYRnDBKCw0\\n8GXsEhGPL2PrU4hcRX1a4Y6dUYZFe13H0s7DEu9tTycxYflgp8Ro76sQhOipx2Ve3yZEMWH5KNfY\\nyZ1ua7nzUn7Q7ucjulrm7gz5FbWRNwv9l91fkDl1DuoSDJkPU1ilYh6DIt8QvD\/65qdcunCoS8LG\\nJ5OFCtZnAcddzCrOvex9raB7oHXWbJ+rmxPq9ZfF77Cgy8VyiYVGAzb13jvk3OYulnnPq10flnSh\\nQqqvgw8czw515FQOd31yLCuExxBM9rrDl5mk1zg3VLcrddizKo\/edmmhDsm704Z9HXywWMj74\/mI\\n9cEHm1t1JPM\/D0u7MGH5V0EHM2s6XMzrNanjPZK+Idz5YLtCGqzqyC0Jc46\/Vej\/6YO5tqyavvap\\n824vyxdYr4md8XHXs\/vShqZVO5\/L7ZNe4+zFLVjxYD4AQAMgCqQGQEOnKDtxC77scVitATFZoQo1\\nvDF+MgDgh6cfmdFFzSs6md3vyIuBEzzurX88tUzC\/Tsv6G6R76X8IImS3sKkblYvV\/c4nFdnczwr\\n1MO8JNz5ybGsAcR+K8TxYbZYTYgVTAum8GZhQHKVuylNYsuqiSvtXCs3+eT+Ig5NSgFqAMB837\/N\\naKIfnk4AAMSWek++umplwClvXsHauGl3Cv3rFGwf3qto76urHkUnlHfkyznBdi+aV5FV6xhkm\/Gf\\nDsn97FOTq9wjve5cyO39qNRnR8gvDTeoNRAF0mg0YF\/6kCflncrEFiGOqTym0Ncyj6RvhQg+WGEV\\nCS1\/SxlxIifk0sjVpSIelyGY3OkOC5axqPX\/1vQ8DEEaJ9MKAEB6tVNiRcchzvEAgP4OqSeHbWgo\\n4VhW6Ibgv\/akhQMAmFTFoq7nh1345que9b3bS75dvsCmTMzN4dt15BZtSYi8VeB37VWPdUH1n\/Y5\\nucOGXdPdOhsAcDqnz+K7S4tnRQoVzN3Ph18Yufa\/D+sHVeVi82eV7jIV7XFZpyDbTLK\/LeyB161b\\nR7YNOpNUJLBWbtVyA5WigiDIhCZdH3zQw7w0xDGlQGjzoMS3g0mln3Wuv1XO+dzeVVKzYNuMvzLD\\nxEpmgHXOZ4EnmLDcgV3dyaKwoZBO3MLYMu+JHvd9eK\/sODU+vPwqqfmSbufYNHlcWSeJitnBpJIC\\naUa7xVox6+4U+23svd+HV8BliJmwfIRrnA+vAADAospMaeJ+DmkMWOlrmXe\/2HdOlyte3CKJkpFY\\n4dXLLqNAaNPbLv19v0iB2FvNGsamwzh8i\/gCta9zhQkFdRuu515IqxQvGIFJgb1Pbv8l9OfA162L\\nAbInLXz14+VL+nb4dICLOas9dS\/txtbcasmmG3kH4ksUKizfhEsjV5vSDXo\/u1ai3Hgjb09c8ReD\\nXJf2dyLbHKS0g1mhUqVefTnHc9M\/e+KKsVUVAIDHFNIoKmzLxINSgfyTsy\/cNj688aKabFsQYegt\\n1vnUioWnMorr5GQbYhDk1UjD\/ng6pBNvX4SPgzmDbHO0YbgtVoVQPn5f8ph9yUZVteBaZnWnzY9+\\neVBAtiHaMFBh7Xtc7L0l9kxqBdmGGChCuWrJmRcDfk14Xiok25bWMThhVYrkkw+mRh9LrxIrtNym\\n1iDb\/2vnKFTaFhru5tQG73iy61GRSk2gTcgwLGHdf1nTdWvc0aSyNu88n9uLEIvIRKGCrxcEar9H\\nJFfPP5kxdt+zaq3vIfEY0DrWqr+zt95+pVQjsodNlYY5JVix6vC3ixw0GpBY4ZlU2RHh\/Xam9PPR\\n3Xo6m+NsF1IMQlg1YsWMo2kX0irJNqR9w6RSNg33WB7qTLYhwCCElVkuGrsvOaO8vR5HMTSW9u+w\\nbZQnDSZ5kEOysO69rBn95zO+tB0sUbYj+rtzL0T7kbsFRKaw9sYVLziZoUA2qDKiE64WzDuLAl14\\nLLIMIEdYagDWXc7ZcONDdkgiHWcu88QM3yCShvMkCEupUn95Oee7268IrvdfiCWbejbar58bl\/iq\\nSRDWsrOZO+4XElzpvxZTBnw+2m9ARwuC6yVaWPNPpO+KLSayRiMsGuXvOf4Ea4vQSemS0xlGVRGP\\nRKEevifpQW4NkZUSJ6z\/Xcz65WERYdUZaY5EoR79Z3JCAXEbFQQJ69ubucbROrnUSJRDdydllImI\\nqY4IYR14Urzq75cEVGREO1UixcDfEssEMgLqwl1Yj1\/xF5z8AI83tVNKBPLwXUkiOe5bHfgKK7Nc\\nNHx3klRpeO5C\/2KSioXzTqTLcfbhwlFYApky8mBqlViJXxVG0HEksWzLLXy3PfASlkoN5p\/ISCo2\\nUMdZI19dyb2agaOfEl7C+vFe\/tGnbTuCGiELDQBRh57nVuF1phIXYT15Vbf6cg4eJRvBkBqJctJf\\nKTg5l2AvLIlCFXUoVaY0OsO0A+ILBeuv4tIEYC+sZWdf5ODWwBrBnK+v5z1+xce8WIyFdTWjyrgb\\n2O6IPJgqUWC8soWlsKpE8jnH3xuRx4jBklstXXEuC9sysXSbWXAi\/Y\/22FypVeySZJqwTOQYqDSx\\naf4JpJSZ5j9kVuXAMoGKaS4zcxS49FEzzcizFS9gCDxe1jOwA2a\/GmbCupdTM+j3pwhPBRoU1k\/2\\nmL2KrZ+BQ3DRgM9lPLfGD9Qqu39+4pSlNr9ZRWOV95gtdvAnx1Y88XcwebS0B5OGTZA3bLpChUq9\\n7NyL9qgqWFRl+lpVr18ylVnOraaPOMWJLVRVf79CYvfoF0algQZq04ekYuEvDzHz7MXmhNAfj4qe\\nFrXLRXYNBQYQBWgaN86cLU1H9LTvwGX06GBWEF+5Pe7Nnc4uLr6+vjeuX5fL5X1Lz2\/9JkahUqeW\\nCp8VCwv5sttZNbXSdr95tf5a7pRAOzszDAIkYdAVVork3ltiK0WGFTsAOc4pR2gv6hsqFpt1\/NQp\\nb2\/vhusymWxu9Ox\/Hj4EAMyZO3fFZ5\/S6fSE+PiICRMBAIdjjgb3ehM\/Qq5SpxQLb2fX3M+tvZxR\\nhXmAOMJY3Nfx5\/Gd9S8HA2GtbZ8HubhMamSA7YI+jn4OpokJiTk52UOHDjUzb3lYKutFFpfLtbax\\nbroya\/qM+\/furV67ZmZ0dKslK1TqI4mlx5LKr2RUtUd9PV0R5O9oqmch+naFxXzZd3fy9SyEYAId\\nTRf1dZzZ07HpGHpg98DA7q3HdfH08mxxxdHREQCQnfXeYRYNpszo6TCjp0OZQLb19qtdsUUCWXs6\\n6r3+2sszs\/z0LETfFuvj05k\/Yzfiw5t+buabhnv0d9frvMqd27dTklM6enYcNnw4kvsVKvWeuOLV\\nl3Oq248HUeKKoAD9Gi29hFUqkDl9\/bBdTAaDnM2+Dncf2smSLANEctX2e6++u51f1x4CVQzsaHFz\\nYRuhubSjl7AWn8r49R9DP3hjxoQ3hnss6ONIegAWAMCrGunnl7JjnpYZ\/rv4bGVQNwf0jRZ6YfGl\\nSpu19+SGPf0Z2om3P9IHk\/kzhlxKq1xwMqOQT8ShBtREBdgemeqL+nH0L\/E313MNWVVUCrRrYucr\\n8wIMTVUAgBE+Vokrg0Z3sSLbEG0cfVqWVYE+aBlKYSnVmt1xBrMtqFZRheX0mnxGTV7DP0+W9OHH\\n3ef2ciTbsvdizaGfi\/bbPMKDUfuKKqoAakMceP3+CP04B2VXeCSxdMrh56hr1R8av4hdlsoue04V\\nV9FElZDmzR+mi2+XP\/fvt7Iy6PagiatXrny2YqVIKpOb2Co51mIbH7G9n5JjEMYzqJSqDSEcVCmi\\nUAor8Ic4cvZw1ErT\/Fjuiyt0YWmrn\/fr33\/vgf0UCvnjdOQ8S3q2cN688vLypisSS8+aTsMk9t1I\\ntaue3z7qtKBPBxQPohHW00JB4I+PUVSmJ5xXsVYpJ6jS97o7jhk7dsu2rVSqoedxeZfCgoIJ48ZX\\nVr51bEbKdan0j5JZIg2cjAe9XMweLe2J4kE0b\/aBeKJHV1Rhuf397+2e7NGiqtFjxnz3\/bb2qCoA\\nQAcnp1Nnzzg4ODS\/yKzNd7yzhffsGKQkLelLbH7do7xaFA\/qLCylWnOW2EwkjKoch3tb2eXafFND\\nBwzYvPU7GG5\/CSObcOzQ4fc9u1tsVkJAY5F93fbRzxQ5ac4jp5LR\/Ll1Ftbl9Mr8GuIWYFhlaQ73\\nttIk2mI7+fr6bv9pJ53eMrl3u8PHx+fnX39lMpktrnPK0+zv\/0iRCUix6nRKOYrj+DoL62Ryuc6V\\noIVZnmH\/z06KWtsWm42NzW+7d5ma6rsbbyD06dtn9Vdr373OrM23f7gDaP0qcCK3WhpfoPMxHt2E\\nJVOqL6dX6VoHOmiCUtvHu6C2vsodP\/9kb29PjEnEEBkVNSli0rvXmTV5No93k2ER2BWr84KWbsJK\\nKKirIMahT6O2jt9HlbURge6T5ct7BgURYQ+xrFm3zt3D493rpkUJJv\/vSE0kt7J1DjOpm7CQ5OXC\\nBIv0i6zqNk7oevv4fPzJUmLsIRgWi7Xz558gqJXUedaJByGllGB78qqlz4p0G+HpJqzrmUQkJKbI\\nBOY5N9u4h0LZvnMHAcaQRWdv70VLlrx7naKScTOvEG\/PZR1D0+ggrIwyUaYeu5LI4WZegeVthMpc\\nsGihR0cyVw4JYM68ue7u7u9et3hxBaiIPmHwIFe38bsOwnpCSMxdSCExzX+o\/R4nZ+ely5YRYAy5\\nmJqafva\/\/757HVIrTfP\/IdiY6y+qq0Q6rNPqICxiFhpYFZnUthYD5y9c0E5X2HUlbOjQ3n36vHud\\nU5RIsCVyleZ5qQ4Rl3UQVjIh4fk4JUnab+js7T1u\/HgCLDEQlq9c8e5FZlU28b3h5QwdVpqQCiu9\\nVJRXg\/9kRKNhad26AQDMmh3NYBic7x5+BHbv7u\/f8kQ\/rJKxKl8QbEkaHi3Wy2oiQl5RheU0sbbX\\nwsbGZviIEQRYYlAsXd7KgJImIjrT8a2cGhniANhIhXU3h4hMLLCijVlnRFQki0Vackey6NO3r5OT\\nU4uLlLYmzpgjlKmQJ7ZAKqzCWiIW5WCtm80wDE+KiCDADEODSqV+NHFCi4uwhIg1xRZkI47ViHRu\\ndVP3RX0UwFrngz2Dguzf9lgiHpXghFp0UcsNsNl0CnsQ5vWOHT9+5\/YdavWbngjSkJCW4V5OzUfd\\nbJDciUhYLyvF5UIi5iCQVnfWEaNGEmCDdjTyFLXwmJYbKKw+AGAvrA4dOvQMCoqLfbNR2Mp2D\/5U\\nId4pRiSsKjH5kWRodNrgsDCyrQAAdoAYAVpvwOscxKDBg5oLixSQZ4RAJCxDiILs69vV2tqabCsA\\nlbsAcBeQUnW\/kBAAviGl6iYyKjAdvCcUkuO72JzQ0FCyTSAZLy8vNptNrg0qNRAgiy+HqMWqM4BY\\ndZ28MYgGpj+qugMqgbYxFsxdBHPwGguGDR1y7sxZnApHSEa5qKdzyyhi74JIWHyyhUWj0VrdMiMe\\njSJHI7ml7Q6TMfjVHhgYSLqwXlZJMBPWS7LHWB09PU1MTMi1oRGqE8QM1nYDbItf5W6tedEQDMIB\\nd\/sQlo0NorUTAqCazwXmc8mqPSBQr5hVmFCAbKkc0eCd9GSW3fzIP2xOInm5uc+Skhpclkk\/O4kw\\nzF77iHFga4tj\/2KwSCSSixcuTI2aPPg\/A9PT0houurm5kWuVGllMhvbhLkej0cg2gVAqKipOHj8R\\nc\/RoUWHL+K5OLs7Z2WSmL0AY6rJtYUnwz3jeJtYGM8bCG4lE8v3WrTFHjkqlrQ9l7OxIPkRZK8Fo\\nHYv0tQYAAJfLJdsE3MnLy1uz6stH\/7R0Znd3d1+\/cUPTaguX2\/ZUH1cQ6qFtYRlCNEhDWWvAAZVK\\ndfP6jZijR+\/dvdv8OgzDYUOGRERF9g8JaX7dhOxgAgj10D7GWKRPhfCgurr67OnTMUdjXua8dTSX\\nx+ONHjt2yrSprY7T28tX0T6EhTq085d\/Z1\/F9JDtwj6Os4P1DW2anZW1e9euy5f+Fovf8pj19PKa\\nO2\/ekPChWlpoDPNL4kr7EJZIhNINN7daiu0OeqkAfQw0pVJ58fyFYzFH45\/EN9cHlUodNnz4lGlT\\nu3fvDrUV5FIkJNojGR1tC8sEVWxTbOHzsc+GTSTV1dVHDh0+deJEQUFB8+s8Hi8iKnJSZOS7Lu3v\\ng89HE18PQxDqoW1hmTLJb9XKyvQNRjKzpz266L8NJBTWxeajPAi+etWqvy9eqqt763Hfrl0jo6KG\\njxxhZqZbttzSUoLisrwPHhuRHsgXDRJk71nUQc6m4R72emQS2HwrD7WwYo4cbfoZgqCh4eFz58\/3\\n80eZXutVPsm51syRNTTtQ1jV1SScSMEWM3Pz2XPmTJ4y2YLH06ecFlNI4oEpiLztEQnLnAnzSc1Z\\nlfwsmcTa9aRnUM\/IqKiRo0frv1Igk8kUCpLPH7QWtKsVEAnL3ZJFbsrn4iKUuTc6mDO62HIaUuvo\\nY4AVh9ZQjhVH513Lo8eP61N1c1JTUrAqCjUOyEYUiITlYckmV1g5OTkymQxFyIbvRnl+N6plilQU\\nzAl2nKP38pX+vMjMJNsE4GGJ6CQ6ImEhnAjgh0wmi330KHTAAMxLlkqlL3NyGg6C0mg0Ty8vAtKl\\nFBYW1tY0HgB2cHTkIR51paam4mkXItwxFBabRv5SVkZ6OrbCevjgwYF9+588fiwQvFlBtbGxCRkQ\\nOmPWrKZc9hhSXV198MCBy39fzs7KaroIQZB\/QMC48eMmToqg0dvoZ69eJiFIZAu8bThIbkP0dvo7\\nkr8HfO\/uPayKqigvX7p4yYyp027dvCkQCKhUqourq6ubG5VKLS8vP3n8xISx47Z8++37HFfQcfHC\\nhWFhQ37asbNBVRYWFp5enjweT6PRPE1MXLt6zbgxY5KfPWv12ZI6WWM7V0vy6igEAJeNaJSJqMXy\\nsUUkUlx5lpRUV1en63Liu7zMyZkxdVpJSQmDwZgwaeL0mTPNzMzOnjmjUqpGjRktkUgOHvjr+LGY\\n3X\/sSkxI\/H3XH3quDjTw7Tff\/Ll7DwDAz98\/Iipy7LhxTXk0FArF6VOnYg4fSUlJmTBu\/A87to8c\\nNar5s2dSyiMPpu6N8FanthHwlwA6WiEN9YOoxTIjb\/F9dqTgt01VENBIpdLrV6\/pWVpeXl7kpIiS\\nkpKQ0NCbd++s37DBxcUlcuLELZu+3fbddyPCh1lZWa3f8PWtu3eDe\/dKiI+fEhkll+ubIOnrdev+\\n3L2HzeHs+PmnU2fPTIqIaJ6dhUajRURGnrlwftPmzVQqddnHS4\/HxDR9uvZKzvj9KUq1Rq7S3Lh2\\nXU9L9KeHE1KnHUTC8rThcFkkaGvx9LoVc4QhQfIfv6qBIM2li9rCvCCBTqezmMyZ0bP2HthvZ2fX\\nMNLKz2tcyxYKBNevXgUA2Nvb\/3Xo0JixYyEKRf8O0dLS0tzc\/PS5syNGajvIOiky4ujxY6ampqrX\\nmWvUAET8lbLhep4ZA742P2CkKzU+Pl5PS\/THgoV0tQWRXCiv8+WfTiE06dfi6XWLpjfu5Hu5KV0c\\nlU8eP66qrLTUI3Wqg4PDqbNnrJrFgHBxdaXT6U3NkrOLS8MPMAxv+\/EHkUikv4\/h4o8\/njJ1KtfC\\nos07\/fz9H8Q+4nA4ZQL58N1JiUUCPweTmGm+nW04e\/7YJZORn5y8rytS\/1WkU2snLqFhP0eHiRdO\\na1RVVS20aLVFXiFNIpFc1LvRsno7soirq+vylSs4HA6DwZg1Ozq4V6+mjyAIwspzFYmqGuBw6oez\\n4\/clJxYJLNnUMzO7dX49Czt96hQmluhJZ8SjbaTCCvVA+tXoj7uzYu0n\/IatA6kMTF9umVfY2AIf\\n2Lcf8z2NufPnP05MiE96+uWaNdiWjJqTM7t6WLKqxMqPDiQX82WJCYkvXhAdyvZdGFTIG3NheVoR\\nFOcEhjVbV9Wy\/j9l3+qt5nkFb\/r1V\/n5N65jP4ZlMBgGFdrU3oyRsCJopLfl0yJhjx8ff\/bjfrIt\\nqmeAhwUL8YomUmF525o4mhGRaXJ0mKRzx8ZzILcfMS7faSnovXv2NI+Y+KFizqQem951RahTiUB+\\n2zJc0AFNYmZs8bXTYdUJqbBgCvDRpVx0MBia6EmNm5JyOdj6eyuT26eJT69euYq3JYYAmw5\/P9or\\nnJaloVCVLOKGIu9jaCdL5DfrsC\/2UVfcT42G9hC6Ozf658RcYOcXtT65\/XnnTrwtMRD+efgwK2aL\\n85UvLLL0XcPTEwgCvvY6TGV0EFZvxFNN1Ezo9ibfyZkr7x30ZGZk7NlFTq5RIlEoFN9u\/AYAQCMj\\n8nYLBna00MkFVwdhdXMwdeW1zIONITCkGuka1\/BzYirtRa62Id3O7dtLiovxM8YQ2L93X3p6Gwlg\\nCKOfm26H0XVbT\/+Ph8W+6hIdTUJKkE0GldI4Kk\/JaGOFVywWf7pi5eGYozgZQwxqeYZaeF6jyNYo\\nXwF1HQTbAKoDhdkLZg8qKFbt+PFHsg18w0gf3daldRPW1O52+57gJaxQxzf+x0+etT0DjYuNPX3q\\n1PiPPsLJHlxRS5+oqjepxZebX2w4aqiu26uE6JkJ7mymSCol32Hp9fIH3d9Rt+1\/3ZzaermYmzPx\\n+lU7mLzJOlRWiaiWLz\/\/orlvUztBoaz8QlE4sIWq3kIj7x+YcWlfxZAQ8gOh17\/z7lyqju6Put3O\\npsNhXhi4kbQO9N7\/vA+FQrFk0eKKCkI3MfVCLZUXR6lqfwSg7f0DM1PN1i9royeRHwt9fm+d3bJ1\\ndsNFmEqFMLKzslYuW65Ukh9rCQmKypUasQ7bnVQYLJstDA8ls91yMKP3cdU5jJTOwprgZ2tnSsQS\\nPHL+efhw7ZerybaibVSCE+q6P3V9CobBuhV8SwvSjt+N6WJN17UjRCEsKgUa44tLuhil+o0xpia6\\nfY\/Hjx37et06HIzCDrVIWbUW3aOmHM2SGaR1iONR9VFoTqRMCbRD8VSbpFa9CQfVK0Bnv82\/9h\/Y\\ntuU7rI3CDJXwIlDmon58fLjEzpqE7r6LLWcwqlE1GmH1d7fwssbeF+Dqq+5NP3u6ofkSf\/\/tt7Wr\\n16hV5AdNfReV6Lw+j1OpYEAvEhz95gSjTBCJ8gzd8hBndA9qoUBoWyZuHCSGBMnsrNHo48ihQ4sX\\nLRIKyTxe2yptJEpBgI8X0YfrYQhM64Eyli5KYc3sac\/UfUDXJmdeNoZwhWEQPgDlVOj61WsfjR2n\\nf+QjjFHrm6LWxZHornB2sIOl7iEFGkApDiYNnhOMfVzo\/Sn\/afp5yliRqQlKv6uc7OzhQ4ZeuqCX\\nH7NYrhLK6v\/pU0gDapVQqGAKFUypEn3AeksLor3QPu6HNBzcu6BvdT4f5IZ59ti0XM6dR41b6A62\\n6qjR6MMi8vn85Z98surzL1B3iyG\/JJiuumO66o5K7z9oeoXGas8pqz2nIq+uQl2IWEJott5hnXk6\\n+cm0AL2wHM0Zk\/wxXixV0Tnb95oq\/7+NmDdZ6OyAvv1Xq9XHY2LGjBx55\/Ztfayqk+o7uGnK5kCB\\n0Is0v5DQE3irBuuVW0WvcdK3Izrq8\/i7qBimWbm0\/ScaXVVZTLBjXTUV1itOcH5e\/pxZ0XOjZyPY\\nVVQCIH99pA+83r9q\/HKuvdDXHepKRlXDD\/Yc9EUVlRG3Id3L2UxXP5kW6CUsNx4rKgDT9ElUhgpm\\n7NxnmpLR+HZ6uat+2VjdPGy9h4fH77t37T1woE\/fvsgLvn3rVnjYkGUfLy0pafDOkKtEV5UVKxUl\\nkbK8TrJs5ut\/JrJsM1k2u\/7nHPN+vEMNzy46lXEtswr173QsqWzzrcYzsSH26ANc3biPozNcCzbr\\n3WRAesYNzygT+X0fJ1dhFnzc6fIXdHGFvY1y95ZqN6fGHuTvW8zV27gyef0g4+DhQ71fS0omk4X2\\n7VdZWYm8cEsLVXiodOFsDwtWUps5Fh6Vdh589juVprGdgCBgb0qnNAtoZ8aEfe1MhnlbTgm0o8GU\\nrArxTw8KnhYJCmplqtfJ19QaTUmdvKkaDlXyfMocOzaawB5PntFmrnxrw6POLaQicDqKotpkYEeL\\nmwv1TYyob7fd2ZYzr5fjzw9bJqlCjYrNBeKKknLq0rUWf2yudrCt75gCfOUOtsrc1+fAmP9\/TovB\\nYAwcNOj4MW0Zmpvo6KqYPEY8Llz8OlLQUySP9LbLmO1zedfzxnPxGg0ornt7P4AP0srEx5+Vv6yS\\nTPSz7fNTvPYp5Ajz8+hUpdaAXw8Sl+nkqyEYZK7DYC1q1WBXDGPBK9iN7+XLAtr8z3kl5ZQaPrRg\\nFS\/3\/08X7ty+XSJpXOKi0dveDre3UW78tPbUH5URo8RtxZ9qyQ\/9\/ljc9RyH2saK2jc38uafSNei\\nKopMwEs9lXn4ak4+mjf5xEXW4ySCTqJH+NuEYHE4GYOJhr0Z438DXdZceal\/UfXAb\/74LwtokxZa\\nmXA0r4rf2Hn\/3v2Tx49PmzGjzTDdNJpmyljR4ulCNgtlT02lqL\/vt+t\/gcfSa5wzapyqpGYaTWNX\\nqAGgSmp2vaB7Nt9RrQGPXgfrhpQyVkUGvbYA0rwWmUYDywT0uiIGv4CilAkBZeGXFod3VFlb6jA3\\njHtK3\/IbQRm\/WDTKhnAPTIrCZga7ItR5\/5MShGmotaOivjVErebD1e9kpTDnWjR4+V1\/\/6loF0fl\\n1ytre3TDYBvEhs23YaeEOrYy7lao4YmXV195FQRey8jhzhYm\/5WWoopKqVM+sfxpfU0nD0TLKBdv\\nMr\/eYd4wuCSA\/\/7HxdMamyPv2AiLTYe3juo4fj8GMX0VHOv3fdS7T28nJ+d+If2HjxhRP3PZtIn\/\\nngh3vQJl276ssTDHPZ8RjaL6LPCEs2lFQgrtZZpQu6oaKCqlzlhpOWuicE6kSEt87vJKyva9pueu\\nsRA60+qPE5fx6QDMtoD1nRU2Z\/Kh1KNP9d2hY1a+cLzbuvfL2Qvnfbt2BQBUVlRu2rjx\/Llzrd42\\nOkz87f+Izr0jkYLpyy3TsnRwgTThqKMnCXsFyrt1VjTNNWVyEJvIuBvLOH6J3dTttgrms8KbCwIG\\nemLmd46lsAprpQE\/PK4U6dX70GvynW5taPWjhYsXWfB4D+7de3D\/wfvCN0SMEq39BGVuEj1RqcC0\\nZZbP0tG417JZag5bU1tHUSiQtk\/YCmtyoO3hKb5YlYaxsAAAB+NLph9N06cESCF1O78EXes\/anB9\\nW4UwdQIeCITQvC94yai0pSsYCsvRjJG4MsjGBEuzMXZ9mdbDXk\/HZQ2NqTBB46Havats\/QoyVfXa\\no1rz84ZqF0eSs5Loyh8TO2OrKuyFBQDYPdHbxgS9c8jrHUOdN9XtrFXbv6phGMAhD0uuZuuqWiaj\\n3QRaig6yH6HjKWckYC8saxO6nr211FK3jSoqrNn6ZQ2Payg5bbt0Uq6cS\/5hQCR4WDJ\/Ht8Jj5Jx\\nSe8x2Iu3JswV9eMS68463T9ppDjQ17B6n4jR4uAALPMP4AGLRjkzyw95kD6dwCtvzBeDXAd7otwZ\\nUHJ0aJmteaolM9E3D2K913SFoubDusY\/EkwB\/50vYNB1a0RRZKHShz2TvLvq4cqnHbyExaLB+6N8\\nEGaKaoHCxFbBQrqgMm+y0NwUZSf4LI02bIbNzYfo\/5z7jnPGz7fKL6rXE9X2L6rN701faeeOytFh\\nYuRF+XTpcvn6tb79dPAF0ofFfR0n43OMrwEcM105mjPPzOrGoeteBQQpTBC5eXHNVJGjdfjjNaeq\\nBprzP15lNbz0K4vLt3V2ddJowE\/7TbbtMisqpc5cYQm438Cmk2CzaVT7kwBqbAbmRgnbdM5pILhX\\nr5gTx52dnXfv3dvFF8v1pFbp78b9YYwXrlXgm0Ktq73J4Sm+KFYAxPbdkNw2f4oQdRI4SwvNgqkN\\n7vDQp99YfL7ZHHmA\/tIKSvSnvN8PNbqyzJwoBMI\/gaq6vhPkDKc5xQF6vf2OdupxQ9vua1d+9tnh\\nmKNsNhsAEPvoURbOkbfdecwzs7rSYXz\/9Ljn5hvja71lpM7uiFKee5v3UGHN+GF6DZFmR4g+ia6D\\nKfWNyoUb7CFTbY6cY\/PrtL0IxWWUnftMhs+wefyM8bpt1Xy+iD9jghgoX8oLBwNlaf13SvdgOMfC\\n1jsAzWPCiPc2qAwGY9z48ddu3li4eFHDlevXri9esFD\/7D1asDGh3VoUaMnBfWEG45X39\/H5xawt\\nt9veoH2DRuN8ZRVNrC0+0eC+kh3rMUizdvwia\/OvbzwITDjqIf2lQX4ydxcVk6GhUTVCMSSRQk+e\\n0VMy6HfjGE1beEyGZv3y2pGDm83+qB40+yMURlOGerVKeOnbHxLjnyQIBHXVVdVMJtPaxsbe3r5P\\nv77Dhg+3sXlzGuXk8RNfrVmDPK8JipV3Dh2+Pt+\/t+6hY1BAkLAAAFGHUmN02aK2jt9vlv9Ayw2b\\n\/lszZgg2U\/rMHOoXW8wzX+rwHnu5KTZ8Wuvb6R3vF8iEarkR5s5B7jnC5\/N\/3Pb9oYMHdbBYd2Gx\\naZQjU33H+L7XeQRbiBOWSg0iDyafTEYaJI1TmGAX99v7P9c8vVyqq0eoFtRqcOEm6\/eDJs2dClvF\\n3kY5f4pwzBCJltoheleq5UYKZ5B2eUml0nNnzn6\/bVt1lc6HNXQSFgRAzDTfSf6YnnzRCnFH1WAK\\nODzFV6ZMuZCG6PiDyMFPyeRSpa13dl07KzBUVf3AiALGhElGDpQkZ9Cu32dm5VKT0uhiSeMYlGeu\\n8vFSuDsrB\/aRBXaVtznw1chTFCVjANUVNp0EMfwlmkEmpm+8QIsKC+Pi4uJiY2\/duFlTo+\/R+zaB\\nADg+3XeCH3GqIlRYAAA6lXJiRtfIgylnUxFoi0IV2fuZ595t9cPOHrgstcMwCOiiCOiiaGjDauvq\\nR1cmHA3KpTJlnqrmu\/pZwnLLogpnCx5PrVaXFBfX1RHn2ANB4ECkD8GqImJW2AIGlXJierepyJbm\\nhI7d3\/cRujhHOkGhAB5X42inRr0A24S7s7K0tDQ9LS0zI4NIVVEp0MnpXVFHjNEHooX1epkAOjCl\\ny+ygtn9bqa2Pktn6OYIunoa1Oagdax4JIbtM6PDfc\/zQxePTHxKE1VDrngifTcPbXqyq9Rza6nU3\\np\/YRzbYB4q3lsakPlnQP0yWtEraQI6wGvhjktjfCm03TZkOdW381pZWBoLmZoTjJIMEWVRA51Pg7\\nmCStDPZzJO6M67uQKSwAwKwgh2vzA7Q4BmpoLIFTL2KNwh5TDnGvwfDOltfnBzhxiQv00CokCwsA\\n0NeNm\/xpr27v99+o6TJWQ9QRKJwgzGF69WDXC3P8rbD2M0YB+cKq7ylM6XGf9JzXq\/U4qioWV+Dc\\nvhstKf5hablM6rlZ3TYM86AYxjtoEMJ6HXuS8sdE74OTfXjsVkZUtZ2Ga6C3TMVzoxZ7qmqw99JU\\nN\/vbBTmbJa4IGk3Udg0SDEVYDUztbv9gSY8+72TcVJjZ891Cm18pLDWItFgIKSzBQVg0VkMnu3qw\\n662FgW6ofCrxw7CE9TqrOefhxz02hLvT4bfa9NrOw1W0N2EFMnIw3dDBmZcF2O9wqJjm7pbM+4u7\\nbxjmwcEu2g9WGJywGlgd5pa0Mrhvs6ZLxbKo7jK26b\/J6e1JWDV8zL9naNmIwLT\/9u6rX0BH\/DBQ\\nYTU0XQ8+7vHLeK+mUON1riEy08YBfjGBATn1J+4pltM0H7+A0xcurJ8\/kYFDqH2sMFzLGljU1+n5\\nZ8GzguxhCgRgakXgdA1UL6l\/4gk90KIPFVUUvgCb18CCx1v99cbz505369oFkwLxw9CF9XoxgrE3\\nwufhku4DO1rIrDrWufYDAEhklLQsQl0zUHMvDoN3gMnmTJkx89qt2zOnT8HCKNxpH38bAECwi\/nN\\nhYE3s6qXnYD5RzLpwtIzV9g+nuQEltGJK\/f0WgSnUKnhI0Z99tlKpw46pzklEeI8SLFCrQaXHzzZ\\ntW1TWUHC3RPlZJvTBlU1lJCJNuiCp8FU2pjx42fPndvJE5vwjUTS\/oTVgFqtvnX7jrPJZhfLe2Tb\\noo29xzjf79Yt\/3t9P0JnTIyMmhU9y90V+yxrxNBehdWEVJSqqNpKkZykwgaXplClAn3H2wpEOgxk\\nbRw6RERGzps\/l2UIoXP0oN0LqwGVUiSu+AkSHaSBHLJteUPMedaGncjWmSAoZOCgydOmDx7QH3ez\\nCOEDEVYTgpoEWH5Jzf+FRiE6DGkLpDIQPs2morqNhQbvbv6TJ0cNGxbONde5xzRkPjRhNVFXeRWS\\n3wOSK1TVc9TH8PVhy2+mf51q3RfIxNSsq5\/f4LCwoeHhdrYGtHOMIR+ssJoQ8p8D6TW1+AakTKND\\nJcRU+iKXGrnYqnl8dogCe3h6+gd2Dw7uGRYWZsLBJpy6wfLhC6s5UmGyVBgPSf6mQ3lKSTqdhst4\\nXyyBJi2yzC2gmfOsnJw6dPMPCAsb7Onp+aE2Tq3y7xJWc6RSPlWTIxZmaqSJGlkCiy4W8DNNWSij\\njKhUQKp2YHNsJQrH\/MretfJe9ra2HTu6w6R0wwbAv1dYrVInKGFSSqmwhi8oA6pKJk0sEpZSNLVQ\\ns8yoSjWDzrCm0y3FcjbLxJFJ54hldCrTncn4wHs3nTAKywgu\/EsbaiN4YxSWEVwwCssILhiFZQQX\\njMIyggtGYRnBBaOwjOCCUVhGcOH\/AgAA\/\/+S+QX6OV9s5wAAAABJRU5ErkJggpQu\\n\",\"__pickled_extra_fields__\":[\"thumbnail\"]}],\"annotations\":null,\"misc\":null,\"original_message\":null,\"__pickled_extra_fields__\":[]}", "response_functions": [ - "send_document(42, '08a7a36671950baba722d94a87dea286f03d7a6f7d0c32d5707d877cd211739a', caption=None, parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, thumbnail='9f39f64560e6415032325cae1fec1ca06b3cc1a3549208a8a35564c0d3749062', message_effect_id=None, reply_to_message_id=None, filename='deeppavlov_article.pdf')", - "send_message(42, \"Here's your document with tumbnail! Run /start command again to restart.\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" + "send_message(42, \"Here's your document with tumbnail! Run /start command again to restart.\", parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)", + "send_document(42, '08a7a36671950baba722d94a87dea286f03d7a6f7d0c32d5707d877cd211739a', caption=None, parse_mode=None, disable_notification=None, protect_content=None, reply_markup=None, thumbnail='9f39f64560e6415032325cae1fec1ca06b3cc1a3549208a8a35564c0d3749062', message_effect_id=None, reply_to_message_id=None, filename='deeppavlov_article.pdf')" ] }, { From 40c7ad5bef820bb6bb43416670f6e38569e3b892 Mon Sep 17 00:00:00 2001 From: pseusys Date: Mon, 1 Jul 2024 11:38:33 +0200 Subject: [PATCH 137/140] Asynchronous interfaces for telegram created --- dff/messengers/telegram/abstract.py | 27 ++++++++++++++++++++++++++- dff/messengers/telegram/interface.py | 28 +++++++++++++++++++--------- tests/messengers/telegram/utils.py | 8 ++++---- 3 files changed, 49 insertions(+), 14 deletions(-) diff --git a/dff/messengers/telegram/abstract.py b/dff/messengers/telegram/abstract.py index 3299f152b..8f7222e30 100644 --- a/dff/messengers/telegram/abstract.py +++ b/dff/messengers/telegram/abstract.py @@ -5,6 +5,8 @@ Telegram API. """ +from abc import abstractmethod, ABC +from asyncio import Future from pathlib import Path from typing import Any, Callable, Optional @@ -54,7 +56,7 @@ telegram_available = False -class _AbstractTelegramInterface(MessengerInterfaceWithAttachments): +class _AbstractTelegramInterface(MessengerInterfaceWithAttachments, ABC): """ Messenger interface mixin for Telegram API usage. """ @@ -661,5 +663,28 @@ async def on_callback(self, update: Update, _: Any) -> None: update, _, lambda s: Message(attachments=[CallbackQuery(query_string=s.callback_query.data)]) ) + @abstractmethod + async def updater_coroutine(self): + raise NotImplementedError + async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs): self._pipeline_runner = pipeline_runner + + try: + await self.application.initialize() + if self.application.post_init: + await self.application.post_init(self.application) + await self.updater_coroutine() + await self.application.start() + await Future() + + finally: + if self.application.updater.running: + await self.application.updater.stop() + if self.application.running: + await self.application.stop() + if self.application.post_stop: + await self.application.post_stop(self.application) + await self.application.shutdown() + if self.application.post_shutdown: + await self.application.post_shutdown(self.application) diff --git a/dff/messengers/telegram/interface.py b/dff/messengers/telegram/interface.py index bcfabb0c1..d8111c9d3 100644 --- a/dff/messengers/telegram/interface.py +++ b/dff/messengers/telegram/interface.py @@ -5,17 +5,19 @@ :py:class:`~._AbstractTelegramInterface`. """ +from asyncio import get_event_loop from pathlib import Path from typing import Any, Optional -from dff.pipeline.types import PipelineRunnerFunction - from .abstract import _AbstractTelegramInterface try: from telegram import Update + from telegram.error import TelegramError + except ImportError: Update = Any + TelegramError = Any class LongpollingInterface(_AbstractTelegramInterface): @@ -35,10 +37,15 @@ def __init__( self.interval = interval self.timeout = timeout - async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs): - await super().connect(pipeline_runner, *args, **kwargs) - self.application.run_polling( - poll_interval=self.interval, timeout=self.timeout, allowed_updates=Update.ALL_TYPES + def error_callback(self, exc: TelegramError) -> None: + get_event_loop().create_task(self.application.process_error(error=exc, update=None)) + + async def updater_coroutine(self): + await self.application.updater.start_polling( + poll_interval=self.interval, + timeout=self.timeout, + allowed_updates=Update.ALL_TYPES, + error_callback=self.error_callback, ) @@ -60,6 +67,9 @@ def __init__( self.listen = host self.port = port - async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs): - await super().connect(pipeline_runner, *args, **kwargs) - self.application.run_webhook(listen=self.listen, port=self.port, allowed_updates=Update.ALL_TYPES) + async def updater_coroutine(self): + await self.application.updater.start_webhook( + listen=self.listen, + port=self.port, + allowed_updates=Update.ALL_TYPES, + ) diff --git a/tests/messengers/telegram/utils.py b/tests/messengers/telegram/utils.py index db6c1fe04..13ebfc31c 100644 --- a/tests/messengers/telegram/utils.py +++ b/tests/messengers/telegram/utils.py @@ -4,6 +4,7 @@ from hashlib import sha256 from typing import Any, Dict, Hashable, Iterator, List, Optional, Tuple +from dff.pipeline.types import PipelineRunnerFunction from pydantic import BaseModel from telegram import InputFile, InputMedia, Update from typing_extensions import TypeAlias @@ -71,6 +72,7 @@ class MockApplication(BaseModel, arbitrary_types_allowed=True): @classmethod def create(cls, interface: _AbstractTelegramInterface, happy_path: List[PathStep]) -> "MockApplication": instance = cls(bot=MockBot(), happy_path=happy_path, interface=interface) + interface.connect = instance.pseudo_connect return instance @contextmanager @@ -121,8 +123,6 @@ def _run_bot(self) -> None: else: raise RuntimeError(f"Update {update} type unknown!") - def run_polling(self, poll_interval: float, timeout: int, allowed_updates: List[str]) -> None: - return self._run_bot() - - def run_webhook(self, listen: str, port: str, allowed_updates: List[str]) -> None: + async def pseudo_connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs) -> None: + self.interface._pipeline_runner = pipeline_runner return self._run_bot() From 4fbf39baf60ffc6ec780d5ad31803868b8bcaa67 Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 2 Jul 2024 16:57:16 +0200 Subject: [PATCH 138/140] one whitespace removed --- tests/messengers/telegram/test_tutorials.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/messengers/telegram/test_tutorials.py b/tests/messengers/telegram/test_tutorials.py index 2ef501818..8cd39000a 100644 --- a/tests/messengers/telegram/test_tutorials.py +++ b/tests/messengers/telegram/test_tutorials.py @@ -20,7 +20,6 @@ "tutorial_module_name", ["1_basic", "2_attachments", "3_advanced"], ) - def test_tutorials(tutorial_module_name: str, monkeypatch): def patched_data_attachment_eq(self: DataAttachment, other: DataAttachment): first_copy = self.model_copy() From 4f1e1fca5b2a3e453f2a889c9f251e27e619dccb Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 2 Jul 2024 18:17:11 +0200 Subject: [PATCH 139/140] lint fixed --- tests/messengers/telegram/utils.py | 2 +- tutorials/messengers/telegram/3_advanced.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/messengers/telegram/utils.py b/tests/messengers/telegram/utils.py index 6a76ee085..5189f4fde 100644 --- a/tests/messengers/telegram/utils.py +++ b/tests/messengers/telegram/utils.py @@ -2,7 +2,7 @@ from contextlib import contextmanager from importlib import import_module from hashlib import sha256 -from typing import Any, Dict, Hashable, Iterator, List, Optional, Tuple, Union +from typing import Any, Dict, Hashable, Iterator, List, Optional, Tuple from dff.pipeline.types import PipelineRunnerFunction from pydantic import BaseModel diff --git a/tutorials/messengers/telegram/3_advanced.py b/tutorials/messengers/telegram/3_advanced.py index 556206c29..0bc262690 100644 --- a/tutorials/messengers/telegram/3_advanced.py +++ b/tutorials/messengers/telegram/3_advanced.py @@ -75,7 +75,7 @@ class for information about different arguments \> Here's a [link](https://core.telegram.org/bots/api\#formatting-options) in a quote\. \> Visit the link for more information about formatting options in telegram\. Run /start command again to restart\. -""" # noqa: W605 +""" # noqa: E501 location_data = {"latitude": 59.9386, "longitude": 30.3141} From 77fe9ebf69961bae683559425f8894af38e843f9 Mon Sep 17 00:00:00 2001 From: pseusys Date: Tue, 2 Jul 2024 18:32:24 +0200 Subject: [PATCH 140/140] happy file updated --- tests/messengers/telegram/test_happy_paths.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/messengers/telegram/test_happy_paths.json b/tests/messengers/telegram/test_happy_paths.json index 203848700..6d29ecc31 100644 --- a/tests/messengers/telegram/test_happy_paths.json +++ b/tests/messengers/telegram/test_happy_paths.json @@ -510,7 +510,7 @@ "original_message": "gASV+g8AAAAAAACMEHRlbGVncmFtLl91cGRhdGWUjAZVcGRhdGWUk5QpgZR9lCiMD19lZmZlY3Rp\ndmVfY2hhdJSMDnRlbGVncmFtLl9jaGF0lIwEQ2hhdJSTlCmBlH2UKIwKZmlyc3RfbmFtZZSMBFRl\nc3SUjAJpZJRLKowIaXNfZm9ydW2UTowJbGFzdF9uYW1llIwEVXNlcpSMBXRpdGxllE6MBHR5cGWU\njBJ0ZWxlZ3JhbS5jb25zdGFudHOUjAhDaGF0VHlwZZSTlIwHcHJpdmF0ZZSFlFKUjAh1c2VybmFt\nZZSMCXRlc3RfdXNlcpSMB19mcm96ZW6UiIwJX2lkX2F0dHJzlEsqhZSMCmFwaV9rd2FyZ3OUfZR1\nYowSX2VmZmVjdGl2ZV9tZXNzYWdllE6MEV9lZmZlY3RpdmVfc2VuZGVylE6MD19lZmZlY3RpdmVf\ndXNlcpROjBNidXNpbmVzc19jb25uZWN0aW9ulE6MEGJ1c2luZXNzX21lc3NhZ2WUTowOY2FsbGJh\nY2tfcXVlcnmUjBd0ZWxlZ3JhbS5fY2FsbGJhY2txdWVyeZSMDUNhbGxiYWNrUXVlcnmUk5QpgZR9\nlCiMDWNoYXRfaW5zdGFuY2WUjAItMZSMBGRhdGGUjAlmb3JtYXR0ZWSUjAlmcm9tX3VzZXKUjA50\nZWxlZ3JhbS5fdXNlcpRoEJOUKYGUfZQojBhhZGRlZF90b19hdHRhY2htZW50X21lbnWUTowXY2Fu\nX2Nvbm5lY3RfdG9fYnVzaW5lc3OUTowPY2FuX2pvaW5fZ3JvdXBzlE6MG2Nhbl9yZWFkX2FsbF9n\ncm91cF9tZXNzYWdlc5ROaAtoDGgNSyqMBmlzX2JvdJSJjAppc19wcmVtaXVtlE6MDWxhbmd1YWdl\nX2NvZGWUjAJlbpRoD2gQjBdzdXBwb3J0c19pbmxpbmVfcXVlcmllc5ROaBloGmgbiGgcSyqFlGge\nfZR1YowPZ2FtZV9zaG9ydF9uYW1llE5oDYwBMZSMEWlubGluZV9tZXNzYWdlX2lklE6MB21lc3Nh\nZ2WUjBF0ZWxlZ3JhbS5fbWVzc2FnZZSMB01lc3NhZ2WUk5QpgZR9lCiMFV9lZmZlY3RpdmVfYXR0\nYWNobWVudJSMHHRlbGVncmFtLl91dGlscy5kZWZhdWx0dmFsdWWUjAxEZWZhdWx0VmFsdWWUk5Qp\ngZROfZSMBXZhbHVllE5zhpRijAlhbmltYXRpb26UTowFYXVkaW+UTowQYXV0aG9yX3NpZ25hdHVy\nZZROjAtib29zdF9hZGRlZJROjBZidXNpbmVzc19jb25uZWN0aW9uX2lklE6MB2NhcHRpb26UTowQ\nY2FwdGlvbl9lbnRpdGllc5QpjBRjaGFubmVsX2NoYXRfY3JlYXRlZJSJjBNjaGF0X2JhY2tncm91\nbmRfc2V0lE6MC2NoYXRfc2hhcmVklE6MEWNvbm5lY3RlZF93ZWJzaXRllE6MB2NvbnRhY3SUTowR\nZGVsZXRlX2NoYXRfcGhvdG+UiYwEZGljZZROjAhkb2N1bWVudJROjAllZGl0X2RhdGWUTowJZWZm\nZWN0X2lklE6MCGVudGl0aWVzlCmMDmV4dGVybmFsX3JlcGx5lE6MEmZvcnVtX3RvcGljX2Nsb3Nl\nZJROjBNmb3J1bV90b3BpY19jcmVhdGVklE6MEmZvcnVtX3RvcGljX2VkaXRlZJROjBRmb3J1bV90\nb3BpY19yZW9wZW5lZJROjA5mb3J3YXJkX29yaWdpbpROaC9oMSmBlH2UKGg0Tmg1Tmg2Tmg3TmgL\njANCb3SUaA1LEGg4iGg5Tmg6TmgPTmg8TmgZjAdkZmZfYm90lGgbiGgcSxCFlGgefZR1YowEZ2Ft\nZZROjBpnZW5lcmFsX2ZvcnVtX3RvcGljX2hpZGRlbpROjBxnZW5lcmFsX2ZvcnVtX3RvcGljX3Vu\naGlkZGVulE6MCGdpdmVhd2F5lE6MEmdpdmVhd2F5X2NvbXBsZXRlZJROjBBnaXZlYXdheV9jcmVh\ndGVklE6MEGdpdmVhd2F5X3dpbm5lcnOUTowSZ3JvdXBfY2hhdF9jcmVhdGVklImMEWhhc19tZWRp\nYV9zcG9pbGVylE6MFWhhc19wcm90ZWN0ZWRfY29udGVudJROjAdpbnZvaWNllE6MFGlzX2F1dG9t\nYXRpY19mb3J3YXJklE6MD2lzX2Zyb21fb2ZmbGluZZROjBBpc190b3BpY19tZXNzYWdllE6MEGxl\nZnRfY2hhdF9tZW1iZXKUTowUbGlua19wcmV2aWV3X29wdGlvbnOUTowIbG9jYXRpb26UjBh0ZWxl\nZ3JhbS5fZmlsZXMubG9jYXRpb26UjAhMb2NhdGlvbpSTlCmBlH2UKIwHaGVhZGluZ5ROjBNob3Jp\nem9udGFsX2FjY3VyYWN5lE6MCGxhdGl0dWRllEdATTc/UvwmV4wLbGl2ZV9wZXJpb2SUTowJbG9u\nZ2l0dWRllEdAO8r6Hj6vaIwWcHJveGltaXR5X2FsZXJ0X3JhZGl1c5ROaBuIaBxHQDvK+h4+r2hH\nQE03P1L8JleGlGgefZR1YowObWVkaWFfZ3JvdXBfaWSUTowhbWVzc2FnZV9hdXRvX2RlbGV0ZV90\naW1lcl9jaGFuZ2VklE6MEW1lc3NhZ2VfdGhyZWFkX2lklE6MFG1pZ3JhdGVfZnJvbV9jaGF0X2lk\nlE6MEm1pZ3JhdGVfdG9fY2hhdF9pZJROjBBuZXdfY2hhdF9tZW1iZXJzlCmMDm5ld19jaGF0X3Bo\nb3RvlCmMDm5ld19jaGF0X3RpdGxllE6MDXBhc3Nwb3J0X2RhdGGUTowFcGhvdG+UKYwOcGlubmVk\nX21lc3NhZ2WUTowEcG9sbJROjBlwcm94aW1pdHlfYWxlcnRfdHJpZ2dlcmVklE6MBXF1b3RllE6M\nDHJlcGx5X21hcmt1cJSMJXRlbGVncmFtLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRtYXJrdXCUjBRJ\nbmxpbmVLZXlib2FyZE1hcmt1cJSTlCmBlH2UKIwPaW5saW5lX2tleWJvYXJklCiMJXRlbGVncmFt\nLl9pbmxpbmUuaW5saW5la2V5Ym9hcmRidXR0b26UjBRJbmxpbmVLZXlib2FyZEJ1dHRvbpSTlCmB\nlH2UKIwNY2FsbGJhY2tfZGF0YZRoLowNY2FsbGJhY2tfZ2FtZZROjAlsb2dpbl91cmyUTowDcGF5\nlE6ME3N3aXRjaF9pbmxpbmVfcXVlcnmUTowfc3dpdGNoX2lubGluZV9xdWVyeV9jaG9zZW5fY2hh\ndJROjCBzd2l0Y2hfaW5saW5lX3F1ZXJ5X2N1cnJlbnRfY2hhdJROjAR0ZXh0lIwUQ3V0ZSBmb3Jt\nYXR0ZWQgdGV4dCGUjAN1cmyUTowHd2ViX2FwcJROaBuIaBwoaK5OTmguTk5OTk50lGgefZR1YoWU\naKMpgZR9lChopowLYXR0YWNobWVudHOUaKdOaKhOaKlOaKpOaKtOaKxOaK2MFU11bHRpcGxlIGF0\ndGFjaG1lbnRzIZRor05osE5oG4hoHChot05OaLZOTk5OTnSUaB59lHVihZRooymBlH2UKGimjAZz\nZWNyZXSUaKdOaKhOaKlOaKpOaKtOaKxOaK2MDVNlY3JldCBpbWFnZSGUaK9OaLBOaBuIaBwoaL5O\nTmi9Tk5OTk50lGgefZR1YoWUaKMpgZR9lChopowJdGh1bWJuYWlslGinTmioTmipTmiqTmirTmis\nTmitjBhEb2N1bWVudCB3aXRoIHRodW1ibmFpbCGUaK9OaLBOaBuIaBwoaMVOTmjETk5OTk50lGge\nfZR1YoWUaKMpgZR9lChopowEaGFzaJRop05oqE5oqU5oqk5oq05orE5orYwcRmlyc3QgYXR0YWNo\nbWVudCBieXRlcyBoYXNoIZRor05osE5oG4hoHChozE5OaMtOTk5OTnSUaB59lHVihZRooymBlH2U\nKGimjAdyZXN0YXJ0lGinTmioTmipTmiqTmirTmisTmitjAhSZXN0YXJ0IZRor05osE5oG4hoHCho\n005OaNJOTk5OTnSUaB59lHViaKMpgZR9lChopowEcXVpdJRop05oqE5oqU5oqk5oq05orE5orYwF\nUXVpdCGUaK9OaLBOaBuIaBwoaNlOTmjYTk5OTk50lGgefZR1YoaUdJRoG4hoHGjdhZRoHn2UdWKM\nEHJlcGx5X3RvX21lc3NhZ2WUTowOcmVwbHlfdG9fc3RvcnmUTowSc2VuZGVyX2Jvb3N0X2NvdW50\nlE6ME3NlbmRlcl9idXNpbmVzc19ib3SUTowLc2VuZGVyX2NoYXSUTowYc2hvd19jYXB0aW9uX2Fi\nb3ZlX21lZGlhlE6MB3N0aWNrZXKUTowFc3RvcnmUTowSc3VjY2Vzc2Z1bF9wYXltZW50lE6MF3N1\ncGVyZ3JvdXBfY2hhdF9jcmVhdGVklIlorU6MDHVzZXJzX3NoYXJlZJROjAV2ZW51ZZROjAd2aWFf\nYm90lE6MBXZpZGVvlE6MEHZpZGVvX2NoYXRfZW5kZWSUTowfdmlkZW9fY2hhdF9wYXJ0aWNpcGFu\ndHNfaW52aXRlZJROjBR2aWRlb19jaGF0X3NjaGVkdWxlZJROjBJ2aWRlb19jaGF0X3N0YXJ0ZWSU\nTowKdmlkZW9fbm90ZZROjAV2b2ljZZROjAx3ZWJfYXBwX2RhdGGUTowUd3JpdGVfYWNjZXNzX2Fs\nbG93ZWSUTowEY2hhdJRoCYwEZGF0ZZSMCGRhdGV0aW1llIwIZGF0ZXRpbWWUk5RDCgfoBRUTCRQA\nAACUjARweXR6lIwEX1VUQ5STlClSlIaUUpSMCm1lc3NhZ2VfaWSUSwJoG4hoHEsCaAmGlGgefZR1\nYmgbiGgcaECFlGgefZR1YowMY2hhbm5lbF9wb3N0lE6MCmNoYXRfYm9vc3SUTowRY2hhdF9qb2lu\nX3JlcXVlc3SUTowLY2hhdF9tZW1iZXKUTowUY2hvc2VuX2lubGluZV9yZXN1bHSUTowZZGVsZXRl\nZF9idXNpbmVzc19tZXNzYWdlc5ROjBdlZGl0ZWRfYnVzaW5lc3NfbWVzc2FnZZROjBNlZGl0ZWRf\nY2hhbm5lbF9wb3N0lE6MDmVkaXRlZF9tZXNzYWdllE6MDGlubGluZV9xdWVyeZROaEJOjBBtZXNz\nYWdlX3JlYWN0aW9ulE6MFm1lc3NhZ2VfcmVhY3Rpb25fY291bnSUTowObXlfY2hhdF9tZW1iZXKU\nTmiXTowLcG9sbF9hbnN3ZXKUTowScHJlX2NoZWNrb3V0X3F1ZXJ5lE6MEnJlbW92ZWRfY2hhdF9i\nb29zdJROjA5zaGlwcGluZ19xdWVyeZROjAl1cGRhdGVfaWSUSwJoG4hoHEsChZRoHn2UdWIu\n" }, "response_message": { - "text": "\nVisit [this link](https://core.telegram.org/bots/api#formatting-options)\nfor more information about formatting options in telegram\\.\n\nRun /start command again to restart\\.\n", + "text": "\nHere's your formatted text\\!\nYou can see **text in bold**, _text in italic_ and a \\`code snippet\\`\\.\n\\> Here's a [link](https://core.telegram.org/bots/api\\#formatting-options) in a quote\\.\n\\> Visit the link for more information about formatting options in telegram\\.\nRun /start command again to restart\\.\n", "attachments": null, "annotations": null, "misc": null, @@ -518,7 +518,7 @@ "parse_mode": "MarkdownV2" }, "response_functions": [ - "send_message(42, '\\nVisit [this link](https://core.telegram.org/bots/api#formatting-options)\\nfor more information about formatting options in telegram\\\\.\\n\\nRun /start command again to restart\\\\.\\n', parse_mode=, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" + "send_message(42, \"\\nHere's your formatted text\\\\!\\nYou can see **text in bold**, _text in italic_ and a \\\\`code snippet\\\\`\\\\.\\n\\\\> Here's a [link](https:\/\/core.telegram.org/bots/api\\\\#formatting-options) in a quote\\\\.\\n\\\\> Visit the link for more information about formatting options in telegram\\\\.\\nRun /start command again to restart\\\\.\\n\", parse_mode=, disable_notification=None, protect_content=None, reply_markup=None, message_effect_id=None, reply_to_message_id=None, disable_web_page_preview=None)" ] }, {