mirror of
https://github.com/aiogram/aiogram.git
synced 2025-12-06 07:50:32 +00:00
Upgrade architecture + 5.0 Bot API (#469)
Upgrade architecture + 5.0 Bot API (#469) * Moved `methods`, `types` and `client` to root package * Removed update handler from routers to dispatcher * Reworked events propagation mechanism to handlers * Reworked inner middlewares logic (very small change) * Updated to Bot API 5.0 * Initial migration from MkDocs to Sphinx + config for readthedocs
This commit is contained in:
parent
566b7ff282
commit
4008a3114d
608 changed files with 12537 additions and 6427 deletions
2
.github/workflows/docs.yml
vendored
2
.github/workflows/docs.yml
vendored
|
|
@ -19,7 +19,7 @@ jobs:
|
|||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip poetry==1.0
|
||||
python -m pip install --upgrade pip poetry==1.1.4
|
||||
poetry install
|
||||
mkdir -p reports
|
||||
|
||||
|
|
|
|||
4
.github/workflows/tests.yml
vendored
4
.github/workflows/tests.yml
vendored
|
|
@ -13,6 +13,7 @@ jobs:
|
|||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
max-parallel: 6
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-latest
|
||||
|
|
@ -21,6 +22,7 @@ jobs:
|
|||
python-version:
|
||||
- 3.7
|
||||
- 3.8
|
||||
- 3.9
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
|
|
@ -32,7 +34,7 @@ jobs:
|
|||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip poetry==1.0
|
||||
python -m pip install --upgrade pip poetry==1.1.4
|
||||
poetry install
|
||||
|
||||
- name: Lint code
|
||||
|
|
|
|||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,10 +1,12 @@
|
|||
.idea/
|
||||
.vscode/
|
||||
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
||||
env/
|
||||
build/
|
||||
_build/
|
||||
dist/
|
||||
site/
|
||||
*.egg-info/
|
||||
|
|
|
|||
14
.readthedocs.yml
Normal file
14
.readthedocs.yml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
version: 2
|
||||
|
||||
sphinx:
|
||||
configuration: docs2/conf.py
|
||||
|
||||
formats: all
|
||||
|
||||
python:
|
||||
version: 3.8
|
||||
install:
|
||||
- method: pip
|
||||
path: .
|
||||
extra_requirements:
|
||||
- docs
|
||||
2
Makefile
2
Makefile
|
|
@ -70,7 +70,7 @@ clean:
|
|||
|
||||
.PHONY: isort
|
||||
isort:
|
||||
$(py) isort -rc aiogram tests
|
||||
$(py) isort aiogram tests
|
||||
|
||||
.PHONY: black
|
||||
black:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
from .api import methods, types
|
||||
from .api.client import session
|
||||
from .api.client.bot import Bot
|
||||
from .client import session
|
||||
from .client.bot import Bot
|
||||
from .dispatcher import filters, handler
|
||||
from .dispatcher.dispatcher import Dispatcher
|
||||
from .dispatcher.middlewares.base import BaseMiddleware
|
||||
|
|
@ -28,5 +27,5 @@ __all__ = (
|
|||
"handler",
|
||||
)
|
||||
|
||||
__version__ = "3.0.0a5"
|
||||
__api_version__ = "4.9"
|
||||
__version__ = "3.0.0a6"
|
||||
__api_version__ = "5.0"
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class TelegramAPIServer:
|
||||
"""
|
||||
Base config for API Endpoints
|
||||
"""
|
||||
|
||||
base: str
|
||||
file: str
|
||||
|
||||
def api_url(self, token: str, method: str) -> str:
|
||||
"""
|
||||
Generate URL for API methods
|
||||
|
||||
:param token: Bot token
|
||||
:param method: API method name (case insensitive)
|
||||
:return: URL
|
||||
"""
|
||||
return self.base.format(token=token, method=method)
|
||||
|
||||
def file_url(self, token: str, path: str) -> str:
|
||||
"""
|
||||
Generate URL for downloading files
|
||||
|
||||
:param token: Bot token
|
||||
:param path: file path
|
||||
:return: URL
|
||||
"""
|
||||
return self.file.format(token=token, path=path)
|
||||
|
||||
|
||||
# Main API server
|
||||
PRODUCTION = TelegramAPIServer(
|
||||
base="https://api.telegram.org/bot{token}/{method}",
|
||||
file="https://api.telegram.org/file/bot{token}/{path}",
|
||||
)
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, Optional
|
||||
|
||||
from .base import Request, TelegramMethod
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ..client.bot import Bot
|
||||
|
||||
|
||||
class AnswerCallbackQuery(TelegramMethod[bool]):
|
||||
"""
|
||||
Use this method to send answers to callback queries sent from inline keyboards. The answer
|
||||
will be displayed to the user as a notification at the top of the chat screen or as an alert.
|
||||
On success, True is returned.
|
||||
Alternatively, the user can be redirected to the specified Game URL. For this option to work,
|
||||
you must first create a game for your bot via @Botfather and accept the terms. Otherwise, you
|
||||
may use links like t.me/your_bot?start=XXXX that open your bot with a parameter.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#answercallbackquery
|
||||
"""
|
||||
|
||||
__returning__ = bool
|
||||
|
||||
callback_query_id: str
|
||||
"""Unique identifier for the query to be answered"""
|
||||
text: Optional[str] = None
|
||||
"""Text of the notification. If not specified, nothing will be shown to the user, 0-200
|
||||
characters"""
|
||||
show_alert: Optional[bool] = None
|
||||
"""If true, an alert will be shown by the client instead of a notification at the top of the
|
||||
chat screen. Defaults to false."""
|
||||
url: Optional[str] = None
|
||||
"""URL that will be opened by the user's client. If you have created a Game and accepted the
|
||||
conditions via @Botfather, specify the URL that opens your game — note that this will only
|
||||
work if the query comes from a callback_game button."""
|
||||
cache_time: Optional[int] = None
|
||||
"""The maximum amount of time in seconds that the result of the callback query may be cached
|
||||
client-side. Telegram apps will support caching starting in version 3.14. Defaults to 0."""
|
||||
|
||||
def build_request(self, bot: Bot) -> Request:
|
||||
data: Dict[str, Any] = self.dict()
|
||||
|
||||
return Request(method="answerCallbackQuery", data=data)
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, Optional
|
||||
|
||||
from .base import Request, TelegramMethod
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ..client.bot import Bot
|
||||
|
||||
|
||||
class AnswerPreCheckoutQuery(TelegramMethod[bool]):
|
||||
"""
|
||||
Once the user has confirmed their payment and shipping details, the Bot API sends the final
|
||||
confirmation in the form of an Update with the field pre_checkout_query. Use this method to
|
||||
respond to such pre-checkout queries. On success, True is returned. Note: The Bot API must
|
||||
receive an answer within 10 seconds after the pre-checkout query was sent.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#answerprecheckoutquery
|
||||
"""
|
||||
|
||||
__returning__ = bool
|
||||
|
||||
pre_checkout_query_id: str
|
||||
"""Unique identifier for the query to be answered"""
|
||||
ok: bool
|
||||
"""Specify True if everything is alright (goods are available, etc.) and the bot is ready to
|
||||
proceed with the order. Use False if there are any problems."""
|
||||
error_message: Optional[str] = None
|
||||
"""Required if ok is False. Error message in human readable form that explains the reason for
|
||||
failure to proceed with the checkout (e.g. "Sorry, somebody just bought the last of our
|
||||
amazing black T-shirts while you were busy filling out your payment details. Please choose
|
||||
a different color or garment!"). Telegram will display this message to the user."""
|
||||
|
||||
def build_request(self, bot: Bot) -> Request:
|
||||
data: Dict[str, Any] = self.dict()
|
||||
|
||||
return Request(method="answerPreCheckoutQuery", data=data)
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, Optional, Union
|
||||
|
||||
from ..types import UNSET, InlineKeyboardMarkup, Message
|
||||
from .base import Request, TelegramMethod, prepare_parse_mode
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ..client.bot import Bot
|
||||
|
||||
|
||||
class EditMessageCaption(TelegramMethod[Union[Message, bool]]):
|
||||
"""
|
||||
Use this method to edit captions of messages. On success, if edited message is sent by the
|
||||
bot, the edited Message is returned, otherwise True is returned.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#editmessagecaption
|
||||
"""
|
||||
|
||||
__returning__ = Union[Message, bool]
|
||||
|
||||
chat_id: Optional[Union[int, str]] = None
|
||||
"""Required if inline_message_id is not specified. Unique identifier for the target chat or
|
||||
username of the target channel (in the format @channelusername)"""
|
||||
message_id: Optional[int] = None
|
||||
"""Required if inline_message_id is not specified. Identifier of the message to edit"""
|
||||
inline_message_id: Optional[str] = None
|
||||
"""Required if chat_id and message_id are not specified. Identifier of the inline message"""
|
||||
caption: Optional[str] = None
|
||||
"""New caption of the message, 0-1024 characters after entities parsing"""
|
||||
parse_mode: Optional[str] = UNSET
|
||||
"""Mode for parsing entities in the message caption. See formatting options for more details."""
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None
|
||||
"""A JSON-serialized object for an inline keyboard."""
|
||||
|
||||
def build_request(self, bot: Bot) -> Request:
|
||||
data: Dict[str, Any] = self.dict()
|
||||
prepare_parse_mode(bot, data)
|
||||
|
||||
return Request(method="editMessageCaption", data=data)
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, Optional, Union
|
||||
|
||||
from ..types import InlineKeyboardMarkup, Message
|
||||
from .base import Request, TelegramMethod
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ..client.bot import Bot
|
||||
|
||||
|
||||
class EditMessageLiveLocation(TelegramMethod[Union[Message, bool]]):
|
||||
"""
|
||||
Use this method to edit live location messages. A location can be edited until its live_period
|
||||
expires or editing is explicitly disabled by a call to stopMessageLiveLocation. On success, if
|
||||
the edited message was sent by the bot, the edited Message is returned, otherwise True is
|
||||
returned.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#editmessagelivelocation
|
||||
"""
|
||||
|
||||
__returning__ = Union[Message, bool]
|
||||
|
||||
latitude: float
|
||||
"""Latitude of new location"""
|
||||
longitude: float
|
||||
"""Longitude of new location"""
|
||||
chat_id: Optional[Union[int, str]] = None
|
||||
"""Required if inline_message_id is not specified. Unique identifier for the target chat or
|
||||
username of the target channel (in the format @channelusername)"""
|
||||
message_id: Optional[int] = None
|
||||
"""Required if inline_message_id is not specified. Identifier of the message to edit"""
|
||||
inline_message_id: Optional[str] = None
|
||||
"""Required if chat_id and message_id are not specified. Identifier of the inline message"""
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None
|
||||
"""A JSON-serialized object for a new inline keyboard."""
|
||||
|
||||
def build_request(self, bot: Bot) -> Request:
|
||||
data: Dict[str, Any] = self.dict()
|
||||
|
||||
return Request(method="editMessageLiveLocation", data=data)
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, Optional, Union
|
||||
|
||||
from ..types import UNSET, InlineKeyboardMarkup, Message
|
||||
from .base import Request, TelegramMethod
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ..client.bot import Bot
|
||||
|
||||
|
||||
class EditMessageText(TelegramMethod[Union[Message, bool]]):
|
||||
"""
|
||||
Use this method to edit text and game messages. On success, if edited message is sent by the
|
||||
bot, the edited Message is returned, otherwise True is returned.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#editmessagetext
|
||||
"""
|
||||
|
||||
__returning__ = Union[Message, bool]
|
||||
|
||||
text: str
|
||||
"""New text of the message, 1-4096 characters after entities parsing"""
|
||||
chat_id: Optional[Union[int, str]] = None
|
||||
"""Required if inline_message_id is not specified. Unique identifier for the target chat or
|
||||
username of the target channel (in the format @channelusername)"""
|
||||
message_id: Optional[int] = None
|
||||
"""Required if inline_message_id is not specified. Identifier of the message to edit"""
|
||||
inline_message_id: Optional[str] = None
|
||||
"""Required if chat_id and message_id are not specified. Identifier of the inline message"""
|
||||
parse_mode: Optional[str] = UNSET
|
||||
"""Mode for parsing entities in the message text. See formatting options for more details."""
|
||||
disable_web_page_preview: Optional[bool] = None
|
||||
"""Disables link previews for links in this message"""
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None
|
||||
"""A JSON-serialized object for an inline keyboard."""
|
||||
|
||||
def build_request(self, bot: Bot) -> Request:
|
||||
data: Dict[str, Any] = self.dict()
|
||||
|
||||
return Request(method="editMessageText", data=data)
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, Union
|
||||
|
||||
from .base import Request, TelegramMethod
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ..client.bot import Bot
|
||||
|
||||
|
||||
class ExportChatInviteLink(TelegramMethod[str]):
|
||||
"""
|
||||
Use this method to generate a new invite link for a chat; any previously generated link is
|
||||
revoked. The bot must be an administrator in the chat for this to work and must have the
|
||||
appropriate admin rights. Returns the new invite link as String on success.
|
||||
Note: Each administrator in a chat generates their own invite links. Bots can't use invite
|
||||
links generated by other administrators. If you want your bot to work with invite links, it
|
||||
will need to generate its own link using exportChatInviteLink — after this the link will
|
||||
become available to the bot via the getChat method. If your bot needs to generate a new invite
|
||||
link replacing its previous one, use exportChatInviteLink again.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#exportchatinvitelink
|
||||
"""
|
||||
|
||||
__returning__ = str
|
||||
|
||||
chat_id: Union[int, str]
|
||||
"""Unique identifier for the target chat or username of the target channel (in the format
|
||||
@channelusername)"""
|
||||
|
||||
def build_request(self, bot: Bot) -> Request:
|
||||
data: Dict[str, Any] = self.dict()
|
||||
|
||||
return Request(method="exportChatInviteLink", data=data)
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict
|
||||
|
||||
from ..types import File
|
||||
from .base import Request, TelegramMethod
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ..client.bot import Bot
|
||||
|
||||
|
||||
class GetFile(TelegramMethod[File]):
|
||||
"""
|
||||
Use this method to get basic info about a file and prepare it for downloading. For the moment,
|
||||
bots can download files of up to 20MB in size. On success, a File object is returned. The file
|
||||
can then be downloaded via the link https://api.telegram.org/file/bot<token>/<file_path>,
|
||||
where <file_path> is taken from the response. It is guaranteed that the link will be valid for
|
||||
at least 1 hour. When the link expires, a new one can be requested by calling getFile again.
|
||||
Note: This function may not preserve the original file name and MIME type. You should save the
|
||||
file's MIME type and name (if available) when the File object is received.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#getfile
|
||||
"""
|
||||
|
||||
__returning__ = File
|
||||
|
||||
file_id: str
|
||||
"""File identifier to get info about"""
|
||||
|
||||
def build_request(self, bot: Bot) -> Request:
|
||||
data: Dict[str, Any] = self.dict()
|
||||
|
||||
return Request(method="getFile", data=data)
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
||||
|
||||
from ..types import Update
|
||||
from .base import Request, TelegramMethod
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ..client.bot import Bot
|
||||
|
||||
|
||||
class GetUpdates(TelegramMethod[List[Update]]):
|
||||
"""
|
||||
Use this method to receive incoming updates using long polling (wiki). An Array of Update
|
||||
objects is returned.
|
||||
Notes
|
||||
1. This method will not work if an outgoing webhook is set up.
|
||||
2. In order to avoid getting duplicate updates, recalculate offset after each server response.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#getupdates
|
||||
"""
|
||||
|
||||
__returning__ = List[Update]
|
||||
|
||||
offset: Optional[int] = None
|
||||
"""Identifier of the first update to be returned. Must be greater by one than the highest
|
||||
among the identifiers of previously received updates. By default, updates starting with the
|
||||
earliest unconfirmed update are returned. An update is considered confirmed as soon as
|
||||
getUpdates is called with an offset higher than its update_id. The negative offset can be
|
||||
specified to retrieve updates starting from -offset update from the end of the updates
|
||||
queue. All previous updates will forgotten."""
|
||||
limit: Optional[int] = None
|
||||
"""Limits the number of updates to be retrieved. Values between 1-100 are accepted. Defaults
|
||||
to 100."""
|
||||
timeout: Optional[int] = None
|
||||
"""Timeout in seconds for long polling. Defaults to 0, i.e. usual short polling. Should be
|
||||
positive, short polling should be used for testing purposes only."""
|
||||
allowed_updates: Optional[List[str]] = None
|
||||
"""A JSON-serialized list of the update types you want your bot to receive. For example,
|
||||
specify ['message', 'edited_channel_post', 'callback_query'] to only receive updates of
|
||||
these types. See Update for a complete list of available update types. Specify an empty
|
||||
list to receive all updates regardless of type (default). If not specified, the previous
|
||||
setting will be used."""
|
||||
|
||||
def build_request(self, bot: Bot) -> Request:
|
||||
data: Dict[str, Any] = self.dict()
|
||||
|
||||
return Request(method="getUpdates", data=data)
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, Optional, Union
|
||||
|
||||
from ..types import (
|
||||
UNSET,
|
||||
ForceReply,
|
||||
InlineKeyboardMarkup,
|
||||
InputFile,
|
||||
Message,
|
||||
ReplyKeyboardMarkup,
|
||||
ReplyKeyboardRemove,
|
||||
)
|
||||
from .base import Request, TelegramMethod, prepare_file
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ..client.bot import Bot
|
||||
|
||||
|
||||
class SendAnimation(TelegramMethod[Message]):
|
||||
"""
|
||||
Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On
|
||||
success, the sent Message is returned. Bots can currently send animation files of up to 50 MB
|
||||
in size, this limit may be changed in the future.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#sendanimation
|
||||
"""
|
||||
|
||||
__returning__ = Message
|
||||
|
||||
chat_id: Union[int, str]
|
||||
"""Unique identifier for the target chat or username of the target channel (in the format
|
||||
@channelusername)"""
|
||||
animation: Union[InputFile, str]
|
||||
"""Animation to send. Pass a file_id as String to send an animation that exists on the
|
||||
Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get an
|
||||
animation from the Internet, or upload a new animation using multipart/form-data."""
|
||||
duration: Optional[int] = None
|
||||
"""Duration of sent animation in seconds"""
|
||||
width: Optional[int] = None
|
||||
"""Animation width"""
|
||||
height: Optional[int] = None
|
||||
"""Animation height"""
|
||||
thumb: Optional[Union[InputFile, str]] = None
|
||||
"""Thumbnail of the file sent; can be ignored if thumbnail generation for the file is
|
||||
supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size.
|
||||
A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded
|
||||
using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new
|
||||
file, so you can pass 'attach://<file_attach_name>' if the thumbnail was uploaded using
|
||||
multipart/form-data under <file_attach_name>."""
|
||||
caption: Optional[str] = None
|
||||
"""Animation caption (may also be used when resending animation by file_id), 0-1024 characters
|
||||
after entities parsing"""
|
||||
parse_mode: Optional[str] = UNSET
|
||||
"""Mode for parsing entities in the animation caption. See formatting options for more
|
||||
details."""
|
||||
disable_notification: Optional[bool] = None
|
||||
"""Sends the message silently. Users will receive a notification with no sound."""
|
||||
reply_to_message_id: Optional[int] = None
|
||||
"""If the message is a reply, ID of the original message"""
|
||||
reply_markup: Optional[
|
||||
Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply]
|
||||
] = None
|
||||
"""Additional interface options. A JSON-serialized object for an inline keyboard, custom reply
|
||||
keyboard, instructions to remove reply keyboard or to force a reply from the user."""
|
||||
|
||||
def build_request(self, bot: Bot) -> Request:
|
||||
data: Dict[str, Any] = self.dict(exclude={"animation", "thumb"})
|
||||
|
||||
files: Dict[str, InputFile] = {}
|
||||
prepare_file(data=data, files=files, name="animation", value=self.animation)
|
||||
prepare_file(data=data, files=files, name="thumb", value=self.thumb)
|
||||
|
||||
return Request(method="sendAnimation", data=data, files=files)
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, Optional, Union
|
||||
|
||||
from ..types import (
|
||||
UNSET,
|
||||
ForceReply,
|
||||
InlineKeyboardMarkup,
|
||||
InputFile,
|
||||
Message,
|
||||
ReplyKeyboardMarkup,
|
||||
ReplyKeyboardRemove,
|
||||
)
|
||||
from .base import Request, TelegramMethod, prepare_file
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ..client.bot import Bot
|
||||
|
||||
|
||||
class SendAudio(TelegramMethod[Message]):
|
||||
"""
|
||||
Use this method to send audio files, if you want Telegram clients to display them in the music
|
||||
player. Your audio must be in the .MP3 or .M4A format. On success, the sent Message is
|
||||
returned. Bots can currently send audio files of up to 50 MB in size, this limit may be
|
||||
changed in the future.
|
||||
For sending voice messages, use the sendVoice method instead.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#sendaudio
|
||||
"""
|
||||
|
||||
__returning__ = Message
|
||||
|
||||
chat_id: Union[int, str]
|
||||
"""Unique identifier for the target chat or username of the target channel (in the format
|
||||
@channelusername)"""
|
||||
audio: Union[InputFile, str]
|
||||
"""Audio file to send. Pass a file_id as String to send an audio file that exists on the
|
||||
Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get an audio
|
||||
file from the Internet, or upload a new one using multipart/form-data."""
|
||||
caption: Optional[str] = None
|
||||
"""Audio caption, 0-1024 characters after entities parsing"""
|
||||
parse_mode: Optional[str] = UNSET
|
||||
"""Mode for parsing entities in the audio caption. See formatting options for more details."""
|
||||
duration: Optional[int] = None
|
||||
"""Duration of the audio in seconds"""
|
||||
performer: Optional[str] = None
|
||||
"""Performer"""
|
||||
title: Optional[str] = None
|
||||
"""Track name"""
|
||||
thumb: Optional[Union[InputFile, str]] = None
|
||||
"""Thumbnail of the file sent; can be ignored if thumbnail generation for the file is
|
||||
supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size.
|
||||
A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded
|
||||
using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new
|
||||
file, so you can pass 'attach://<file_attach_name>' if the thumbnail was uploaded using
|
||||
multipart/form-data under <file_attach_name>."""
|
||||
disable_notification: Optional[bool] = None
|
||||
"""Sends the message silently. Users will receive a notification with no sound."""
|
||||
reply_to_message_id: Optional[int] = None
|
||||
"""If the message is a reply, ID of the original message"""
|
||||
reply_markup: Optional[
|
||||
Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply]
|
||||
] = None
|
||||
"""Additional interface options. A JSON-serialized object for an inline keyboard, custom reply
|
||||
keyboard, instructions to remove reply keyboard or to force a reply from the user."""
|
||||
|
||||
def build_request(self, bot: Bot) -> Request:
|
||||
data: Dict[str, Any] = self.dict(exclude={"audio", "thumb"})
|
||||
|
||||
files: Dict[str, InputFile] = {}
|
||||
prepare_file(data=data, files=files, name="audio", value=self.audio)
|
||||
prepare_file(data=data, files=files, name="thumb", value=self.thumb)
|
||||
|
||||
return Request(method="sendAudio", data=data, files=files)
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, Union
|
||||
|
||||
from .base import Request, TelegramMethod
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ..client.bot import Bot
|
||||
|
||||
|
||||
class SendChatAction(TelegramMethod[bool]):
|
||||
"""
|
||||
Use this method when you need to tell the user that something is happening on the bot's side.
|
||||
The status is set for 5 seconds or less (when a message arrives from your bot, Telegram
|
||||
clients clear its typing status). Returns True on success.
|
||||
Example: The ImageBot needs some time to process a request and upload the image. Instead of
|
||||
sending a text message along the lines of 'Retrieving image, please wait…', the bot may use
|
||||
sendChatAction with action = upload_photo. The user will see a 'sending photo' status for the
|
||||
bot.
|
||||
We only recommend using this method when a response from the bot will take a noticeable amount
|
||||
of time to arrive.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#sendchataction
|
||||
"""
|
||||
|
||||
__returning__ = bool
|
||||
|
||||
chat_id: Union[int, str]
|
||||
"""Unique identifier for the target chat or username of the target channel (in the format
|
||||
@channelusername)"""
|
||||
action: str
|
||||
"""Type of action to broadcast. Choose one, depending on what the user is about to receive:
|
||||
typing for text messages, upload_photo for photos, record_video or upload_video for videos,
|
||||
record_audio or upload_audio for audio files, upload_document for general files,
|
||||
find_location for location data, record_video_note or upload_video_note for video notes."""
|
||||
|
||||
def build_request(self, bot: Bot) -> Request:
|
||||
data: Dict[str, Any] = self.dict()
|
||||
|
||||
return Request(method="sendChatAction", data=data)
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, Optional, Union
|
||||
|
||||
from ..types import (
|
||||
UNSET,
|
||||
ForceReply,
|
||||
InlineKeyboardMarkup,
|
||||
InputFile,
|
||||
Message,
|
||||
ReplyKeyboardMarkup,
|
||||
ReplyKeyboardRemove,
|
||||
)
|
||||
from .base import Request, TelegramMethod, prepare_file
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ..client.bot import Bot
|
||||
|
||||
|
||||
class SendDocument(TelegramMethod[Message]):
|
||||
"""
|
||||
Use this method to send general files. On success, the sent Message is returned. Bots can
|
||||
currently send files of any type of up to 50 MB in size, this limit may be changed in the
|
||||
future.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#senddocument
|
||||
"""
|
||||
|
||||
__returning__ = Message
|
||||
|
||||
chat_id: Union[int, str]
|
||||
"""Unique identifier for the target chat or username of the target channel (in the format
|
||||
@channelusername)"""
|
||||
document: Union[InputFile, str]
|
||||
"""File to send. Pass a file_id as String to send a file that exists on the Telegram servers
|
||||
(recommended), pass an HTTP URL as a String for Telegram to get a file from the Internet,
|
||||
or upload a new one using multipart/form-data."""
|
||||
thumb: Optional[Union[InputFile, str]] = None
|
||||
"""Thumbnail of the file sent; can be ignored if thumbnail generation for the file is
|
||||
supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size.
|
||||
A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded
|
||||
using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new
|
||||
file, so you can pass 'attach://<file_attach_name>' if the thumbnail was uploaded using
|
||||
multipart/form-data under <file_attach_name>."""
|
||||
caption: Optional[str] = None
|
||||
"""Document caption (may also be used when resending documents by file_id), 0-1024 characters
|
||||
after entities parsing"""
|
||||
parse_mode: Optional[str] = UNSET
|
||||
"""Mode for parsing entities in the document caption. See formatting options for more details."""
|
||||
disable_notification: Optional[bool] = None
|
||||
"""Sends the message silently. Users will receive a notification with no sound."""
|
||||
reply_to_message_id: Optional[int] = None
|
||||
"""If the message is a reply, ID of the original message"""
|
||||
reply_markup: Optional[
|
||||
Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply]
|
||||
] = None
|
||||
"""Additional interface options. A JSON-serialized object for an inline keyboard, custom reply
|
||||
keyboard, instructions to remove reply keyboard or to force a reply from the user."""
|
||||
|
||||
def build_request(self, bot: Bot) -> Request:
|
||||
data: Dict[str, Any] = self.dict(exclude={"document", "thumb"})
|
||||
|
||||
files: Dict[str, InputFile] = {}
|
||||
prepare_file(data=data, files=files, name="document", value=self.document)
|
||||
prepare_file(data=data, files=files, name="thumb", value=self.thumb)
|
||||
|
||||
return Request(method="sendDocument", data=data, files=files)
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
||||
|
||||
from ..types import InlineKeyboardMarkup, LabeledPrice, Message
|
||||
from .base import Request, TelegramMethod
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ..client.bot import Bot
|
||||
|
||||
|
||||
class SendInvoice(TelegramMethod[Message]):
|
||||
"""
|
||||
Use this method to send invoices. On success, the sent Message is returned.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#sendinvoice
|
||||
"""
|
||||
|
||||
__returning__ = Message
|
||||
|
||||
chat_id: int
|
||||
"""Unique identifier for the target private chat"""
|
||||
title: str
|
||||
"""Product name, 1-32 characters"""
|
||||
description: str
|
||||
"""Product description, 1-255 characters"""
|
||||
payload: str
|
||||
"""Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for
|
||||
your internal processes."""
|
||||
provider_token: str
|
||||
"""Payments provider token, obtained via Botfather"""
|
||||
start_parameter: str
|
||||
"""Unique deep-linking parameter that can be used to generate this invoice when used as a
|
||||
start parameter"""
|
||||
currency: str
|
||||
"""Three-letter ISO 4217 currency code, see more on currencies"""
|
||||
prices: List[LabeledPrice]
|
||||
"""Price breakdown, a JSON-serialized list of components (e.g. product price, tax, discount,
|
||||
delivery cost, delivery tax, bonus, etc.)"""
|
||||
provider_data: Optional[str] = None
|
||||
"""JSON-encoded data about the invoice, which will be shared with the payment provider. A
|
||||
detailed description of required fields should be provided by the payment provider."""
|
||||
photo_url: Optional[str] = None
|
||||
"""URL of the product photo for the invoice. Can be a photo of the goods or a marketing image
|
||||
for a service. People like it better when they see what they are paying for."""
|
||||
photo_size: Optional[int] = None
|
||||
"""Photo size"""
|
||||
photo_width: Optional[int] = None
|
||||
"""Photo width"""
|
||||
photo_height: Optional[int] = None
|
||||
"""Photo height"""
|
||||
need_name: Optional[bool] = None
|
||||
"""Pass True, if you require the user's full name to complete the order"""
|
||||
need_phone_number: Optional[bool] = None
|
||||
"""Pass True, if you require the user's phone number to complete the order"""
|
||||
need_email: Optional[bool] = None
|
||||
"""Pass True, if you require the user's email address to complete the order"""
|
||||
need_shipping_address: Optional[bool] = None
|
||||
"""Pass True, if you require the user's shipping address to complete the order"""
|
||||
send_phone_number_to_provider: Optional[bool] = None
|
||||
"""Pass True, if user's phone number should be sent to provider"""
|
||||
send_email_to_provider: Optional[bool] = None
|
||||
"""Pass True, if user's email address should be sent to provider"""
|
||||
is_flexible: Optional[bool] = None
|
||||
"""Pass True, if the final price depends on the shipping method"""
|
||||
disable_notification: Optional[bool] = None
|
||||
"""Sends the message silently. Users will receive a notification with no sound."""
|
||||
reply_to_message_id: Optional[int] = None
|
||||
"""If the message is a reply, ID of the original message"""
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None
|
||||
"""A JSON-serialized object for an inline keyboard. If empty, one 'Pay total price' button
|
||||
will be shown. If not empty, the first button must be a Pay button."""
|
||||
|
||||
def build_request(self, bot: Bot) -> Request:
|
||||
data: Dict[str, Any] = self.dict()
|
||||
|
||||
return Request(method="sendInvoice", data=data)
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
|
||||
|
||||
from ..types import InputFile, InputMediaPhoto, InputMediaVideo, Message
|
||||
from .base import Request, TelegramMethod, prepare_input_media, prepare_parse_mode
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ..client.bot import Bot
|
||||
|
||||
|
||||
class SendMediaGroup(TelegramMethod[List[Message]]):
|
||||
"""
|
||||
Use this method to send a group of photos or videos as an album. On success, an array of the
|
||||
sent Messages is returned.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#sendmediagroup
|
||||
"""
|
||||
|
||||
__returning__ = List[Message]
|
||||
|
||||
chat_id: Union[int, str]
|
||||
"""Unique identifier for the target chat or username of the target channel (in the format
|
||||
@channelusername)"""
|
||||
media: List[Union[InputMediaPhoto, InputMediaVideo]]
|
||||
"""A JSON-serialized array describing photos and videos to be sent, must include 2-10 items"""
|
||||
disable_notification: Optional[bool] = None
|
||||
"""Sends the messages silently. Users will receive a notification with no sound."""
|
||||
reply_to_message_id: Optional[int] = None
|
||||
"""If the messages are a reply, ID of the original message"""
|
||||
|
||||
def build_request(self, bot: Bot) -> Request:
|
||||
data: Dict[str, Any] = self.dict()
|
||||
prepare_parse_mode(bot, data["media"])
|
||||
|
||||
files: Dict[str, InputFile] = {}
|
||||
prepare_input_media(data, files)
|
||||
|
||||
return Request(method="sendMediaGroup", data=data, files=files)
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, Optional, Union
|
||||
|
||||
from ..types import (
|
||||
UNSET,
|
||||
ForceReply,
|
||||
InlineKeyboardMarkup,
|
||||
InputFile,
|
||||
Message,
|
||||
ReplyKeyboardMarkup,
|
||||
ReplyKeyboardRemove,
|
||||
)
|
||||
from .base import Request, TelegramMethod, prepare_file
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ..client.bot import Bot
|
||||
|
||||
|
||||
class SendPhoto(TelegramMethod[Message]):
|
||||
"""
|
||||
Use this method to send photos. On success, the sent Message is returned.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#sendphoto
|
||||
"""
|
||||
|
||||
__returning__ = Message
|
||||
|
||||
chat_id: Union[int, str]
|
||||
"""Unique identifier for the target chat or username of the target channel (in the format
|
||||
@channelusername)"""
|
||||
photo: Union[InputFile, str]
|
||||
"""Photo to send. Pass a file_id as String to send a photo that exists on the Telegram servers
|
||||
(recommended), pass an HTTP URL as a String for Telegram to get a photo from the Internet,
|
||||
or upload a new photo using multipart/form-data."""
|
||||
caption: Optional[str] = None
|
||||
"""Photo caption (may also be used when resending photos by file_id), 0-1024 characters after
|
||||
entities parsing"""
|
||||
parse_mode: Optional[str] = UNSET
|
||||
"""Mode for parsing entities in the photo caption. See formatting options for more details."""
|
||||
disable_notification: Optional[bool] = None
|
||||
"""Sends the message silently. Users will receive a notification with no sound."""
|
||||
reply_to_message_id: Optional[int] = None
|
||||
"""If the message is a reply, ID of the original message"""
|
||||
reply_markup: Optional[
|
||||
Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply]
|
||||
] = None
|
||||
"""Additional interface options. A JSON-serialized object for an inline keyboard, custom reply
|
||||
keyboard, instructions to remove reply keyboard or to force a reply from the user."""
|
||||
|
||||
def build_request(self, bot: Bot) -> Request:
|
||||
data: Dict[str, Any] = self.dict(exclude={"photo"})
|
||||
|
||||
files: Dict[str, InputFile] = {}
|
||||
prepare_file(data=data, files=files, name="photo", value=self.photo)
|
||||
|
||||
return Request(method="sendPhoto", data=data, files=files)
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, Optional, Union
|
||||
|
||||
from ..types import (
|
||||
UNSET,
|
||||
ForceReply,
|
||||
InlineKeyboardMarkup,
|
||||
InputFile,
|
||||
Message,
|
||||
ReplyKeyboardMarkup,
|
||||
ReplyKeyboardRemove,
|
||||
)
|
||||
from .base import Request, TelegramMethod, prepare_file
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ..client.bot import Bot
|
||||
|
||||
|
||||
class SendVideo(TelegramMethod[Message]):
|
||||
"""
|
||||
Use this method to send video files, Telegram clients support mp4 videos (other formats may be
|
||||
sent as Document). On success, the sent Message is returned. Bots can currently send video
|
||||
files of up to 50 MB in size, this limit may be changed in the future.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#sendvideo
|
||||
"""
|
||||
|
||||
__returning__ = Message
|
||||
|
||||
chat_id: Union[int, str]
|
||||
"""Unique identifier for the target chat or username of the target channel (in the format
|
||||
@channelusername)"""
|
||||
video: Union[InputFile, str]
|
||||
"""Video to send. Pass a file_id as String to send a video that exists on the Telegram servers
|
||||
(recommended), pass an HTTP URL as a String for Telegram to get a video from the Internet,
|
||||
or upload a new video using multipart/form-data."""
|
||||
duration: Optional[int] = None
|
||||
"""Duration of sent video in seconds"""
|
||||
width: Optional[int] = None
|
||||
"""Video width"""
|
||||
height: Optional[int] = None
|
||||
"""Video height"""
|
||||
thumb: Optional[Union[InputFile, str]] = None
|
||||
"""Thumbnail of the file sent; can be ignored if thumbnail generation for the file is
|
||||
supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size.
|
||||
A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded
|
||||
using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new
|
||||
file, so you can pass 'attach://<file_attach_name>' if the thumbnail was uploaded using
|
||||
multipart/form-data under <file_attach_name>."""
|
||||
caption: Optional[str] = None
|
||||
"""Video caption (may also be used when resending videos by file_id), 0-1024 characters after
|
||||
entities parsing"""
|
||||
parse_mode: Optional[str] = UNSET
|
||||
"""Mode for parsing entities in the video caption. See formatting options for more details."""
|
||||
supports_streaming: Optional[bool] = None
|
||||
"""Pass True, if the uploaded video is suitable for streaming"""
|
||||
disable_notification: Optional[bool] = None
|
||||
"""Sends the message silently. Users will receive a notification with no sound."""
|
||||
reply_to_message_id: Optional[int] = None
|
||||
"""If the message is a reply, ID of the original message"""
|
||||
reply_markup: Optional[
|
||||
Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply]
|
||||
] = None
|
||||
"""Additional interface options. A JSON-serialized object for an inline keyboard, custom reply
|
||||
keyboard, instructions to remove reply keyboard or to force a reply from the user."""
|
||||
|
||||
def build_request(self, bot: Bot) -> Request:
|
||||
data: Dict[str, Any] = self.dict(exclude={"video", "thumb"})
|
||||
|
||||
files: Dict[str, InputFile] = {}
|
||||
prepare_file(data=data, files=files, name="video", value=self.video)
|
||||
prepare_file(data=data, files=files, name="thumb", value=self.thumb)
|
||||
|
||||
return Request(method="sendVideo", data=data, files=files)
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, Optional, Union
|
||||
|
||||
from ..types import (
|
||||
UNSET,
|
||||
ForceReply,
|
||||
InlineKeyboardMarkup,
|
||||
InputFile,
|
||||
Message,
|
||||
ReplyKeyboardMarkup,
|
||||
ReplyKeyboardRemove,
|
||||
)
|
||||
from .base import Request, TelegramMethod, prepare_file
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ..client.bot import Bot
|
||||
|
||||
|
||||
class SendVoice(TelegramMethod[Message]):
|
||||
"""
|
||||
Use this method to send audio files, if you want Telegram clients to display the file as a
|
||||
playable voice message. For this to work, your audio must be in an .OGG file encoded with OPUS
|
||||
(other formats may be sent as Audio or Document). On success, the sent Message is returned.
|
||||
Bots can currently send voice messages of up to 50 MB in size, this limit may be changed in
|
||||
the future.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#sendvoice
|
||||
"""
|
||||
|
||||
__returning__ = Message
|
||||
|
||||
chat_id: Union[int, str]
|
||||
"""Unique identifier for the target chat or username of the target channel (in the format
|
||||
@channelusername)"""
|
||||
voice: Union[InputFile, str]
|
||||
"""Audio file to send. Pass a file_id as String to send a file that exists on the Telegram
|
||||
servers (recommended), pass an HTTP URL as a String for Telegram to get a file from the
|
||||
Internet, or upload a new one using multipart/form-data."""
|
||||
caption: Optional[str] = None
|
||||
"""Voice message caption, 0-1024 characters after entities parsing"""
|
||||
parse_mode: Optional[str] = UNSET
|
||||
"""Mode for parsing entities in the voice message caption. See formatting options for more
|
||||
details."""
|
||||
duration: Optional[int] = None
|
||||
"""Duration of the voice message in seconds"""
|
||||
disable_notification: Optional[bool] = None
|
||||
"""Sends the message silently. Users will receive a notification with no sound."""
|
||||
reply_to_message_id: Optional[int] = None
|
||||
"""If the message is a reply, ID of the original message"""
|
||||
reply_markup: Optional[
|
||||
Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply]
|
||||
] = None
|
||||
"""Additional interface options. A JSON-serialized object for an inline keyboard, custom reply
|
||||
keyboard, instructions to remove reply keyboard or to force a reply from the user."""
|
||||
|
||||
def build_request(self, bot: Bot) -> Request:
|
||||
data: Dict[str, Any] = self.dict(exclude={"voice"})
|
||||
|
||||
files: Dict[str, InputFile] = {}
|
||||
prepare_file(data=data, files=files, name="voice", value=self.voice)
|
||||
|
||||
return Request(method="sendVoice", data=data, files=files)
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
||||
|
||||
from ..types import InputFile
|
||||
from .base import Request, TelegramMethod, prepare_file
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ..client.bot import Bot
|
||||
|
||||
|
||||
class SetWebhook(TelegramMethod[bool]):
|
||||
"""
|
||||
Use this method to specify a url and receive incoming updates via an outgoing webhook.
|
||||
Whenever there is an update for the bot, we will send an HTTPS POST request to the specified
|
||||
url, containing a JSON-serialized Update. In case of an unsuccessful request, we will give up
|
||||
after a reasonable amount of attempts. Returns True on success.
|
||||
If you'd like to make sure that the Webhook request comes from Telegram, we recommend using a
|
||||
secret path in the URL, e.g. https://www.example.com/<token>. Since nobody else knows your
|
||||
bot's token, you can be pretty sure it's us.
|
||||
Notes
|
||||
1. You will not be able to receive updates using getUpdates for as long as an outgoing webhook
|
||||
is set up.
|
||||
2. To use a self-signed certificate, you need to upload your public key certificate using
|
||||
certificate parameter. Please upload as InputFile, sending a String will not work.
|
||||
3. Ports currently supported for Webhooks: 443, 80, 88, 8443.
|
||||
NEW! If you're having any trouble setting up webhooks, please check out this amazing guide to
|
||||
Webhooks.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#setwebhook
|
||||
"""
|
||||
|
||||
__returning__ = bool
|
||||
|
||||
url: str
|
||||
"""HTTPS url to send updates to. Use an empty string to remove webhook integration"""
|
||||
certificate: Optional[InputFile] = None
|
||||
"""Upload your public key certificate so that the root certificate in use can be checked. See
|
||||
our self-signed guide for details."""
|
||||
max_connections: Optional[int] = None
|
||||
"""Maximum allowed number of simultaneous HTTPS connections to the webhook for update
|
||||
delivery, 1-100. Defaults to 40. Use lower values to limit the load on your bot's server,
|
||||
and higher values to increase your bot's throughput."""
|
||||
allowed_updates: Optional[List[str]] = None
|
||||
"""A JSON-serialized list of the update types you want your bot to receive. For example,
|
||||
specify ['message', 'edited_channel_post', 'callback_query'] to only receive updates of
|
||||
these types. See Update for a complete list of available update types. Specify an empty
|
||||
list to receive all updates regardless of type (default). If not specified, the previous
|
||||
setting will be used."""
|
||||
|
||||
def build_request(self, bot: Bot) -> Request:
|
||||
data: Dict[str, Any] = self.dict(exclude={"certificate"})
|
||||
|
||||
files: Dict[str, InputFile] = {}
|
||||
prepare_file(data=data, files=files, name="certificate", value=self.certificate)
|
||||
|
||||
return Request(method="setWebhook", data=data, files=files)
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, Union
|
||||
|
||||
from .base import Request, TelegramMethod
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ..client.bot import Bot
|
||||
|
||||
|
||||
class UnbanChatMember(TelegramMethod[bool]):
|
||||
"""
|
||||
Use this method to unban a previously kicked user in a supergroup or channel. The user will
|
||||
not return to the group or channel automatically, but will be able to join via link, etc. The
|
||||
bot must be an administrator for this to work. Returns True on success.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#unbanchatmember
|
||||
"""
|
||||
|
||||
__returning__ = bool
|
||||
|
||||
chat_id: Union[int, str]
|
||||
"""Unique identifier for the target group or username of the target supergroup or channel (in
|
||||
the format @username)"""
|
||||
user_id: int
|
||||
"""Unique identifier of the target user"""
|
||||
|
||||
def build_request(self, bot: Bot) -> Request:
|
||||
data: Dict[str, Any] = self.dict()
|
||||
|
||||
return Request(method="unbanChatMember", data=data)
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, Union
|
||||
|
||||
from .base import Request, TelegramMethod
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ..client.bot import Bot
|
||||
|
||||
|
||||
class UnpinChatMessage(TelegramMethod[bool]):
|
||||
"""
|
||||
Use this method to unpin a message in a group, a supergroup, or a channel. The bot must be an
|
||||
administrator in the chat for this to work and must have the 'can_pin_messages' admin right in
|
||||
the supergroup or 'can_edit_messages' admin right in the channel. Returns True on success.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#unpinchatmessage
|
||||
"""
|
||||
|
||||
__returning__ = bool
|
||||
|
||||
chat_id: Union[int, str]
|
||||
"""Unique identifier for the target chat or username of the target channel (in the format
|
||||
@channelusername)"""
|
||||
|
||||
def build_request(self, bot: Bot) -> Request:
|
||||
data: Dict[str, Any] = self.dict()
|
||||
|
||||
return Request(method="unpinChatMessage", data=data)
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .base import TelegramObject
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .message import Message
|
||||
from .user import User
|
||||
from ..methods import AnswerCallbackQuery
|
||||
|
||||
|
||||
class CallbackQuery(TelegramObject):
|
||||
"""
|
||||
This object represents an incoming callback query from a callback button in an inline
|
||||
keyboard. If the button that originated the query was attached to a message sent by the bot,
|
||||
the field message will be present. If the button was attached to a message sent via the bot
|
||||
(in inline mode), the field inline_message_id will be present. Exactly one of the fields data
|
||||
or game_short_name will be present.
|
||||
NOTE: After the user presses a callback button, Telegram clients will display a progress bar
|
||||
until you call answerCallbackQuery. It is, therefore, necessary to react by calling
|
||||
answerCallbackQuery even if no notification to the user is needed (e.g., without specifying
|
||||
any of the optional parameters).
|
||||
|
||||
Source: https://core.telegram.org/bots/api#callbackquery
|
||||
"""
|
||||
|
||||
id: str
|
||||
"""Unique identifier for this query"""
|
||||
from_user: User = Field(..., alias="from")
|
||||
"""Sender"""
|
||||
chat_instance: str
|
||||
"""Global identifier, uniquely corresponding to the chat to which the message with the
|
||||
callback button was sent. Useful for high scores in games."""
|
||||
message: Optional[Message] = None
|
||||
"""Message with the callback button that originated the query. Note that message content and
|
||||
message date will not be available if the message is too old"""
|
||||
inline_message_id: Optional[str] = None
|
||||
"""Identifier of the message sent via the bot in inline mode, that originated the query."""
|
||||
data: Optional[str] = None
|
||||
"""Data associated with the callback button. Be aware that a bad client can send arbitrary
|
||||
data in this field."""
|
||||
game_short_name: Optional[str] = None
|
||||
"""Short name of a Game to be returned, serves as the unique identifier for the game"""
|
||||
|
||||
def answer(
|
||||
self,
|
||||
text: Optional[str] = None,
|
||||
show_alert: Optional[bool] = None,
|
||||
url: Optional[str] = None,
|
||||
cache_time: Optional[int] = None,
|
||||
) -> AnswerCallbackQuery:
|
||||
"""
|
||||
Answer to callback query
|
||||
|
||||
:param text:
|
||||
:param show_alert:
|
||||
:param url:
|
||||
:param cache_time:
|
||||
:return:
|
||||
"""
|
||||
from ..methods import AnswerCallbackQuery
|
||||
|
||||
return AnswerCallbackQuery(
|
||||
callback_query_id=self.id,
|
||||
text=text,
|
||||
show_alert=show_alert,
|
||||
url=url,
|
||||
cache_time=cache_time,
|
||||
)
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from .base import TelegramObject
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .chat_permissions import ChatPermissions
|
||||
from .chat_photo import ChatPhoto
|
||||
from .message import Message
|
||||
|
||||
|
||||
class Chat(TelegramObject):
|
||||
"""
|
||||
This object represents a chat.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#chat
|
||||
"""
|
||||
|
||||
id: int
|
||||
"""Unique identifier for this chat. This number may be greater than 32 bits and some
|
||||
programming languages may have difficulty/silent defects in interpreting it. But it is
|
||||
smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe
|
||||
for storing this identifier."""
|
||||
type: str
|
||||
"""Type of chat, can be either 'private', 'group', 'supergroup' or 'channel'"""
|
||||
title: Optional[str] = None
|
||||
"""Title, for supergroups, channels and group chats"""
|
||||
username: Optional[str] = None
|
||||
"""Username, for private chats, supergroups and channels if available"""
|
||||
first_name: Optional[str] = None
|
||||
"""First name of the other party in a private chat"""
|
||||
last_name: Optional[str] = None
|
||||
"""Last name of the other party in a private chat"""
|
||||
photo: Optional[ChatPhoto] = None
|
||||
"""Chat photo. Returned only in getChat."""
|
||||
description: Optional[str] = None
|
||||
"""Description, for groups, supergroups and channel chats. Returned only in getChat."""
|
||||
invite_link: Optional[str] = None
|
||||
"""Chat invite link, for groups, supergroups and channel chats. Each administrator in a chat
|
||||
generates their own invite links, so the bot must first generate the link using
|
||||
exportChatInviteLink. Returned only in getChat."""
|
||||
pinned_message: Optional[Message] = None
|
||||
"""Pinned message, for groups, supergroups and channels. Returned only in getChat."""
|
||||
permissions: Optional[ChatPermissions] = None
|
||||
"""Default chat member permissions, for groups and supergroups. Returned only in getChat."""
|
||||
slow_mode_delay: Optional[int] = None
|
||||
"""For supergroups, the minimum allowed delay between consecutive messages sent by each
|
||||
unpriviledged user. Returned only in getChat."""
|
||||
sticker_set_name: Optional[str] = None
|
||||
"""For supergroups, name of group sticker set. Returned only in getChat."""
|
||||
can_set_sticker_set: Optional[bool] = None
|
||||
"""True, if the bot can change the group sticker set. Returned only in getChat."""
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
from typing import TYPE_CHECKING, Optional, Union
|
||||
|
||||
from ...utils import helper
|
||||
from .base import TelegramObject
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .user import User
|
||||
|
||||
|
||||
class ChatMember(TelegramObject):
|
||||
"""
|
||||
This object contains information about one member of a chat.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#chatmember
|
||||
"""
|
||||
|
||||
user: User
|
||||
"""Information about the user"""
|
||||
status: str
|
||||
"""The member's status in the chat. Can be 'creator', 'administrator', 'member', 'restricted',
|
||||
'left' or 'kicked'"""
|
||||
custom_title: Optional[str] = None
|
||||
"""Owner and administrators only. Custom title for this user"""
|
||||
until_date: Optional[Union[datetime.datetime, datetime.timedelta, int]] = None
|
||||
"""Restricted and kicked only. Date when restrictions will be lifted for this user; unix time"""
|
||||
can_be_edited: Optional[bool] = None
|
||||
"""Administrators only. True, if the bot is allowed to edit administrator privileges of that
|
||||
user"""
|
||||
can_post_messages: Optional[bool] = None
|
||||
"""Administrators only. True, if the administrator can post in the channel; channels only"""
|
||||
can_edit_messages: Optional[bool] = None
|
||||
"""Administrators only. True, if the administrator can edit messages of other users and can
|
||||
pin messages; channels only"""
|
||||
can_delete_messages: Optional[bool] = None
|
||||
"""Administrators only. True, if the administrator can delete messages of other users"""
|
||||
can_restrict_members: Optional[bool] = None
|
||||
"""Administrators only. True, if the administrator can restrict, ban or unban chat members"""
|
||||
can_promote_members: Optional[bool] = None
|
||||
"""Administrators only. True, if the administrator can add new administrators with a subset of
|
||||
their own privileges or demote administrators that he has promoted, directly or indirectly
|
||||
(promoted by administrators that were appointed by the user)"""
|
||||
can_change_info: Optional[bool] = None
|
||||
"""Administrators and restricted only. True, if the user is allowed to change the chat title,
|
||||
photo and other settings"""
|
||||
can_invite_users: Optional[bool] = None
|
||||
"""Administrators and restricted only. True, if the user is allowed to invite new users to the
|
||||
chat"""
|
||||
can_pin_messages: Optional[bool] = None
|
||||
"""Administrators and restricted only. True, if the user is allowed to pin messages; groups
|
||||
and supergroups only"""
|
||||
is_member: Optional[bool] = None
|
||||
"""Restricted only. True, if the user is a member of the chat at the moment of the request"""
|
||||
can_send_messages: Optional[bool] = None
|
||||
"""Restricted only. True, if the user is allowed to send text messages, contacts, locations
|
||||
and venues"""
|
||||
can_send_media_messages: Optional[bool] = None
|
||||
"""Restricted only. True, if the user is allowed to send audios, documents, photos, videos,
|
||||
video notes and voice notes"""
|
||||
can_send_polls: Optional[bool] = None
|
||||
"""Restricted only. True, if the user is allowed to send polls"""
|
||||
can_send_other_messages: Optional[bool] = None
|
||||
"""Restricted only. True, if the user is allowed to send animations, games, stickers and use
|
||||
inline bots"""
|
||||
can_add_web_page_previews: Optional[bool] = None
|
||||
"""Restricted only. True, if the user is allowed to add web page previews to their messages"""
|
||||
|
||||
@property
|
||||
def is_chat_admin(self) -> bool:
|
||||
return self.status in {ChatMemberStatus.CREATOR, ChatMemberStatus.ADMINISTRATOR}
|
||||
|
||||
@property
|
||||
def is_chat_member(self) -> bool:
|
||||
return self.status not in {ChatMemberStatus.LEFT, ChatMemberStatus.KICKED}
|
||||
|
||||
|
||||
class ChatMemberStatus(helper.Helper):
|
||||
"""
|
||||
Chat member status
|
||||
"""
|
||||
|
||||
mode = helper.HelperMode.lowercase
|
||||
|
||||
CREATOR = helper.Item() # creator
|
||||
ADMINISTRATOR = helper.Item() # administrator
|
||||
MEMBER = helper.Item() # member
|
||||
RESTRICTED = helper.Item() # restricted
|
||||
LEFT = helper.Item() # left
|
||||
KICKED = helper.Item() # kicked
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from .base import MutableTelegramObject
|
||||
|
||||
|
||||
class ChatPermissions(MutableTelegramObject):
|
||||
"""
|
||||
Describes actions that a non-administrator user is allowed to take in a chat.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#chatpermissions
|
||||
"""
|
||||
|
||||
can_send_messages: Optional[bool] = None
|
||||
"""True, if the user is allowed to send text messages, contacts, locations and venues"""
|
||||
can_send_media_messages: Optional[bool] = None
|
||||
"""True, if the user is allowed to send audios, documents, photos, videos, video notes and
|
||||
voice notes, implies can_send_messages"""
|
||||
can_send_polls: Optional[bool] = None
|
||||
"""True, if the user is allowed to send polls, implies can_send_messages"""
|
||||
can_send_other_messages: Optional[bool] = None
|
||||
"""True, if the user is allowed to send animations, games, stickers and use inline bots,
|
||||
implies can_send_media_messages"""
|
||||
can_add_web_page_previews: Optional[bool] = None
|
||||
"""True, if the user is allowed to add web page previews to their messages, implies
|
||||
can_send_media_messages"""
|
||||
can_change_info: Optional[bool] = None
|
||||
"""True, if the user is allowed to change the chat title, photo and other settings. Ignored in
|
||||
public supergroups"""
|
||||
can_invite_users: Optional[bool] = None
|
||||
"""True, if the user is allowed to invite new users to the chat"""
|
||||
can_pin_messages: Optional[bool] = None
|
||||
"""True, if the user is allowed to pin messages. Ignored in public supergroups"""
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .base import TelegramObject
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .location import Location
|
||||
from .user import User
|
||||
|
||||
|
||||
class ChosenInlineResult(TelegramObject):
|
||||
"""
|
||||
Represents a result of an inline query that was chosen by the user and sent to their chat
|
||||
partner.
|
||||
Note: It is necessary to enable inline feedback via @Botfather in order to receive these
|
||||
objects in updates.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#choseninlineresult
|
||||
"""
|
||||
|
||||
result_id: str
|
||||
"""The unique identifier for the result that was chosen"""
|
||||
from_user: User = Field(..., alias="from")
|
||||
"""The user that chose the result"""
|
||||
query: str
|
||||
"""The query that was used to obtain the result"""
|
||||
location: Optional[Location] = None
|
||||
"""Sender location, only for bots that require user location"""
|
||||
inline_message_id: Optional[str] = None
|
||||
"""Identifier of the sent inline message. Available only if there is an inline keyboard
|
||||
attached to the message. Will be also received in callback queries and can be used to edit
|
||||
the message."""
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
from typing_extensions import Protocol
|
||||
|
||||
|
||||
class Downloadable(Protocol):
|
||||
file_id: str
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from .base import TelegramObject
|
||||
|
||||
|
||||
class EncryptedCredentials(TelegramObject):
|
||||
"""
|
||||
Contains data required for decrypting and authenticating EncryptedPassportElement. See the
|
||||
Telegram Passport Documentation for a complete description of the data decryption and
|
||||
authentication processes.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#encryptedcredentials
|
||||
"""
|
||||
|
||||
data: str
|
||||
"""Base64-encoded encrypted JSON-serialized data with unique user's payload, data hashes and
|
||||
secrets required for EncryptedPassportElement decryption and authentication"""
|
||||
hash: str
|
||||
"""Base64-encoded data hash for data authentication"""
|
||||
secret: str
|
||||
"""Base64-encoded secret, encrypted with the bot's public RSA key, required for data
|
||||
decryption"""
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, List, Optional
|
||||
|
||||
from .base import TelegramObject
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .passport_file import PassportFile
|
||||
|
||||
|
||||
class EncryptedPassportElement(TelegramObject):
|
||||
"""
|
||||
Contains information about documents or other Telegram Passport elements shared with the bot
|
||||
by the user.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#encryptedpassportelement
|
||||
"""
|
||||
|
||||
type: str
|
||||
"""Element type. One of 'personal_details', 'passport', 'driver_license', 'identity_card',
|
||||
'internal_passport', 'address', 'utility_bill', 'bank_statement', 'rental_agreement',
|
||||
'passport_registration', 'temporary_registration', 'phone_number', 'email'."""
|
||||
hash: str
|
||||
"""Base64-encoded element hash for using in PassportElementErrorUnspecified"""
|
||||
data: Optional[str] = None
|
||||
"""Base64-encoded encrypted Telegram Passport element data provided by the user, available for
|
||||
'personal_details', 'passport', 'driver_license', 'identity_card', 'internal_passport' and
|
||||
'address' types. Can be decrypted and verified using the accompanying EncryptedCredentials."""
|
||||
phone_number: Optional[str] = None
|
||||
"""User's verified phone number, available only for 'phone_number' type"""
|
||||
email: Optional[str] = None
|
||||
"""User's verified email address, available only for 'email' type"""
|
||||
files: Optional[List[PassportFile]] = None
|
||||
"""Array of encrypted files with documents provided by the user, available for 'utility_bill',
|
||||
'bank_statement', 'rental_agreement', 'passport_registration' and 'temporary_registration'
|
||||
types. Files can be decrypted and verified using the accompanying EncryptedCredentials."""
|
||||
front_side: Optional[PassportFile] = None
|
||||
"""Encrypted file with the front side of the document, provided by the user. Available for
|
||||
'passport', 'driver_license', 'identity_card' and 'internal_passport'. The file can be
|
||||
decrypted and verified using the accompanying EncryptedCredentials."""
|
||||
reverse_side: Optional[PassportFile] = None
|
||||
"""Encrypted file with the reverse side of the document, provided by the user. Available for
|
||||
'driver_license' and 'identity_card'. The file can be decrypted and verified using the
|
||||
accompanying EncryptedCredentials."""
|
||||
selfie: Optional[PassportFile] = None
|
||||
"""Encrypted file with the selfie of the user holding a document, provided by the user;
|
||||
available for 'passport', 'driver_license', 'identity_card' and 'internal_passport'. The
|
||||
file can be decrypted and verified using the accompanying EncryptedCredentials."""
|
||||
translation: Optional[List[PassportFile]] = None
|
||||
"""Array of encrypted files with translated versions of documents provided by the user.
|
||||
Available if requested for 'passport', 'driver_license', 'identity_card',
|
||||
'internal_passport', 'utility_bill', 'bank_statement', 'rental_agreement',
|
||||
'passport_registration' and 'temporary_registration' types. Files can be decrypted and
|
||||
verified using the accompanying EncryptedCredentials."""
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from .base import TelegramObject
|
||||
|
||||
|
||||
class File(TelegramObject):
|
||||
"""
|
||||
This object represents a file ready to be downloaded. The file can be downloaded via the link
|
||||
https://api.telegram.org/file/bot<token>/<file_path>. It is guaranteed that the link will be
|
||||
valid for at least 1 hour. When the link expires, a new one can be requested by calling
|
||||
getFile.
|
||||
Maximum file size to download is 20 MB
|
||||
|
||||
Source: https://core.telegram.org/bots/api#file
|
||||
"""
|
||||
|
||||
file_id: str
|
||||
"""Identifier for this file, which can be used to download or reuse the file"""
|
||||
file_unique_id: str
|
||||
"""Unique identifier for this file, which is supposed to be the same over time and for
|
||||
different bots. Can't be used to download or reuse the file."""
|
||||
file_size: Optional[int] = None
|
||||
"""File size, if known"""
|
||||
file_path: Optional[str] = None
|
||||
"""File path. Use https://api.telegram.org/file/bot<token>/<file_path> to get the file."""
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from .base import MutableTelegramObject
|
||||
|
||||
|
||||
class ForceReply(MutableTelegramObject):
|
||||
"""
|
||||
Upon receiving a message with this object, Telegram clients will display a reply interface to
|
||||
the user (act as if the user has selected the bot's message and tapped 'Reply'). This can be
|
||||
extremely useful if you want to create user-friendly step-by-step interfaces without having to
|
||||
sacrifice privacy mode.
|
||||
Example: A poll bot for groups runs in privacy mode (only receives commands, replies to its
|
||||
messages and mentions). There could be two ways to create a new poll:
|
||||
|
||||
Explain the user how to send a command with parameters (e.g. /newpoll question answer1
|
||||
answer2). May be appealing for hardcore users but lacks modern day polish.
|
||||
Guide the user through a step-by-step process. 'Please send me your question', 'Cool, now
|
||||
let's add the first answer option', 'Great. Keep adding answer options, then send /done when
|
||||
you're ready'.
|
||||
The last option is definitely more attractive. And if you use ForceReply in your bot's
|
||||
questions, it will receive the user's answers even if it only receives replies, commands and
|
||||
mentions — without any extra work for the user.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#forcereply
|
||||
"""
|
||||
|
||||
force_reply: bool
|
||||
"""Shows reply interface to the user, as if they manually selected the bot's message and
|
||||
tapped 'Reply'"""
|
||||
selective: Optional[bool] = None
|
||||
"""Use this parameter if you want to force reply from specific users only. Targets: 1) users
|
||||
that are @mentioned in the text of the Message object; 2) if the bot's message is a reply
|
||||
(has reply_to_message_id), sender of the original message."""
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from .base import MutableTelegramObject
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .callback_game import CallbackGame
|
||||
from .login_url import LoginUrl
|
||||
|
||||
|
||||
class InlineKeyboardButton(MutableTelegramObject):
|
||||
"""
|
||||
This object represents one button of an inline keyboard. You must use exactly one of the
|
||||
optional fields.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inlinekeyboardbutton
|
||||
"""
|
||||
|
||||
text: str
|
||||
"""Label text on the button"""
|
||||
url: Optional[str] = None
|
||||
"""HTTP or tg:// url to be opened when button is pressed"""
|
||||
login_url: Optional[LoginUrl] = None
|
||||
"""An HTTP URL used to automatically authorize the user. Can be used as a replacement for the
|
||||
Telegram Login Widget."""
|
||||
callback_data: Optional[str] = None
|
||||
"""Data to be sent in a callback query to the bot when button is pressed, 1-64 bytes"""
|
||||
switch_inline_query: Optional[str] = None
|
||||
"""If set, pressing the button will prompt the user to select one of their chats, open that
|
||||
chat and insert the bot's username and the specified inline query in the input field. Can
|
||||
be empty, in which case just the bot's username will be inserted."""
|
||||
switch_inline_query_current_chat: Optional[str] = None
|
||||
"""If set, pressing the button will insert the bot's username and the specified inline query
|
||||
in the current chat's input field. Can be empty, in which case only the bot's username will
|
||||
be inserted."""
|
||||
callback_game: Optional[CallbackGame] = None
|
||||
"""Description of the game that will be launched when the user presses the button."""
|
||||
pay: Optional[bool] = None
|
||||
"""Specify True, to send a Pay button."""
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, List
|
||||
|
||||
from .base import MutableTelegramObject
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .inline_keyboard_button import InlineKeyboardButton
|
||||
|
||||
|
||||
class InlineKeyboardMarkup(MutableTelegramObject):
|
||||
"""
|
||||
This object represents an inline keyboard that appears right next to the message it belongs
|
||||
to.
|
||||
Note: This will only work in Telegram versions released after 9 April, 2016. Older clients
|
||||
will display unsupported message.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inlinekeyboardmarkup
|
||||
"""
|
||||
|
||||
inline_keyboard: List[List[InlineKeyboardButton]]
|
||||
"""Array of button rows, each represented by an Array of InlineKeyboardButton objects"""
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from .base import MutableTelegramObject
|
||||
|
||||
|
||||
class InlineQueryResult(MutableTelegramObject):
|
||||
"""
|
||||
This object represents one result of an inline query. Telegram clients currently support
|
||||
results of the following 20 types:
|
||||
- InlineQueryResultCachedAudio
|
||||
- InlineQueryResultCachedDocument
|
||||
- InlineQueryResultCachedGif
|
||||
- InlineQueryResultCachedMpeg4Gif
|
||||
- InlineQueryResultCachedPhoto
|
||||
- InlineQueryResultCachedSticker
|
||||
- InlineQueryResultCachedVideo
|
||||
- InlineQueryResultCachedVoice
|
||||
- InlineQueryResultArticle
|
||||
- InlineQueryResultAudio
|
||||
- InlineQueryResultContact
|
||||
- InlineQueryResultGame
|
||||
- InlineQueryResultDocument
|
||||
- InlineQueryResultGif
|
||||
- InlineQueryResultLocation
|
||||
- InlineQueryResultMpeg4Gif
|
||||
- InlineQueryResultPhoto
|
||||
- InlineQueryResultVenue
|
||||
- InlineQueryResultVideo
|
||||
- InlineQueryResultVoice
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inlinequeryresult
|
||||
"""
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .base import UNSET
|
||||
from .inline_query_result import InlineQueryResult
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .inline_keyboard_markup import InlineKeyboardMarkup
|
||||
from .input_message_content import InputMessageContent
|
||||
|
||||
|
||||
class InlineQueryResultAudio(InlineQueryResult):
|
||||
"""
|
||||
Represents a link to an MP3 audio file. By default, this audio file will be sent by the user.
|
||||
Alternatively, you can use input_message_content to send a message with the specified content
|
||||
instead of the audio.
|
||||
Note: This will only work in Telegram versions released after 9 April, 2016. Older clients
|
||||
will ignore them.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inlinequeryresultaudio
|
||||
"""
|
||||
|
||||
type: str = Field("audio", const=True)
|
||||
"""Type of the result, must be audio"""
|
||||
id: str
|
||||
"""Unique identifier for this result, 1-64 bytes"""
|
||||
audio_url: str
|
||||
"""A valid URL for the audio file"""
|
||||
title: str
|
||||
"""Title"""
|
||||
caption: Optional[str] = None
|
||||
"""Caption, 0-1024 characters after entities parsing"""
|
||||
parse_mode: Optional[str] = UNSET
|
||||
"""Mode for parsing entities in the audio caption. See formatting options for more details."""
|
||||
performer: Optional[str] = None
|
||||
"""Performer"""
|
||||
audio_duration: Optional[int] = None
|
||||
"""Audio duration in seconds"""
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None
|
||||
"""Inline keyboard attached to the message"""
|
||||
input_message_content: Optional[InputMessageContent] = None
|
||||
"""Content of the message to be sent instead of the audio"""
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .base import UNSET
|
||||
from .inline_query_result import InlineQueryResult
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .inline_keyboard_markup import InlineKeyboardMarkup
|
||||
from .input_message_content import InputMessageContent
|
||||
|
||||
|
||||
class InlineQueryResultCachedAudio(InlineQueryResult):
|
||||
"""
|
||||
Represents a link to an MP3 audio file stored on the Telegram servers. By default, this audio
|
||||
file will be sent by the user. Alternatively, you can use input_message_content to send a
|
||||
message with the specified content instead of the audio.
|
||||
Note: This will only work in Telegram versions released after 9 April, 2016. Older clients
|
||||
will ignore them.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inlinequeryresultcachedaudio
|
||||
"""
|
||||
|
||||
type: str = Field("audio", const=True)
|
||||
"""Type of the result, must be audio"""
|
||||
id: str
|
||||
"""Unique identifier for this result, 1-64 bytes"""
|
||||
audio_file_id: str
|
||||
"""A valid file identifier for the audio file"""
|
||||
caption: Optional[str] = None
|
||||
"""Caption, 0-1024 characters after entities parsing"""
|
||||
parse_mode: Optional[str] = UNSET
|
||||
"""Mode for parsing entities in the audio caption. See formatting options for more details."""
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None
|
||||
"""Inline keyboard attached to the message"""
|
||||
input_message_content: Optional[InputMessageContent] = None
|
||||
"""Content of the message to be sent instead of the audio"""
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .base import UNSET
|
||||
from .inline_query_result import InlineQueryResult
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .inline_keyboard_markup import InlineKeyboardMarkup
|
||||
from .input_message_content import InputMessageContent
|
||||
|
||||
|
||||
class InlineQueryResultCachedDocument(InlineQueryResult):
|
||||
"""
|
||||
Represents a link to a file stored on the Telegram servers. By default, this file will be sent
|
||||
by the user with an optional caption. Alternatively, you can use input_message_content to send
|
||||
a message with the specified content instead of the file.
|
||||
Note: This will only work in Telegram versions released after 9 April, 2016. Older clients
|
||||
will ignore them.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inlinequeryresultcacheddocument
|
||||
"""
|
||||
|
||||
type: str = Field("document", const=True)
|
||||
"""Type of the result, must be document"""
|
||||
id: str
|
||||
"""Unique identifier for this result, 1-64 bytes"""
|
||||
title: str
|
||||
"""Title for the result"""
|
||||
document_file_id: str
|
||||
"""A valid file identifier for the file"""
|
||||
description: Optional[str] = None
|
||||
"""Short description of the result"""
|
||||
caption: Optional[str] = None
|
||||
"""Caption of the document to be sent, 0-1024 characters after entities parsing"""
|
||||
parse_mode: Optional[str] = UNSET
|
||||
"""Mode for parsing entities in the document caption. See formatting options for more details."""
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None
|
||||
"""Inline keyboard attached to the message"""
|
||||
input_message_content: Optional[InputMessageContent] = None
|
||||
"""Content of the message to be sent instead of the file"""
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .base import UNSET
|
||||
from .inline_query_result import InlineQueryResult
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .inline_keyboard_markup import InlineKeyboardMarkup
|
||||
from .input_message_content import InputMessageContent
|
||||
|
||||
|
||||
class InlineQueryResultCachedGif(InlineQueryResult):
|
||||
"""
|
||||
Represents a link to an animated GIF file stored on the Telegram servers. By default, this
|
||||
animated GIF file will be sent by the user with an optional caption. Alternatively, you can
|
||||
use input_message_content to send a message with specified content instead of the animation.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inlinequeryresultcachedgif
|
||||
"""
|
||||
|
||||
type: str = Field("gif", const=True)
|
||||
"""Type of the result, must be gif"""
|
||||
id: str
|
||||
"""Unique identifier for this result, 1-64 bytes"""
|
||||
gif_file_id: str
|
||||
"""A valid file identifier for the GIF file"""
|
||||
title: Optional[str] = None
|
||||
"""Title for the result"""
|
||||
caption: Optional[str] = None
|
||||
"""Caption of the GIF file to be sent, 0-1024 characters after entities parsing"""
|
||||
parse_mode: Optional[str] = UNSET
|
||||
"""Mode for parsing entities in the caption. See formatting options for more details."""
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None
|
||||
"""Inline keyboard attached to the message"""
|
||||
input_message_content: Optional[InputMessageContent] = None
|
||||
"""Content of the message to be sent instead of the GIF animation"""
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .base import UNSET
|
||||
from .inline_query_result import InlineQueryResult
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .inline_keyboard_markup import InlineKeyboardMarkup
|
||||
from .input_message_content import InputMessageContent
|
||||
|
||||
|
||||
class InlineQueryResultCachedMpeg4Gif(InlineQueryResult):
|
||||
"""
|
||||
Represents a link to a video animation (H.264/MPEG-4 AVC video without sound) stored on the
|
||||
Telegram servers. By default, this animated MPEG-4 file will be sent by the user with an
|
||||
optional caption. Alternatively, you can use input_message_content to send a message with the
|
||||
specified content instead of the animation.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inlinequeryresultcachedmpeg4gif
|
||||
"""
|
||||
|
||||
type: str = Field("mpeg4_gif", const=True)
|
||||
"""Type of the result, must be mpeg4_gif"""
|
||||
id: str
|
||||
"""Unique identifier for this result, 1-64 bytes"""
|
||||
mpeg4_file_id: str
|
||||
"""A valid file identifier for the MP4 file"""
|
||||
title: Optional[str] = None
|
||||
"""Title for the result"""
|
||||
caption: Optional[str] = None
|
||||
"""Caption of the MPEG-4 file to be sent, 0-1024 characters after entities parsing"""
|
||||
parse_mode: Optional[str] = UNSET
|
||||
"""Mode for parsing entities in the caption. See formatting options for more details."""
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None
|
||||
"""Inline keyboard attached to the message"""
|
||||
input_message_content: Optional[InputMessageContent] = None
|
||||
"""Content of the message to be sent instead of the video animation"""
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .base import UNSET
|
||||
from .inline_query_result import InlineQueryResult
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .inline_keyboard_markup import InlineKeyboardMarkup
|
||||
from .input_message_content import InputMessageContent
|
||||
|
||||
|
||||
class InlineQueryResultCachedPhoto(InlineQueryResult):
|
||||
"""
|
||||
Represents a link to a photo stored on the Telegram servers. By default, this photo will be
|
||||
sent by the user with an optional caption. Alternatively, you can use input_message_content to
|
||||
send a message with the specified content instead of the photo.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inlinequeryresultcachedphoto
|
||||
"""
|
||||
|
||||
type: str = Field("photo", const=True)
|
||||
"""Type of the result, must be photo"""
|
||||
id: str
|
||||
"""Unique identifier for this result, 1-64 bytes"""
|
||||
photo_file_id: str
|
||||
"""A valid file identifier of the photo"""
|
||||
title: Optional[str] = None
|
||||
"""Title for the result"""
|
||||
description: Optional[str] = None
|
||||
"""Short description of the result"""
|
||||
caption: Optional[str] = None
|
||||
"""Caption of the photo to be sent, 0-1024 characters after entities parsing"""
|
||||
parse_mode: Optional[str] = UNSET
|
||||
"""Mode for parsing entities in the photo caption. See formatting options for more details."""
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None
|
||||
"""Inline keyboard attached to the message"""
|
||||
input_message_content: Optional[InputMessageContent] = None
|
||||
"""Content of the message to be sent instead of the photo"""
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .base import UNSET
|
||||
from .inline_query_result import InlineQueryResult
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .inline_keyboard_markup import InlineKeyboardMarkup
|
||||
from .input_message_content import InputMessageContent
|
||||
|
||||
|
||||
class InlineQueryResultCachedVideo(InlineQueryResult):
|
||||
"""
|
||||
Represents a link to a video file stored on the Telegram servers. By default, this video file
|
||||
will be sent by the user with an optional caption. Alternatively, you can use
|
||||
input_message_content to send a message with the specified content instead of the video.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inlinequeryresultcachedvideo
|
||||
"""
|
||||
|
||||
type: str = Field("video", const=True)
|
||||
"""Type of the result, must be video"""
|
||||
id: str
|
||||
"""Unique identifier for this result, 1-64 bytes"""
|
||||
video_file_id: str
|
||||
"""A valid file identifier for the video file"""
|
||||
title: str
|
||||
"""Title for the result"""
|
||||
description: Optional[str] = None
|
||||
"""Short description of the result"""
|
||||
caption: Optional[str] = None
|
||||
"""Caption of the video to be sent, 0-1024 characters after entities parsing"""
|
||||
parse_mode: Optional[str] = UNSET
|
||||
"""Mode for parsing entities in the video caption. See formatting options for more details."""
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None
|
||||
"""Inline keyboard attached to the message"""
|
||||
input_message_content: Optional[InputMessageContent] = None
|
||||
"""Content of the message to be sent instead of the video"""
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .base import UNSET
|
||||
from .inline_query_result import InlineQueryResult
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .inline_keyboard_markup import InlineKeyboardMarkup
|
||||
from .input_message_content import InputMessageContent
|
||||
|
||||
|
||||
class InlineQueryResultCachedVoice(InlineQueryResult):
|
||||
"""
|
||||
Represents a link to a voice message stored on the Telegram servers. By default, this voice
|
||||
message will be sent by the user. Alternatively, you can use input_message_content to send a
|
||||
message with the specified content instead of the voice message.
|
||||
Note: This will only work in Telegram versions released after 9 April, 2016. Older clients
|
||||
will ignore them.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inlinequeryresultcachedvoice
|
||||
"""
|
||||
|
||||
type: str = Field("voice", const=True)
|
||||
"""Type of the result, must be voice"""
|
||||
id: str
|
||||
"""Unique identifier for this result, 1-64 bytes"""
|
||||
voice_file_id: str
|
||||
"""A valid file identifier for the voice message"""
|
||||
title: str
|
||||
"""Voice message title"""
|
||||
caption: Optional[str] = None
|
||||
"""Caption, 0-1024 characters after entities parsing"""
|
||||
parse_mode: Optional[str] = UNSET
|
||||
"""Mode for parsing entities in the voice message caption. See formatting options for more
|
||||
details."""
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None
|
||||
"""Inline keyboard attached to the message"""
|
||||
input_message_content: Optional[InputMessageContent] = None
|
||||
"""Content of the message to be sent instead of the voice message"""
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .base import UNSET
|
||||
from .inline_query_result import InlineQueryResult
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .inline_keyboard_markup import InlineKeyboardMarkup
|
||||
from .input_message_content import InputMessageContent
|
||||
|
||||
|
||||
class InlineQueryResultDocument(InlineQueryResult):
|
||||
"""
|
||||
Represents a link to a file. By default, this file will be sent by the user with an optional
|
||||
caption. Alternatively, you can use input_message_content to send a message with the specified
|
||||
content instead of the file. Currently, only .PDF and .ZIP files can be sent using this
|
||||
method.
|
||||
Note: This will only work in Telegram versions released after 9 April, 2016. Older clients
|
||||
will ignore them.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inlinequeryresultdocument
|
||||
"""
|
||||
|
||||
type: str = Field("document", const=True)
|
||||
"""Type of the result, must be document"""
|
||||
id: str
|
||||
"""Unique identifier for this result, 1-64 bytes"""
|
||||
title: str
|
||||
"""Title for the result"""
|
||||
document_url: str
|
||||
"""A valid URL for the file"""
|
||||
mime_type: str
|
||||
"""Mime type of the content of the file, either 'application/pdf' or 'application/zip'"""
|
||||
caption: Optional[str] = None
|
||||
"""Caption of the document to be sent, 0-1024 characters after entities parsing"""
|
||||
parse_mode: Optional[str] = UNSET
|
||||
"""Mode for parsing entities in the document caption. See formatting options for more details."""
|
||||
description: Optional[str] = None
|
||||
"""Short description of the result"""
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None
|
||||
"""Inline keyboard attached to the message"""
|
||||
input_message_content: Optional[InputMessageContent] = None
|
||||
"""Content of the message to be sent instead of the file"""
|
||||
thumb_url: Optional[str] = None
|
||||
"""URL of the thumbnail (jpeg only) for the file"""
|
||||
thumb_width: Optional[int] = None
|
||||
"""Thumbnail width"""
|
||||
thumb_height: Optional[int] = None
|
||||
"""Thumbnail height"""
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .base import UNSET
|
||||
from .inline_query_result import InlineQueryResult
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .inline_keyboard_markup import InlineKeyboardMarkup
|
||||
from .input_message_content import InputMessageContent
|
||||
|
||||
|
||||
class InlineQueryResultGif(InlineQueryResult):
|
||||
"""
|
||||
Represents a link to an animated GIF file. By default, this animated GIF file will be sent by
|
||||
the user with optional caption. Alternatively, you can use input_message_content to send a
|
||||
message with the specified content instead of the animation.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inlinequeryresultgif
|
||||
"""
|
||||
|
||||
type: str = Field("gif", const=True)
|
||||
"""Type of the result, must be gif"""
|
||||
id: str
|
||||
"""Unique identifier for this result, 1-64 bytes"""
|
||||
gif_url: str
|
||||
"""A valid URL for the GIF file. File size must not exceed 1MB"""
|
||||
thumb_url: str
|
||||
"""URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the result"""
|
||||
gif_width: Optional[int] = None
|
||||
"""Width of the GIF"""
|
||||
gif_height: Optional[int] = None
|
||||
"""Height of the GIF"""
|
||||
gif_duration: Optional[int] = None
|
||||
"""Duration of the GIF"""
|
||||
thumb_mime_type: Optional[str] = None
|
||||
"""MIME type of the thumbnail, must be one of 'image/jpeg', 'image/gif', or 'video/mp4'.
|
||||
Defaults to 'image/jpeg'"""
|
||||
title: Optional[str] = None
|
||||
"""Title for the result"""
|
||||
caption: Optional[str] = None
|
||||
"""Caption of the GIF file to be sent, 0-1024 characters after entities parsing"""
|
||||
parse_mode: Optional[str] = UNSET
|
||||
"""Mode for parsing entities in the caption. See formatting options for more details."""
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None
|
||||
"""Inline keyboard attached to the message"""
|
||||
input_message_content: Optional[InputMessageContent] = None
|
||||
"""Content of the message to be sent instead of the GIF animation"""
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .inline_query_result import InlineQueryResult
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .inline_keyboard_markup import InlineKeyboardMarkup
|
||||
from .input_message_content import InputMessageContent
|
||||
|
||||
|
||||
class InlineQueryResultLocation(InlineQueryResult):
|
||||
"""
|
||||
Represents a location on a map. By default, the location will be sent by the user.
|
||||
Alternatively, you can use input_message_content to send a message with the specified content
|
||||
instead of the location.
|
||||
Note: This will only work in Telegram versions released after 9 April, 2016. Older clients
|
||||
will ignore them.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inlinequeryresultlocation
|
||||
"""
|
||||
|
||||
type: str = Field("location", const=True)
|
||||
"""Type of the result, must be location"""
|
||||
id: str
|
||||
"""Unique identifier for this result, 1-64 Bytes"""
|
||||
latitude: float
|
||||
"""Location latitude in degrees"""
|
||||
longitude: float
|
||||
"""Location longitude in degrees"""
|
||||
title: str
|
||||
"""Location title"""
|
||||
live_period: Optional[int] = None
|
||||
"""Period in seconds for which the location can be updated, should be between 60 and 86400."""
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None
|
||||
"""Inline keyboard attached to the message"""
|
||||
input_message_content: Optional[InputMessageContent] = None
|
||||
"""Content of the message to be sent instead of the location"""
|
||||
thumb_url: Optional[str] = None
|
||||
"""Url of the thumbnail for the result"""
|
||||
thumb_width: Optional[int] = None
|
||||
"""Thumbnail width"""
|
||||
thumb_height: Optional[int] = None
|
||||
"""Thumbnail height"""
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .base import UNSET
|
||||
from .inline_query_result import InlineQueryResult
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .inline_keyboard_markup import InlineKeyboardMarkup
|
||||
from .input_message_content import InputMessageContent
|
||||
|
||||
|
||||
class InlineQueryResultMpeg4Gif(InlineQueryResult):
|
||||
"""
|
||||
Represents a link to a video animation (H.264/MPEG-4 AVC video without sound). By default,
|
||||
this animated MPEG-4 file will be sent by the user with optional caption. Alternatively, you
|
||||
can use input_message_content to send a message with the specified content instead of the
|
||||
animation.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inlinequeryresultmpeg4gif
|
||||
"""
|
||||
|
||||
type: str = Field("mpeg4_gif", const=True)
|
||||
"""Type of the result, must be mpeg4_gif"""
|
||||
id: str
|
||||
"""Unique identifier for this result, 1-64 bytes"""
|
||||
mpeg4_url: str
|
||||
"""A valid URL for the MP4 file. File size must not exceed 1MB"""
|
||||
thumb_url: str
|
||||
"""URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the result"""
|
||||
mpeg4_width: Optional[int] = None
|
||||
"""Video width"""
|
||||
mpeg4_height: Optional[int] = None
|
||||
"""Video height"""
|
||||
mpeg4_duration: Optional[int] = None
|
||||
"""Video duration"""
|
||||
thumb_mime_type: Optional[str] = None
|
||||
"""MIME type of the thumbnail, must be one of 'image/jpeg', 'image/gif', or 'video/mp4'.
|
||||
Defaults to 'image/jpeg'"""
|
||||
title: Optional[str] = None
|
||||
"""Title for the result"""
|
||||
caption: Optional[str] = None
|
||||
"""Caption of the MPEG-4 file to be sent, 0-1024 characters after entities parsing"""
|
||||
parse_mode: Optional[str] = UNSET
|
||||
"""Mode for parsing entities in the caption. See formatting options for more details."""
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None
|
||||
"""Inline keyboard attached to the message"""
|
||||
input_message_content: Optional[InputMessageContent] = None
|
||||
"""Content of the message to be sent instead of the video animation"""
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .base import UNSET
|
||||
from .inline_query_result import InlineQueryResult
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .inline_keyboard_markup import InlineKeyboardMarkup
|
||||
from .input_message_content import InputMessageContent
|
||||
|
||||
|
||||
class InlineQueryResultPhoto(InlineQueryResult):
|
||||
"""
|
||||
Represents a link to a photo. By default, this photo will be sent by the user with optional
|
||||
caption. Alternatively, you can use input_message_content to send a message with the specified
|
||||
content instead of the photo.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inlinequeryresultphoto
|
||||
"""
|
||||
|
||||
type: str = Field("photo", const=True)
|
||||
"""Type of the result, must be photo"""
|
||||
id: str
|
||||
"""Unique identifier for this result, 1-64 bytes"""
|
||||
photo_url: str
|
||||
"""A valid URL of the photo. Photo must be in jpeg format. Photo size must not exceed 5MB"""
|
||||
thumb_url: str
|
||||
"""URL of the thumbnail for the photo"""
|
||||
photo_width: Optional[int] = None
|
||||
"""Width of the photo"""
|
||||
photo_height: Optional[int] = None
|
||||
"""Height of the photo"""
|
||||
title: Optional[str] = None
|
||||
"""Title for the result"""
|
||||
description: Optional[str] = None
|
||||
"""Short description of the result"""
|
||||
caption: Optional[str] = None
|
||||
"""Caption of the photo to be sent, 0-1024 characters after entities parsing"""
|
||||
parse_mode: Optional[str] = UNSET
|
||||
"""Mode for parsing entities in the photo caption. See formatting options for more details."""
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None
|
||||
"""Inline keyboard attached to the message"""
|
||||
input_message_content: Optional[InputMessageContent] = None
|
||||
"""Content of the message to be sent instead of the photo"""
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .base import UNSET
|
||||
from .inline_query_result import InlineQueryResult
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .inline_keyboard_markup import InlineKeyboardMarkup
|
||||
from .input_message_content import InputMessageContent
|
||||
|
||||
|
||||
class InlineQueryResultVideo(InlineQueryResult):
|
||||
"""
|
||||
Represents a link to a page containing an embedded video player or a video file. By default,
|
||||
this video file will be sent by the user with an optional caption. Alternatively, you can use
|
||||
input_message_content to send a message with the specified content instead of the video.
|
||||
If an InlineQueryResultVideo message contains an embedded video (e.g., YouTube), you must
|
||||
replace its content using input_message_content.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inlinequeryresultvideo
|
||||
"""
|
||||
|
||||
type: str = Field("video", const=True)
|
||||
"""Type of the result, must be video"""
|
||||
id: str
|
||||
"""Unique identifier for this result, 1-64 bytes"""
|
||||
video_url: str
|
||||
"""A valid URL for the embedded video player or video file"""
|
||||
mime_type: str
|
||||
"""Mime type of the content of video url, 'text/html' or 'video/mp4'"""
|
||||
thumb_url: str
|
||||
"""URL of the thumbnail (jpeg only) for the video"""
|
||||
title: str
|
||||
"""Title for the result"""
|
||||
caption: Optional[str] = None
|
||||
"""Caption of the video to be sent, 0-1024 characters after entities parsing"""
|
||||
parse_mode: Optional[str] = UNSET
|
||||
"""Mode for parsing entities in the video caption. See formatting options for more details."""
|
||||
video_width: Optional[int] = None
|
||||
"""Video width"""
|
||||
video_height: Optional[int] = None
|
||||
"""Video height"""
|
||||
video_duration: Optional[int] = None
|
||||
"""Video duration in seconds"""
|
||||
description: Optional[str] = None
|
||||
"""Short description of the result"""
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None
|
||||
"""Inline keyboard attached to the message"""
|
||||
input_message_content: Optional[InputMessageContent] = None
|
||||
"""Content of the message to be sent instead of the video. This field is required if
|
||||
InlineQueryResultVideo is used to send an HTML-page as a result (e.g., a YouTube video)."""
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .base import UNSET
|
||||
from .inline_query_result import InlineQueryResult
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .inline_keyboard_markup import InlineKeyboardMarkup
|
||||
from .input_message_content import InputMessageContent
|
||||
|
||||
|
||||
class InlineQueryResultVoice(InlineQueryResult):
|
||||
"""
|
||||
Represents a link to a voice recording in an .OGG container encoded with OPUS. By default,
|
||||
this voice recording will be sent by the user. Alternatively, you can use
|
||||
input_message_content to send a message with the specified content instead of the the voice
|
||||
message.
|
||||
Note: This will only work in Telegram versions released after 9 April, 2016. Older clients
|
||||
will ignore them.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inlinequeryresultvoice
|
||||
"""
|
||||
|
||||
type: str = Field("voice", const=True)
|
||||
"""Type of the result, must be voice"""
|
||||
id: str
|
||||
"""Unique identifier for this result, 1-64 bytes"""
|
||||
voice_url: str
|
||||
"""A valid URL for the voice recording"""
|
||||
title: str
|
||||
"""Recording title"""
|
||||
caption: Optional[str] = None
|
||||
"""Caption, 0-1024 characters after entities parsing"""
|
||||
parse_mode: Optional[str] = UNSET
|
||||
"""Mode for parsing entities in the voice message caption. See formatting options for more
|
||||
details."""
|
||||
voice_duration: Optional[int] = None
|
||||
"""Recording duration in seconds"""
|
||||
reply_markup: Optional[InlineKeyboardMarkup] = None
|
||||
"""Inline keyboard attached to the message"""
|
||||
input_message_content: Optional[InputMessageContent] = None
|
||||
"""Content of the message to be sent instead of the voice recording"""
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from .input_message_content import InputMessageContent
|
||||
|
||||
|
||||
class InputLocationMessageContent(InputMessageContent):
|
||||
"""
|
||||
Represents the content of a location message to be sent as the result of an inline query.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inputlocationmessagecontent
|
||||
"""
|
||||
|
||||
latitude: float
|
||||
"""Latitude of the location in degrees"""
|
||||
longitude: float
|
||||
"""Longitude of the location in degrees"""
|
||||
live_period: Optional[int] = None
|
||||
"""Period in seconds for which the location can be updated, should be between 60 and 86400."""
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from .base import MutableTelegramObject
|
||||
|
||||
|
||||
class InputMedia(MutableTelegramObject):
|
||||
"""
|
||||
This object represents the content of a media message to be sent. It should be one of
|
||||
- InputMediaAnimation
|
||||
- InputMediaDocument
|
||||
- InputMediaAudio
|
||||
- InputMediaPhoto
|
||||
- InputMediaVideo
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inputmedia
|
||||
"""
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional, Union
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .base import UNSET
|
||||
from .input_media import InputMedia
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .input_file import InputFile
|
||||
|
||||
|
||||
class InputMediaAnimation(InputMedia):
|
||||
"""
|
||||
Represents an animation file (GIF or H.264/MPEG-4 AVC video without sound) to be sent.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inputmediaanimation
|
||||
"""
|
||||
|
||||
type: str = Field("animation", const=True)
|
||||
"""Type of the result, must be animation"""
|
||||
media: Union[str, InputFile]
|
||||
"""File to send. Pass a file_id to send a file that exists on the Telegram servers
|
||||
(recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass
|
||||
'attach://<file_attach_name>' to upload a new one using multipart/form-data under
|
||||
<file_attach_name> name."""
|
||||
thumb: Optional[Union[InputFile, str]] = None
|
||||
"""Thumbnail of the file sent; can be ignored if thumbnail generation for the file is
|
||||
supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size.
|
||||
A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded
|
||||
using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new
|
||||
file, so you can pass 'attach://<file_attach_name>' if the thumbnail was uploaded using
|
||||
multipart/form-data under <file_attach_name>."""
|
||||
caption: Optional[str] = None
|
||||
"""Caption of the animation to be sent, 0-1024 characters after entities parsing"""
|
||||
parse_mode: Optional[str] = UNSET
|
||||
"""Mode for parsing entities in the animation caption. See formatting options for more
|
||||
details."""
|
||||
width: Optional[int] = None
|
||||
"""Animation width"""
|
||||
height: Optional[int] = None
|
||||
"""Animation height"""
|
||||
duration: Optional[int] = None
|
||||
"""Animation duration"""
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional, Union
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .base import UNSET
|
||||
from .input_media import InputMedia
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .input_file import InputFile
|
||||
|
||||
|
||||
class InputMediaAudio(InputMedia):
|
||||
"""
|
||||
Represents an audio file to be treated as music to be sent.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inputmediaaudio
|
||||
"""
|
||||
|
||||
type: str = Field("audio", const=True)
|
||||
"""Type of the result, must be audio"""
|
||||
media: Union[str, InputFile]
|
||||
"""File to send. Pass a file_id to send a file that exists on the Telegram servers
|
||||
(recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass
|
||||
'attach://<file_attach_name>' to upload a new one using multipart/form-data under
|
||||
<file_attach_name> name."""
|
||||
thumb: Optional[Union[InputFile, str]] = None
|
||||
"""Thumbnail of the file sent; can be ignored if thumbnail generation for the file is
|
||||
supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size.
|
||||
A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded
|
||||
using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new
|
||||
file, so you can pass 'attach://<file_attach_name>' if the thumbnail was uploaded using
|
||||
multipart/form-data under <file_attach_name>."""
|
||||
caption: Optional[str] = None
|
||||
"""Caption of the audio to be sent, 0-1024 characters after entities parsing"""
|
||||
parse_mode: Optional[str] = UNSET
|
||||
"""Mode for parsing entities in the audio caption. See formatting options for more details."""
|
||||
duration: Optional[int] = None
|
||||
"""Duration of the audio in seconds"""
|
||||
performer: Optional[str] = None
|
||||
"""Performer of the audio"""
|
||||
title: Optional[str] = None
|
||||
"""Title of the audio"""
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional, Union
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .base import UNSET
|
||||
from .input_media import InputMedia
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .input_file import InputFile
|
||||
|
||||
|
||||
class InputMediaDocument(InputMedia):
|
||||
"""
|
||||
Represents a general file to be sent.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inputmediadocument
|
||||
"""
|
||||
|
||||
type: str = Field("document", const=True)
|
||||
"""Type of the result, must be document"""
|
||||
media: Union[str, InputFile]
|
||||
"""File to send. Pass a file_id to send a file that exists on the Telegram servers
|
||||
(recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass
|
||||
'attach://<file_attach_name>' to upload a new one using multipart/form-data under
|
||||
<file_attach_name> name."""
|
||||
thumb: Optional[Union[InputFile, str]] = None
|
||||
"""Thumbnail of the file sent; can be ignored if thumbnail generation for the file is
|
||||
supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size.
|
||||
A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded
|
||||
using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new
|
||||
file, so you can pass 'attach://<file_attach_name>' if the thumbnail was uploaded using
|
||||
multipart/form-data under <file_attach_name>."""
|
||||
caption: Optional[str] = None
|
||||
"""Caption of the document to be sent, 0-1024 characters after entities parsing"""
|
||||
parse_mode: Optional[str] = UNSET
|
||||
"""Mode for parsing entities in the document caption. See formatting options for more details."""
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional, Union
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .base import UNSET
|
||||
from .input_media import InputMedia
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .input_file import InputFile
|
||||
|
||||
|
||||
class InputMediaPhoto(InputMedia):
|
||||
"""
|
||||
Represents a photo to be sent.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inputmediaphoto
|
||||
"""
|
||||
|
||||
type: str = Field("photo", const=True)
|
||||
"""Type of the result, must be photo"""
|
||||
media: Union[str, InputFile]
|
||||
"""File to send. Pass a file_id to send a file that exists on the Telegram servers
|
||||
(recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass
|
||||
'attach://<file_attach_name>' to upload a new one using multipart/form-data under
|
||||
<file_attach_name> name."""
|
||||
caption: Optional[str] = None
|
||||
"""Caption of the photo to be sent, 0-1024 characters after entities parsing"""
|
||||
parse_mode: Optional[str] = UNSET
|
||||
"""Mode for parsing entities in the photo caption. See formatting options for more details."""
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional, Union
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .base import UNSET
|
||||
from .input_media import InputMedia
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .input_file import InputFile
|
||||
|
||||
|
||||
class InputMediaVideo(InputMedia):
|
||||
"""
|
||||
Represents a video to be sent.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inputmediavideo
|
||||
"""
|
||||
|
||||
type: str = Field("video", const=True)
|
||||
"""Type of the result, must be video"""
|
||||
media: Union[str, InputFile]
|
||||
"""File to send. Pass a file_id to send a file that exists on the Telegram servers
|
||||
(recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass
|
||||
'attach://<file_attach_name>' to upload a new one using multipart/form-data under
|
||||
<file_attach_name> name."""
|
||||
thumb: Optional[Union[InputFile, str]] = None
|
||||
"""Thumbnail of the file sent; can be ignored if thumbnail generation for the file is
|
||||
supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size.
|
||||
A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded
|
||||
using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new
|
||||
file, so you can pass 'attach://<file_attach_name>' if the thumbnail was uploaded using
|
||||
multipart/form-data under <file_attach_name>."""
|
||||
caption: Optional[str] = None
|
||||
"""Caption of the video to be sent, 0-1024 characters after entities parsing"""
|
||||
parse_mode: Optional[str] = UNSET
|
||||
"""Mode for parsing entities in the video caption. See formatting options for more details."""
|
||||
width: Optional[int] = None
|
||||
"""Video width"""
|
||||
height: Optional[int] = None
|
||||
"""Video height"""
|
||||
duration: Optional[int] = None
|
||||
"""Video duration"""
|
||||
supports_streaming: Optional[bool] = None
|
||||
"""Pass True, if the uploaded video is suitable for streaming"""
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from .base import MutableTelegramObject
|
||||
|
||||
|
||||
class InputMessageContent(MutableTelegramObject):
|
||||
"""
|
||||
This object represents the content of a message to be sent as a result of an inline query.
|
||||
Telegram clients currently support the following 4 types:
|
||||
- InputTextMessageContent
|
||||
- InputLocationMessageContent
|
||||
- InputVenueMessageContent
|
||||
- InputContactMessageContent
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inputmessagecontent
|
||||
"""
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from .base import UNSET
|
||||
from .input_message_content import InputMessageContent
|
||||
|
||||
|
||||
class InputTextMessageContent(InputMessageContent):
|
||||
"""
|
||||
Represents the content of a text message to be sent as the result of an inline query.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inputtextmessagecontent
|
||||
"""
|
||||
|
||||
message_text: str
|
||||
"""Text of the message to be sent, 1-4096 characters"""
|
||||
parse_mode: Optional[str] = UNSET
|
||||
"""Mode for parsing entities in the message text. See formatting options for more details."""
|
||||
disable_web_page_preview: Optional[bool] = None
|
||||
"""Disables link previews for links in the sent message"""
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from .input_message_content import InputMessageContent
|
||||
|
||||
|
||||
class InputVenueMessageContent(InputMessageContent):
|
||||
"""
|
||||
Represents the content of a venue message to be sent as the result of an inline query.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inputvenuemessagecontent
|
||||
"""
|
||||
|
||||
latitude: float
|
||||
"""Latitude of the venue in degrees"""
|
||||
longitude: float
|
||||
"""Longitude of the venue in degrees"""
|
||||
title: str
|
||||
"""Name of the venue"""
|
||||
address: str
|
||||
"""Address of the venue"""
|
||||
foursquare_id: Optional[str] = None
|
||||
"""Foursquare identifier of the venue, if known"""
|
||||
foursquare_type: Optional[str] = None
|
||||
"""Foursquare type of the venue, if known. (For example, 'arts_entertainment/default',
|
||||
'arts_entertainment/aquarium' or 'food/icecream'.)"""
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from .base import TelegramObject
|
||||
|
||||
|
||||
class Invoice(TelegramObject):
|
||||
"""
|
||||
This object contains basic information about an invoice.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#invoice
|
||||
"""
|
||||
|
||||
title: str
|
||||
"""Product name"""
|
||||
description: str
|
||||
"""Product description"""
|
||||
start_parameter: str
|
||||
"""Unique bot deep-linking parameter that can be used to generate this invoice"""
|
||||
currency: str
|
||||
"""Three-letter ISO 4217 currency code"""
|
||||
total_amount: int
|
||||
"""Total price in the smallest units of the currency (integer, not float/double). For example,
|
||||
for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it
|
||||
shows the number of digits past the decimal point for each currency (2 for the majority of
|
||||
currencies)."""
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from .base import MutableTelegramObject
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .keyboard_button_poll_type import KeyboardButtonPollType
|
||||
|
||||
|
||||
class KeyboardButton(MutableTelegramObject):
|
||||
"""
|
||||
This object represents one button of the reply keyboard. For simple text buttons String can be
|
||||
used instead of this object to specify text of the button. Optional fields request_contact,
|
||||
request_location, and request_poll are mutually exclusive.
|
||||
Note: request_contact and request_location options will only work in Telegram versions
|
||||
released after 9 April, 2016. Older clients will display unsupported message.
|
||||
Note: request_poll option will only work in Telegram versions released after 23 January, 2020.
|
||||
Older clients will display unsupported message.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#keyboardbutton
|
||||
"""
|
||||
|
||||
text: str
|
||||
"""Text of the button. If none of the optional fields are used, it will be sent as a message
|
||||
when the button is pressed"""
|
||||
request_contact: Optional[bool] = None
|
||||
"""If True, the user's phone number will be sent as a contact when the button is pressed.
|
||||
Available in private chats only"""
|
||||
request_location: Optional[bool] = None
|
||||
"""If True, the user's current location will be sent when the button is pressed. Available in
|
||||
private chats only"""
|
||||
request_poll: Optional[KeyboardButtonPollType] = None
|
||||
"""If specified, the user will be asked to create a poll and send it to the bot when the
|
||||
button is pressed. Available in private chats only"""
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from .base import MutableTelegramObject
|
||||
|
||||
|
||||
class LabeledPrice(MutableTelegramObject):
|
||||
"""
|
||||
This object represents a portion of the price for goods or services.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#labeledprice
|
||||
"""
|
||||
|
||||
label: str
|
||||
"""Portion label"""
|
||||
amount: int
|
||||
"""Price of the product in the smallest units of the currency (integer, not float/double). For
|
||||
example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in
|
||||
currencies.json, it shows the number of digits past the decimal point for each currency (2
|
||||
for the majority of currencies)."""
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from .base import TelegramObject
|
||||
|
||||
|
||||
class Location(TelegramObject):
|
||||
"""
|
||||
This object represents a point on the map.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#location
|
||||
"""
|
||||
|
||||
longitude: float
|
||||
"""Longitude as defined by sender"""
|
||||
latitude: float
|
||||
"""Latitude as defined by sender"""
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from .base import TelegramObject
|
||||
|
||||
|
||||
class LoginUrl(TelegramObject):
|
||||
"""
|
||||
This object represents a parameter of the inline keyboard button used to automatically
|
||||
authorize a user. Serves as a great replacement for the Telegram Login Widget when the user is
|
||||
coming from Telegram. All the user needs to do is tap/click a button and confirm that they
|
||||
want to log in:
|
||||
Telegram apps support these buttons as of version 5.7.
|
||||
Sample bot: @discussbot
|
||||
|
||||
Source: https://core.telegram.org/bots/api#loginurl
|
||||
"""
|
||||
|
||||
url: str
|
||||
"""An HTTP URL to be opened with user authorization data added to the query string when the
|
||||
button is pressed. If the user refuses to provide authorization data, the original URL
|
||||
without information about the user will be opened. The data added is the same as described
|
||||
in Receiving authorization data."""
|
||||
forward_text: Optional[str] = None
|
||||
"""New text of the button in forwarded messages."""
|
||||
bot_username: Optional[str] = None
|
||||
"""Username of a bot, which will be used for user authorization. See Setting up a bot for more
|
||||
details. If not specified, the current bot's username will be assumed. The url's domain
|
||||
must be the same as the domain linked with the bot. See Linking your domain to the bot for
|
||||
more details."""
|
||||
request_write_access: Optional[bool] = None
|
||||
"""Pass True to request the permission for your bot to send messages to the user."""
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from .base import TelegramObject
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .user import User
|
||||
|
||||
|
||||
class MessageEntity(TelegramObject):
|
||||
"""
|
||||
This object represents one special entity in a text message. For example, hashtags, usernames,
|
||||
URLs, etc.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#messageentity
|
||||
"""
|
||||
|
||||
type: str
|
||||
"""Type of the entity. Can be 'mention' (@username), 'hashtag' (#hashtag), 'cashtag' ($USD),
|
||||
'bot_command' (/start@jobs_bot), 'url' (https://telegram.org), 'email'
|
||||
(do-not-reply@telegram.org), 'phone_number' (+1-212-555-0123), 'bold' (bold text), 'italic'
|
||||
(italic text), 'underline' (underlined text), 'strikethrough' (strikethrough text), 'code'
|
||||
(monowidth string), 'pre' (monowidth block), 'text_link' (for clickable text URLs),
|
||||
'text_mention' (for users without usernames)"""
|
||||
offset: int
|
||||
"""Offset in UTF-16 code units to the start of the entity"""
|
||||
length: int
|
||||
"""Length of the entity in UTF-16 code units"""
|
||||
url: Optional[str] = None
|
||||
"""For 'text_link' only, url that will be opened after user taps on the text"""
|
||||
user: Optional[User] = None
|
||||
"""For 'text_mention' only, the mentioned user"""
|
||||
language: Optional[str] = None
|
||||
"""For 'pre' only, the programming language of the entity text"""
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from .base import MutableTelegramObject
|
||||
|
||||
|
||||
class PassportElementError(MutableTelegramObject):
|
||||
"""
|
||||
This object represents an error in the Telegram Passport element which was submitted that
|
||||
should be resolved by the user. It should be one of:
|
||||
- PassportElementErrorDataField
|
||||
- PassportElementErrorFrontSide
|
||||
- PassportElementErrorReverseSide
|
||||
- PassportElementErrorSelfie
|
||||
- PassportElementErrorFile
|
||||
- PassportElementErrorFiles
|
||||
- PassportElementErrorTranslationFile
|
||||
- PassportElementErrorTranslationFiles
|
||||
- PassportElementErrorUnspecified
|
||||
|
||||
Source: https://core.telegram.org/bots/api#passportelementerror
|
||||
"""
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, List, Optional
|
||||
|
||||
from .base import MutableTelegramObject
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .keyboard_button import KeyboardButton
|
||||
|
||||
|
||||
class ReplyKeyboardMarkup(MutableTelegramObject):
|
||||
"""
|
||||
This object represents a custom keyboard with reply options (see Introduction to bots for
|
||||
details and examples).
|
||||
|
||||
Source: https://core.telegram.org/bots/api#replykeyboardmarkup
|
||||
"""
|
||||
|
||||
keyboard: List[List[KeyboardButton]]
|
||||
"""Array of button rows, each represented by an Array of KeyboardButton objects"""
|
||||
resize_keyboard: Optional[bool] = None
|
||||
"""Requests clients to resize the keyboard vertically for optimal fit (e.g., make the keyboard
|
||||
smaller if there are just two rows of buttons). Defaults to false, in which case the custom
|
||||
keyboard is always of the same height as the app's standard keyboard."""
|
||||
one_time_keyboard: Optional[bool] = None
|
||||
"""Requests clients to hide the keyboard as soon as it's been used. The keyboard will still be
|
||||
available, but clients will automatically display the usual letter-keyboard in the chat –
|
||||
the user can press a special button in the input field to see the custom keyboard again.
|
||||
Defaults to false."""
|
||||
selective: Optional[bool] = None
|
||||
"""Use this parameter if you want to show the keyboard to specific users only. Targets: 1)
|
||||
users that are @mentioned in the text of the Message object; 2) if the bot's message is a
|
||||
reply (has reply_to_message_id), sender of the original message."""
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from .base import MutableTelegramObject
|
||||
|
||||
|
||||
class ReplyKeyboardRemove(MutableTelegramObject):
|
||||
"""
|
||||
Upon receiving a message with this object, Telegram clients will remove the current custom
|
||||
keyboard and display the default letter-keyboard. By default, custom keyboards are displayed
|
||||
until a new keyboard is sent by a bot. An exception is made for one-time keyboards that are
|
||||
hidden immediately after the user presses a button (see ReplyKeyboardMarkup).
|
||||
|
||||
Source: https://core.telegram.org/bots/api#replykeyboardremove
|
||||
"""
|
||||
|
||||
remove_keyboard: bool = True
|
||||
"""Requests clients to remove the custom keyboard (user will not be able to summon this
|
||||
keyboard; if you want to hide the keyboard from sight but keep it accessible, use
|
||||
one_time_keyboard in ReplyKeyboardMarkup)"""
|
||||
selective: Optional[bool] = None
|
||||
"""Use this parameter if you want to remove the keyboard for specific users only. Targets: 1)
|
||||
users that are @mentioned in the text of the Message object; 2) if the bot's message is a
|
||||
reply (has reply_to_message_id), sender of the original message."""
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from .base import TelegramObject
|
||||
|
||||
|
||||
class ResponseParameters(TelegramObject):
|
||||
"""
|
||||
Contains information about why a request was unsuccessful.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#responseparameters
|
||||
"""
|
||||
|
||||
migrate_to_chat_id: Optional[int] = None
|
||||
"""The group has been migrated to a supergroup with the specified identifier. This number may
|
||||
be greater than 32 bits and some programming languages may have difficulty/silent defects
|
||||
in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or
|
||||
double-precision float type are safe for storing this identifier."""
|
||||
retry_after: Optional[int] = None
|
||||
"""In case of exceeding flood control, the number of seconds left to wait before the request
|
||||
can be repeated"""
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from .base import TelegramObject
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .callback_query import CallbackQuery
|
||||
from .chosen_inline_result import ChosenInlineResult
|
||||
from .inline_query import InlineQuery
|
||||
from .message import Message
|
||||
from .poll import Poll
|
||||
from .poll_answer import PollAnswer
|
||||
from .pre_checkout_query import PreCheckoutQuery
|
||||
from .shipping_query import ShippingQuery
|
||||
|
||||
|
||||
class Update(TelegramObject):
|
||||
"""
|
||||
This object represents an incoming update.
|
||||
At most one of the optional parameters can be present in any given update.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#update
|
||||
"""
|
||||
|
||||
update_id: int
|
||||
"""The update's unique identifier. Update identifiers start from a certain positive number and
|
||||
increase sequentially. This ID becomes especially handy if you're using Webhooks, since it
|
||||
allows you to ignore repeated updates or to restore the correct update sequence, should
|
||||
they get out of order. If there are no new updates for at least a week, then identifier of
|
||||
the next update will be chosen randomly instead of sequentially."""
|
||||
message: Optional[Message] = None
|
||||
"""New incoming message of any kind — text, photo, sticker, etc."""
|
||||
edited_message: Optional[Message] = None
|
||||
"""New version of a message that is known to the bot and was edited"""
|
||||
channel_post: Optional[Message] = None
|
||||
"""New incoming channel post of any kind — text, photo, sticker, etc."""
|
||||
edited_channel_post: Optional[Message] = None
|
||||
"""New version of a channel post that is known to the bot and was edited"""
|
||||
inline_query: Optional[InlineQuery] = None
|
||||
"""New incoming inline query"""
|
||||
chosen_inline_result: Optional[ChosenInlineResult] = None
|
||||
"""The result of an inline query that was chosen by a user and sent to their chat partner.
|
||||
Please see our documentation on the feedback collecting for details on how to enable these
|
||||
updates for your bot."""
|
||||
callback_query: Optional[CallbackQuery] = None
|
||||
"""New incoming callback query"""
|
||||
shipping_query: Optional[ShippingQuery] = None
|
||||
"""New incoming shipping query. Only for invoices with flexible price"""
|
||||
pre_checkout_query: Optional[PreCheckoutQuery] = None
|
||||
"""New incoming pre-checkout query. Contains full information about checkout"""
|
||||
poll: Optional[Poll] = None
|
||||
"""New poll state. Bots receive only updates about stopped polls and polls, which are sent by
|
||||
the bot"""
|
||||
poll_answer: Optional[PollAnswer] = None
|
||||
"""A user changed their answer in a non-anonymous poll. Bots receive new votes only in polls
|
||||
that were sent by the bot itself."""
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from .base import TelegramObject
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .location import Location
|
||||
|
||||
|
||||
class Venue(TelegramObject):
|
||||
"""
|
||||
This object represents a venue.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#venue
|
||||
"""
|
||||
|
||||
location: Location
|
||||
"""Venue location"""
|
||||
title: str
|
||||
"""Name of the venue"""
|
||||
address: str
|
||||
"""Address of the venue"""
|
||||
foursquare_id: Optional[str] = None
|
||||
"""Foursquare identifier of the venue"""
|
||||
foursquare_type: Optional[str] = None
|
||||
"""Foursquare type of the venue. (For example, 'arts_entertainment/default',
|
||||
'arts_entertainment/aquarium' or 'food/icecream'.)"""
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -17,7 +17,7 @@ from typing import (
|
|||
|
||||
from aiohttp import BasicAuth, ClientSession, FormData, TCPConnector
|
||||
|
||||
from aiogram.api.methods import Request, TelegramMethod
|
||||
from aiogram.methods import Request, TelegramMethod
|
||||
|
||||
from .base import BaseSession, UNSET
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ def _retrieve_basic(basic: _ProxyBasic) -> Dict[str, Any]:
|
|||
|
||||
|
||||
def _prepare_connector(chain_or_plain: _ProxyType) -> Tuple[Type["TCPConnector"], Dict[str, Any]]:
|
||||
from aiohttp_socks import ProxyInfo, ProxyConnector, ChainProxyConnector # type: ignore
|
||||
from aiohttp_socks import ChainProxyConnector, ProxyConnector, ProxyInfo # type: ignore
|
||||
|
||||
# since tuple is Iterable(compatible with _ProxyChain) object, we assume that
|
||||
# user wants chained proxies if tuple is a pair of string(url) and BasicAuth
|
||||
|
|
@ -17,8 +17,8 @@ from typing import (
|
|||
)
|
||||
|
||||
from aiogram.utils.exceptions import TelegramAPIError
|
||||
from aiogram.utils.helper import Default
|
||||
|
||||
from ....utils.helper import Default
|
||||
from ...methods import Response, TelegramMethod
|
||||
from ...types import UNSET
|
||||
from ..telegram import PRODUCTION, TelegramAPIServer
|
||||
|
|
@ -32,35 +32,63 @@ _JsonDumps = Callable[..., str]
|
|||
|
||||
|
||||
class BaseSession(abc.ABC):
|
||||
default_timeout: ClassVar[float] = 60.0
|
||||
api: Default[TelegramAPIServer] = Default(PRODUCTION)
|
||||
"""Telegra Bot API URL patterns"""
|
||||
json_loads: Default[_JsonLoads] = Default(json.loads)
|
||||
"""JSON loader"""
|
||||
json_dumps: Default[_JsonDumps] = Default(json.dumps)
|
||||
"""JSON dumper"""
|
||||
default_timeout: ClassVar[float] = 60.0
|
||||
"""Default timeout"""
|
||||
timeout: Default[float] = Default(fget=lambda self: float(self.__class__.default_timeout))
|
||||
"""Session scope request timeout"""
|
||||
|
||||
@classmethod
|
||||
def raise_for_status(cls, response: Response[T]) -> None:
|
||||
"""
|
||||
Check response status
|
||||
|
||||
:param response: Response instance
|
||||
"""
|
||||
if response.ok:
|
||||
return
|
||||
raise TelegramAPIError(response.description)
|
||||
|
||||
@abc.abstractmethod
|
||||
async def close(self) -> None: # pragma: no cover
|
||||
"""
|
||||
Close client session
|
||||
"""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
async def make_request(
|
||||
self, bot: Bot, method: TelegramMethod[T], timeout: Optional[int] = UNSET
|
||||
) -> T: # pragma: no cover
|
||||
"""
|
||||
Make request to Telegram Bot API
|
||||
|
||||
:param bot: Bot instance
|
||||
:param method: Method instance
|
||||
:param timeout: Request timeout
|
||||
:return:
|
||||
:raise TelegramApiError:
|
||||
"""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
async def stream_content(
|
||||
self, url: str, timeout: int, chunk_size: int
|
||||
) -> AsyncGenerator[bytes, None]: # pragma: no cover
|
||||
"""
|
||||
Stream reader
|
||||
"""
|
||||
yield b""
|
||||
|
||||
def prepare_value(self, value: Any) -> Union[str, int, bool]:
|
||||
"""
|
||||
Prepare value before send
|
||||
"""
|
||||
if isinstance(value, str):
|
||||
return value
|
||||
if isinstance(value, (list, dict)):
|
||||
|
|
@ -74,6 +102,9 @@ class BaseSession(abc.ABC):
|
|||
return str(value)
|
||||
|
||||
def clean_json(self, value: Any) -> Any:
|
||||
"""
|
||||
Clean data before send
|
||||
"""
|
||||
if isinstance(value, list):
|
||||
return [self.clean_json(v) for v in value if v is not None]
|
||||
elif isinstance(value, dict):
|
||||
52
aiogram/client/telegram.py
Normal file
52
aiogram/client/telegram.py
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TelegramAPIServer:
|
||||
"""
|
||||
Base config for API Endpoints
|
||||
"""
|
||||
|
||||
base: str
|
||||
file: str
|
||||
is_local: bool = False
|
||||
|
||||
def api_url(self, token: str, method: str) -> str:
|
||||
"""
|
||||
Generate URL for API methods
|
||||
|
||||
:param token: Bot token
|
||||
:param method: API method name (case insensitive)
|
||||
:return: URL
|
||||
"""
|
||||
return self.base.format(token=token, method=method)
|
||||
|
||||
def file_url(self, token: str, path: str) -> str:
|
||||
"""
|
||||
Generate URL for downloading files
|
||||
|
||||
:param token: Bot token
|
||||
:param path: file path
|
||||
:return: URL
|
||||
"""
|
||||
return self.file.format(token=token, path=path)
|
||||
|
||||
@classmethod
|
||||
def from_base(cls, base: str, is_local: bool = False) -> "TelegramAPIServer":
|
||||
"""
|
||||
Use this method to auto-generate TelegramAPIServer instance from base URL
|
||||
|
||||
:param base: Base URL
|
||||
:param is_local: Mark this server is in `local mode <https://core.telegram.org/bots/api#using-a-local-bot-api-server>`_.
|
||||
:return: instance of :class:`TelegramAPIServer`
|
||||
"""
|
||||
base = base.rstrip("/")
|
||||
return cls(
|
||||
base=f"{base}/bot{{token}}/{{method}}",
|
||||
file=f"{base}/file/bot{{token}}/{{path}}",
|
||||
is_local=is_local,
|
||||
)
|
||||
|
||||
|
||||
# Main API server
|
||||
PRODUCTION = TelegramAPIServer.from_base("https://api.telegram.org")
|
||||
|
|
@ -7,11 +7,13 @@ from asyncio import CancelledError, Future, Lock
|
|||
from typing import Any, AsyncGenerator, Dict, Optional, Union
|
||||
|
||||
from .. import loggers
|
||||
from ..api.client.bot import Bot
|
||||
from ..api.methods import TelegramMethod
|
||||
from ..api.types import Update, User
|
||||
from ..client.bot import Bot
|
||||
from ..methods import TelegramMethod
|
||||
from ..types import TelegramObject, Update, User
|
||||
from ..utils.exceptions import TelegramAPIError
|
||||
from .event.bases import NOT_HANDLED
|
||||
from .event.bases import UNHANDLED, SkipHandler
|
||||
from .event.telegram import TelegramEventObserver
|
||||
from .middlewares.error import ErrorsMiddleware
|
||||
from .middlewares.user_context import UserContextMiddleware
|
||||
from .router import Router
|
||||
|
||||
|
|
@ -23,10 +25,15 @@ class Dispatcher(Router):
|
|||
|
||||
def __init__(self, **kwargs: Any) -> None:
|
||||
super(Dispatcher, self).__init__(**kwargs)
|
||||
self._running_lock = Lock()
|
||||
|
||||
# Default middleware is needed for contextual features
|
||||
self.update = TelegramEventObserver(router=self, event_name="update")
|
||||
self.observers["update"] = self.update
|
||||
|
||||
self.update.register(self._listen_update)
|
||||
self.update.outer_middleware(UserContextMiddleware())
|
||||
self.update.outer_middleware(ErrorsMiddleware(self))
|
||||
|
||||
self._running_lock = Lock()
|
||||
|
||||
@property
|
||||
def parent_router(self) -> None:
|
||||
|
|
@ -61,7 +68,7 @@ class Dispatcher(Router):
|
|||
Bot.set_current(bot)
|
||||
try:
|
||||
response = await self.update.trigger(update, bot=bot, **kwargs)
|
||||
handled = response is not NOT_HANDLED
|
||||
handled = response is not UNHANDLED
|
||||
return response
|
||||
finally:
|
||||
finish_time = loop.time()
|
||||
|
|
@ -97,6 +104,74 @@ class Dispatcher(Router):
|
|||
yield update
|
||||
update_id = update.update_id + 1
|
||||
|
||||
async def _listen_update(self, update: Update, **kwargs: Any) -> Any:
|
||||
"""
|
||||
Main updates listener
|
||||
|
||||
Workflow:
|
||||
- Detect content type and propagate to observers in current router
|
||||
- If no one filter is pass - propagate update to child routers as Update
|
||||
|
||||
:param update:
|
||||
:param kwargs:
|
||||
:return:
|
||||
"""
|
||||
event: TelegramObject
|
||||
if update.message:
|
||||
update_type = "message"
|
||||
event = update.message
|
||||
elif update.edited_message:
|
||||
update_type = "edited_message"
|
||||
event = update.edited_message
|
||||
elif update.channel_post:
|
||||
update_type = "channel_post"
|
||||
event = update.channel_post
|
||||
elif update.edited_channel_post:
|
||||
update_type = "edited_channel_post"
|
||||
event = update.edited_channel_post
|
||||
elif update.inline_query:
|
||||
update_type = "inline_query"
|
||||
event = update.inline_query
|
||||
elif update.chosen_inline_result:
|
||||
update_type = "chosen_inline_result"
|
||||
event = update.chosen_inline_result
|
||||
elif update.callback_query:
|
||||
update_type = "callback_query"
|
||||
event = update.callback_query
|
||||
elif update.shipping_query:
|
||||
update_type = "shipping_query"
|
||||
event = update.shipping_query
|
||||
elif update.pre_checkout_query:
|
||||
update_type = "pre_checkout_query"
|
||||
event = update.pre_checkout_query
|
||||
elif update.poll:
|
||||
update_type = "poll"
|
||||
event = update.poll
|
||||
elif update.poll_answer:
|
||||
update_type = "poll_answer"
|
||||
event = update.poll_answer
|
||||
else:
|
||||
warnings.warn(
|
||||
"Detected unknown update type.\n"
|
||||
"Seems like Telegram Bot API was updated and you have "
|
||||
"installed not latest version of aiogram framework",
|
||||
RuntimeWarning,
|
||||
)
|
||||
raise SkipHandler
|
||||
|
||||
kwargs.update(event_update=update)
|
||||
|
||||
for router in self.chain:
|
||||
kwargs.update(event_router=router)
|
||||
observer = router.observers[update_type]
|
||||
response = await observer.trigger(event, update=update, **kwargs)
|
||||
if response is not UNHANDLED:
|
||||
break
|
||||
else:
|
||||
response = UNHANDLED
|
||||
|
||||
return response
|
||||
|
||||
@classmethod
|
||||
async def _silent_call_request(cls, bot: Bot, result: TelegramMethod[Any]) -> None:
|
||||
"""
|
||||
|
|
@ -129,7 +204,7 @@ class Dispatcher(Router):
|
|||
handled = False
|
||||
try:
|
||||
response = await self.feed_update(bot, update, **kwargs)
|
||||
handled = handled is not NOT_HANDLED
|
||||
handled = handled is not UNHANDLED
|
||||
if call_answer and isinstance(response, TelegramMethod):
|
||||
await self._silent_call_request(bot=bot, result=response)
|
||||
return handled
|
||||
|
|
@ -172,7 +247,7 @@ class Dispatcher(Router):
|
|||
raise
|
||||
|
||||
async def feed_webhook_update(
|
||||
self, bot: Bot, update: Union[Update, Dict[str, Any]], _timeout: int = 55, **kwargs: Any
|
||||
self, bot: Bot, update: Union[Update, Dict[str, Any]], _timeout: float = 55, **kwargs: Any
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
if not isinstance(update, Update): # Allow to use raw updates
|
||||
update = Update(**update)
|
||||
|
|
@ -255,7 +330,7 @@ class Dispatcher(Router):
|
|||
await asyncio.gather(*coro_list)
|
||||
finally:
|
||||
for bot in bots: # Close sessions
|
||||
await bot.close()
|
||||
await bot.session.close()
|
||||
loggers.dispatcher.info("Polling stopped")
|
||||
await self.emit_shutdown(**workflow_data)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
|||
from typing import Any, Awaitable, Callable, Dict, NoReturn, Optional, Union
|
||||
from unittest.mock import sentinel
|
||||
|
||||
from ...api.types import TelegramObject
|
||||
from ...types import TelegramObject
|
||||
from ..middlewares.base import BaseMiddleware
|
||||
|
||||
NextMiddlewareType = Callable[[TelegramObject, Dict[str, Any]], Awaitable[Any]]
|
||||
|
|
@ -11,7 +11,7 @@ MiddlewareType = Union[
|
|||
BaseMiddleware, Callable[[NextMiddlewareType, TelegramObject, Dict[str, Any]], Awaitable[Any]]
|
||||
]
|
||||
|
||||
NOT_HANDLED = sentinel.NOT_HANDLED
|
||||
UNHANDLED = sentinel.UNHANDLED
|
||||
|
||||
|
||||
class SkipHandler(Exception):
|
||||
|
|
|
|||
|
|
@ -8,6 +8,19 @@ from .handler import CallbackType, HandlerObject, HandlerType
|
|||
class EventObserver:
|
||||
"""
|
||||
Simple events observer
|
||||
|
||||
Is used for managing events is not related with Telegram (For example startup/shutdown processes)
|
||||
|
||||
Handlers can be registered via decorator or method
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
<observer>.register(my_handler)
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@<observer>()
|
||||
async def my_handler(*args, **kwargs): ...
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
|
|
|
|||
|
|
@ -24,12 +24,6 @@ class CallableMixin:
|
|||
def __post_init__(self) -> None:
|
||||
callback = inspect.unwrap(self.callback)
|
||||
self.awaitable = inspect.isawaitable(callback) or inspect.iscoroutinefunction(callback)
|
||||
if isinstance(callback, BaseFilter):
|
||||
# Pydantic 1.5 has incorrect signature generator
|
||||
# Issue: https://github.com/samuelcolvin/pydantic/issues/1419
|
||||
# Fixes: https://github.com/samuelcolvin/pydantic/pull/1427
|
||||
# TODO: Remove this temporary fix
|
||||
callback = inspect.unwrap(callback.__call__)
|
||||
self.spec = inspect.getfullargspec(callback)
|
||||
|
||||
def _prepare_kwargs(self, kwargs: Dict[str, Any]) -> Dict[str, Any]:
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ from typing import TYPE_CHECKING, Any, Callable, Dict, Generator, List, Optional
|
|||
|
||||
from pydantic import ValidationError
|
||||
|
||||
from ...api.types import TelegramObject
|
||||
from ...types import TelegramObject
|
||||
from ..filters.base import BaseFilter
|
||||
from .bases import NOT_HANDLED, MiddlewareType, NextMiddlewareType, SkipHandler
|
||||
from .bases import UNHANDLED, MiddlewareType, NextMiddlewareType, SkipHandler
|
||||
from .handler import CallbackType, FilterObject, FilterType, HandlerObject, HandlerType
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
|
|
@ -18,6 +18,9 @@ if TYPE_CHECKING: # pragma: no cover
|
|||
class TelegramEventObserver:
|
||||
"""
|
||||
Event observer for Telegram events
|
||||
|
||||
Here you can register handler with filters or bounded filters which can be used as keyword arguments instead of writing full references when you register new handlers.
|
||||
This observer will stops event propagation when first handler is pass.
|
||||
"""
|
||||
|
||||
def __init__(self, router: Router, event_name: str) -> None:
|
||||
|
|
@ -57,6 +60,17 @@ class TelegramEventObserver:
|
|||
yield filter_
|
||||
registry.append(filter_)
|
||||
|
||||
def _resolve_inner_middlewares(self) -> List[MiddlewareType]:
|
||||
"""
|
||||
Get all inner middlewares in an tree
|
||||
"""
|
||||
middlewares = []
|
||||
|
||||
for router in self.router.chain_head:
|
||||
observer = router.observers[self.event_name]
|
||||
middlewares.extend(observer.middlewares)
|
||||
return middlewares
|
||||
|
||||
def resolve_filters(self, full_config: Dict[str, Any]) -> List[BaseFilter]:
|
||||
"""
|
||||
Resolve keyword filters via filters factory
|
||||
|
|
@ -126,12 +140,14 @@ class TelegramEventObserver:
|
|||
if result:
|
||||
kwargs.update(data)
|
||||
try:
|
||||
wrapped_inner = self._wrap_middleware(self.middlewares, handler.call)
|
||||
wrapped_inner = self._wrap_middleware(
|
||||
self._resolve_inner_middlewares(), handler.call
|
||||
)
|
||||
return await wrapped_inner(event, kwargs)
|
||||
except SkipHandler:
|
||||
continue
|
||||
|
||||
return NOT_HANDLED
|
||||
return UNHANDLED
|
||||
|
||||
def __call__(
|
||||
self, *args: FilterType, **bound_filters: BaseFilter
|
||||
|
|
@ -147,16 +163,26 @@ class TelegramEventObserver:
|
|||
return wrapper
|
||||
|
||||
def middleware(
|
||||
self, middleware: Optional[MiddlewareType] = None,
|
||||
self,
|
||||
middleware: Optional[MiddlewareType] = None,
|
||||
) -> Union[Callable[[MiddlewareType], MiddlewareType], MiddlewareType]:
|
||||
"""
|
||||
Decorator for registering inner middlewares
|
||||
|
||||
Usage:
|
||||
>>> @<event>.middleware() # via decorator (variant 1)
|
||||
>>> @<event>.middleware # via decorator (variant 2)
|
||||
>>> async def my_middleware(handler, event, data): ...
|
||||
>>> <event>.middleware(middleware) # via method
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@<event>.middleware() # via decorator (variant 1)
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@<event>.middleware # via decorator (variant 2)
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
async def my_middleware(handler, event, data): ...
|
||||
<event>.middleware(my_middleware) # via method
|
||||
"""
|
||||
|
||||
def wrapper(m: MiddlewareType) -> MiddlewareType:
|
||||
|
|
@ -168,16 +194,26 @@ class TelegramEventObserver:
|
|||
return wrapper(middleware)
|
||||
|
||||
def outer_middleware(
|
||||
self, middleware: Optional[MiddlewareType] = None,
|
||||
self,
|
||||
middleware: Optional[MiddlewareType] = None,
|
||||
) -> Union[Callable[[MiddlewareType], MiddlewareType], MiddlewareType]:
|
||||
"""
|
||||
Decorator for registering outer middlewares
|
||||
|
||||
Usage:
|
||||
>>> @<event>.outer_middleware() # via decorator (variant 1)
|
||||
>>> @<event>.outer_middleware # via decorator (variant 2)
|
||||
>>> async def my_middleware(handler, event, data): ...
|
||||
>>> <event>.outer_middleware(my_middleware) # via method
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@<event>.outer_middleware() # via decorator (variant 1)
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@<event>.outer_middleware # via decorator (variant 2)
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
async def my_middleware(handler, event, data): ...
|
||||
<event>.outer_middleware(my_middleware) # via method
|
||||
"""
|
||||
|
||||
def wrapper(m: MiddlewareType) -> MiddlewareType:
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ __all__ = (
|
|||
)
|
||||
|
||||
BUILTIN_FILTERS: Dict[str, Tuple[Type[BaseFilter], ...]] = {
|
||||
"update": (),
|
||||
"message": (Text, Command, ContentTypesFilter),
|
||||
"edited_message": (Text, Command, ContentTypesFilter),
|
||||
"channel_post": (Text, ContentTypesFilter),
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ from typing import Any, Dict, Match, Optional, Pattern, Sequence, Union, cast
|
|||
from pydantic import validator
|
||||
|
||||
from aiogram import Bot
|
||||
from aiogram.api.types import Message
|
||||
from aiogram.dispatcher.filters import BaseFilter
|
||||
from aiogram.types import Message
|
||||
|
||||
CommandPatterType = Union[str, re.Pattern]
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@ from typing import Any, Dict, Optional, Sequence, Union
|
|||
|
||||
from pydantic import validator
|
||||
|
||||
from ...api.types import Message
|
||||
from ...api.types.message import ContentType
|
||||
from aiogram.types.message import ContentType
|
||||
|
||||
from ...types import Message
|
||||
from .base import BaseFilter
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ from typing import Any, Dict, Optional, Sequence, Union
|
|||
|
||||
from pydantic import root_validator
|
||||
|
||||
from aiogram.api.types import CallbackQuery, InlineQuery, Message, Poll
|
||||
from aiogram.dispatcher.filters import BaseFilter
|
||||
from aiogram.types import CallbackQuery, InlineQuery, Message, Poll
|
||||
|
||||
TextType = str
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from abc import ABC, abstractmethod
|
|||
from typing import TYPE_CHECKING, Any, Dict, Generic, TypeVar, cast
|
||||
|
||||
from aiogram import Bot
|
||||
from aiogram.api.types import Update
|
||||
from aiogram.types import Update
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
from abc import ABC
|
||||
from typing import Optional
|
||||
|
||||
from aiogram.api.types import CallbackQuery, Message, User
|
||||
from aiogram.dispatcher.handler import BaseHandler
|
||||
from aiogram.types import CallbackQuery, Message, User
|
||||
|
||||
|
||||
class CallbackQueryHandler(BaseHandler[CallbackQuery], ABC):
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from abc import ABC
|
||||
|
||||
from aiogram.api.types import ChosenInlineResult, User
|
||||
from aiogram.dispatcher.handler import BaseHandler
|
||||
from aiogram.types import ChosenInlineResult, User
|
||||
|
||||
|
||||
class ChosenInlineResultHandler(BaseHandler[ChosenInlineResult], ABC):
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from abc import ABC
|
||||
|
||||
from aiogram.api.types import InlineQuery, User
|
||||
from aiogram.dispatcher.handler import BaseHandler
|
||||
from aiogram.types import InlineQuery, User
|
||||
|
||||
|
||||
class InlineQueryHandler(BaseHandler[InlineQuery], ABC):
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
from abc import ABC
|
||||
from typing import Optional, cast
|
||||
|
||||
from aiogram.api.types import Chat, Message, User
|
||||
from aiogram.dispatcher.filters import CommandObject
|
||||
from aiogram.dispatcher.handler.base import BaseHandler, BaseHandlerMixin
|
||||
from aiogram.types import Chat, Message, User
|
||||
|
||||
|
||||
class MessageHandler(BaseHandler[Message], ABC):
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
from abc import ABC
|
||||
from typing import List
|
||||
|
||||
from aiogram.api.types import Poll, PollOption
|
||||
from aiogram.dispatcher.handler import BaseHandler
|
||||
from aiogram.types import Poll, PollOption
|
||||
|
||||
|
||||
class PollHandler(BaseHandler[Poll], ABC):
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from abc import ABC
|
||||
|
||||
from aiogram.api.types import PreCheckoutQuery, User
|
||||
from aiogram.dispatcher.handler import BaseHandler
|
||||
from aiogram.types import PreCheckoutQuery, User
|
||||
|
||||
|
||||
class PreCheckoutQueryHandler(BaseHandler[PreCheckoutQuery], ABC):
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue