Merge branch 'dev-2.x'

# Conflicts:
#	aiogram/__init__.py
This commit is contained in:
Alex Root Junior 2020-11-08 18:26:37 +02:00
commit dc235c66c2
58 changed files with 2939 additions and 1242 deletions

View file

@ -21,7 +21,7 @@ AIOGramBot
:target: https://pypi.python.org/pypi/aiogram
:alt: Supported python versions
.. image:: https://img.shields.io/badge/Telegram%20Bot%20API-4.9-blue.svg?style=flat-square&logo=telegram
.. image:: https://img.shields.io/badge/Telegram%20Bot%20API-5.0-blue.svg?style=flat-square&logo=telegram
:target: https://core.telegram.org/bots/api
:alt: Telegram Bot API

View file

@ -43,5 +43,5 @@ __all__ = (
'utils',
)
__version__ = '2.10.1'
__api_version__ = '4.9'
__version__ = '2.11'
__api_version__ = '5.0'

View file

@ -1,20 +1,57 @@
import logging
import os
from dataclasses import dataclass
from http import HTTPStatus
import aiohttp
from .. import types
from ..utils import exceptions
from ..utils import json
from ..utils import exceptions, json
from ..utils.helper import Helper, HelperMode, Item
# Main aiogram logger
log = logging.getLogger('aiogram')
# API Url's
API_URL = "https://api.telegram.org/bot{token}/{method}"
FILE_URL = "https://api.telegram.org/file/bot{token}/{path}"
@dataclass(frozen=True)
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)
@classmethod
def from_base(cls, base: str) -> 'TelegramAPIServer':
base = base.rstrip("/")
return cls(
base=f"{base}/bot{{token}}/{{method}}",
file=f"{base}/file/bot{{token}}/{{method}}",
)
TELEGRAM_PRODUCTION = TelegramAPIServer.from_base("https://api.telegram.org")
def check_token(token: str) -> bool:
@ -92,11 +129,10 @@ def check_result(method_name: str, content_type: str, status_code: int, body: st
raise exceptions.TelegramAPIError(f"{description} [{status_code}]")
async def make_request(session, token, method, data=None, files=None, **kwargs):
# log.debug(f"Make request: '{method}' with data: {data} and files {files}")
async def make_request(session, server, token, method, data=None, files=None, **kwargs):
log.debug('Make request: "%s" with data: "%r" and files "%r"', method, data, files)
url = Methods.api_url(token=token, method=method)
url = server.api_url(token=token, method=method)
req = compose_data(data, files)
try:
@ -153,7 +189,7 @@ class Methods(Helper):
"""
Helper for Telegram API Methods listed on https://core.telegram.org/bots/api
List is updated to Bot API 4.9
List is updated to Bot API 5.0
"""
mode = HelperMode.lowerCamelCase
@ -165,8 +201,11 @@ class Methods(Helper):
# Available methods
GET_ME = Item() # getMe
LOG_OUT = Item() # logOut
CLOSE = Item() # close
SEND_MESSAGE = Item() # sendMessage
FORWARD_MESSAGE = Item() # forwardMessage
COPY_MESSAGE = Item() # copyMessage
SEND_PHOTO = Item() # sendPhoto
SEND_AUDIO = Item() # sendAudio
SEND_DOCUMENT = Item() # sendDocument
@ -198,6 +237,7 @@ class Methods(Helper):
SET_CHAT_DESCRIPTION = Item() # setChatDescription
PIN_CHAT_MESSAGE = Item() # pinChatMessage
UNPIN_CHAT_MESSAGE = Item() # unpinChatMessage
UNPIN_ALL_CHAT_MESSAGES = Item() # unpinAllChatMessages
LEAVE_CHAT = Item() # leaveChat
GET_CHAT = Item() # getChat
GET_CHAT_ADMINISTRATORS = Item() # getChatAdministrators
@ -242,25 +282,3 @@ class Methods(Helper):
SEND_GAME = Item() # sendGame
SET_GAME_SCORE = Item() # setGameScore
GET_GAME_HIGH_SCORES = Item() # getGameHighScores
@staticmethod
def api_url(token, method):
"""
Generate API URL with included token and method name
:param token:
:param method:
:return:
"""
return API_URL.format(token=token, method=method)
@staticmethod
def file_url(token, path):
"""
Generate File URL with included token and file path
:param token:
:param path:
:return:
"""
return FILE_URL.format(token=token, path=path)

View file

@ -12,9 +12,11 @@ import certifi
from aiohttp.helpers import sentinel
from . import api
from .api import TelegramAPIServer, TELEGRAM_PRODUCTION
from ..types import ParseMode, base
from ..utils import json
from ..utils.auth_widget import check_integrity
from ..utils.deprecated import deprecated
class BaseBot:
@ -33,7 +35,8 @@ class BaseBot:
proxy_auth: Optional[aiohttp.BasicAuth] = None,
validate_token: Optional[base.Boolean] = True,
parse_mode: typing.Optional[base.String] = None,
timeout: typing.Optional[typing.Union[base.Integer, base.Float, aiohttp.ClientTimeout]] = None
timeout: typing.Optional[typing.Union[base.Integer, base.Float, aiohttp.ClientTimeout]] = None,
server: TelegramAPIServer = TELEGRAM_PRODUCTION
):
"""
Instructions how to get Bot token is found here: https://core.telegram.org/bots#3-how-do-i-create-a-bot
@ -54,6 +57,8 @@ class BaseBot:
:type parse_mode: :obj:`str`
:param timeout: Request timeout
:type timeout: :obj:`typing.Optional[typing.Union[base.Integer, base.Float, aiohttp.ClientTimeout]]`
:param server: Telegram Bot API Server endpoint.
:type server: :obj:`TelegramAPIServer`
:raise: when token is invalid throw an :obj:`aiogram.utils.exceptions.ValidationError`
"""
self._main_loop = loop
@ -64,6 +69,7 @@ class BaseBot:
self._token = None
self.__token = token
self.id = int(token.split(sep=':')[0])
self.server = server
self.proxy = proxy
self.proxy_auth = proxy_auth
@ -173,6 +179,8 @@ class BaseBot:
finally:
self._ctx_token.reset(token)
@deprecated("This method's behavior will be changed in aiogram v3.0. "
"More info: https://core.telegram.org/bots/api#close", stacklevel=3)
async def close(self):
"""
Close all client sessions
@ -197,7 +205,7 @@ class BaseBot:
:rtype: Union[List, Dict]
:raise: :obj:`aiogram.exceptions.TelegramApiError`
"""
return await api.make_request(self.session, self.__token, method, data, files,
return await api.make_request(self.session, self.server, self.__token, method, data, files,
proxy=self.proxy, proxy_auth=self.proxy_auth, timeout=self.timeout, **kwargs)
async def download_file(self, file_path: base.String,
@ -237,7 +245,7 @@ class BaseBot:
return dest
def get_file_url(self, file_path):
return api.Methods.file_url(token=self.__token, path=file_path)
return self.server.file_url(token=self.__token, path=file_path)
async def send_file(self, file_type, method, file, payload) -> Union[Dict, base.Boolean]:
"""

File diff suppressed because it is too large Load diff

View file

@ -5,9 +5,9 @@ This module has mongo storage for finite-state machine
from typing import Union, Dict, Optional, List, Tuple, AnyStr
import pymongo
try:
import pymongo
import motor
from motor.motor_asyncio import AsyncIOMotorClient, AsyncIOMotorDatabase
except ModuleNotFoundError as e:
@ -26,6 +26,7 @@ COLLECTIONS = (STATE, DATA, BUCKET)
class MongoStorage(BaseStorage):
"""
Mongo-based storage for FSM.
Usage:
.. code-block:: python3
@ -39,7 +40,6 @@ class MongoStorage(BaseStorage):
await dp.storage.close()
await dp.storage.wait_closed()
"""
def __init__(self, host='localhost', port=27017, db_name='aiogram_fsm', uri=None,

View file

@ -19,16 +19,17 @@ class RethinkDBStorage(BaseStorage):
Usage:
..code-block:: python3
.. code-block:: python3
storage = RethinkDBStorage(db='aiogram', table='aiogram', user='aiogram', password='aiogram_secret')
dispatcher = Dispatcher(bot, storage=storage)
And need to close connection when shutdown
..code-clock:: python3
.. code-block:: python3
await storage.close()
await storage.wait_closed()
"""
@ -54,7 +55,7 @@ class RethinkDBStorage(BaseStorage):
self._ssl = ssl or {}
self._loop = loop
self._conn: typing.Union[Connection, None] = None
self._conn: typing.Optional[Connection] = None
async def connect(self) -> Connection:
"""

View file

@ -300,7 +300,7 @@ class FSMContext:
async def update_data(self, data: typing.Dict = None, **kwargs):
await self.storage.update_data(chat=self.chat, user=self.user, data=data, **kwargs)
async def set_state(self, state: typing.Union[typing.AnyStr, None] = None):
async def set_state(self, state: typing.Optional[typing.AnyStr] = None):
await self.storage.set_state(chat=self.chat, user=self.user, state=self._resolve_state(state))
async def set_data(self, data: typing.Dict = None):

View file

@ -939,8 +939,8 @@ class SendMediaGroup(BaseResponse, ReplyToMixin, DisableNotificationMixin):
def __init__(self, chat_id: Union[Integer, String],
media: Union[types.MediaGroup, List] = None,
disable_notification: typing.Union[Boolean, None] = None,
reply_to_message_id: typing.Union[Integer, None] = None):
disable_notification: typing.Optional[Boolean] = None,
reply_to_message_id: typing.Optional[Integer] = None):
"""
Use this method to send a group of photos or videos as an album.
@ -951,9 +951,9 @@ class SendMediaGroup(BaseResponse, ReplyToMixin, DisableNotificationMixin):
:param media: A JSON-serialized array describing photos and videos to be sent
:type media: :obj:`typing.Union[types.MediaGroup, typing.List]`
:param disable_notification: Sends the message silently. Users will receive a notification with no sound.
:type disable_notification: :obj:`typing.Union[base.Boolean, None]`
:type disable_notification: :obj:`typing.Optional[base.Boolean]`
:param reply_to_message_id: If the message is a reply, ID of the original message
:type reply_to_message_id: :obj:`typing.Union[base.Integer, None]`
:type reply_to_message_id: :obj:`typing.Optional[base.Integer]`
:return: On success, an array of the sent Messages is returned.
:rtype: typing.List[types.Message]
"""

View file

@ -7,6 +7,7 @@ from .bot_command import BotCommand
from .callback_game import CallbackGame
from .callback_query import CallbackQuery
from .chat import Chat, ChatActions, ChatType
from .chat_location import ChatLocation
from .chat_member import ChatMember, ChatMemberStatus
from .chat_permissions import ChatPermissions
from .chat_photo import ChatPhoto
@ -40,6 +41,7 @@ from .login_url import LoginUrl
from .mask_position import MaskPosition
from .message import ContentType, ContentTypes, Message, ParseMode
from .message_entity import MessageEntity, MessageEntityType
from .message_id import MessageId
from .order_info import OrderInfo
from .passport_data import PassportData
from .passport_element_error import PassportElementError, PassportElementErrorDataField, PassportElementErrorFile, \
@ -49,6 +51,7 @@ from .passport_file import PassportFile
from .photo_size import PhotoSize
from .poll import PollOption, Poll, PollAnswer, PollType
from .pre_checkout_query import PreCheckoutQuery
from .proximity_alert_triggered import ProximityAlertTriggered
from .reply_keyboard import KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove, KeyboardButtonPollType
from .response_parameters import ResponseParameters
from .shipping_address import ShippingAddress
@ -76,6 +79,7 @@ __all__ = (
'CallbackQuery',
'Chat',
'ChatActions',
'ChatLocation',
'ChatMember',
'ChatMemberStatus',
'ChatPermissions',
@ -141,6 +145,7 @@ __all__ = (
'Message',
'MessageEntity',
'MessageEntityType',
'MessageId',
'OrderInfo',
'ParseMode',
'PassportData',
@ -158,6 +163,7 @@ __all__ = (
'PollOption',
'PollType',
'PreCheckoutQuery',
'ProximityAlertTriggered',
'ReplyKeyboardMarkup',
'ReplyKeyboardRemove',
'ResponseParameters',

View file

@ -15,6 +15,7 @@ class Audio(base.TelegramObject, mixins.Downloadable):
duration: base.Integer = fields.Field()
performer: base.String = fields.Field()
title: base.String = fields.Field()
file_name: base.String = fields.Field()
mime_type: base.String = fields.Field()
file_size: base.Integer = fields.Field()
thumb: PhotoSize = fields.Field(base=PhotoSize)

View file

@ -28,10 +28,10 @@ class CallbackQuery(base.TelegramObject):
data: base.String = fields.Field()
game_short_name: base.String = fields.Field()
async def answer(self, text: typing.Union[base.String, None] = None,
show_alert: typing.Union[base.Boolean, None] = None,
url: typing.Union[base.String, None] = None,
cache_time: typing.Union[base.Integer, None] = None):
async def answer(self, text: typing.Optional[base.String] = None,
show_alert: typing.Optional[base.Boolean] = None,
url: typing.Optional[base.String] = None,
cache_time: typing.Optional[base.Integer] = None):
"""
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.
@ -43,15 +43,15 @@ class CallbackQuery(base.TelegramObject):
Source: https://core.telegram.org/bots/api#answercallbackquery
:param text: Text of the notification. If not specified, nothing will be shown to the user, 0-200 characters
:type text: :obj:`typing.Union[base.String, None]`
:type text: :obj:`typing.Optional[base.String]`
:param show_alert: If true, an alert will be shown by the client instead of a notification
at the top of the chat screen. Defaults to false.
:type show_alert: :obj:`typing.Union[base.Boolean, None]`
:type show_alert: :obj:`typing.Optional[base.Boolean]`
:param url: URL that will be opened by the user's client.
:type url: :obj:`typing.Union[base.String, None]`
:type url: :obj:`typing.Optional[base.String]`
:param cache_time: The maximum amount of time in seconds that the
result of the callback query may be cached client-side.
:type cache_time: :obj:`typing.Union[base.Integer, None]`
:type cache_time: :obj:`typing.Optional[base.Integer]`
:return: On success, True is returned.
:rtype: :obj:`base.Boolean`"""
return await self.bot.answer_callback_query(callback_query_id=self.id,

View file

@ -4,13 +4,14 @@ import asyncio
import datetime
import typing
from ..utils import helper, markdown
from . import base, fields
from .chat_location import ChatLocation
from .chat_member import ChatMember
from .chat_permissions import ChatPermissions
from .chat_photo import ChatPhoto
from .input_file import InputFile
from ..utils.deprecated import deprecated
from ..utils import helper, markdown
from ..utils.deprecated import deprecated, DeprecatedReadOnlyClassVar
class Chat(base.TelegramObject):
@ -27,6 +28,7 @@ class Chat(base.TelegramObject):
last_name: base.String = fields.Field()
all_members_are_administrators: base.Boolean = fields.Field()
photo: ChatPhoto = fields.Field(base=ChatPhoto)
bio: base.String = fields.Field()
description: base.String = fields.Field()
invite_link: base.String = fields.Field()
pinned_message: 'Message' = fields.Field(base='Message')
@ -34,6 +36,8 @@ class Chat(base.TelegramObject):
slow_mode_delay: base.Integer = fields.Field()
sticker_set_name: base.String = fields.Field()
can_set_sticker_set: base.Boolean = fields.Field()
linked_chat_id: base.Integer = fields.Field()
location: ChatLocation = fields.Field()
def __hash__(self):
return self.id
@ -48,7 +52,7 @@ class Chat(base.TelegramObject):
return self.title
@property
def mention(self) -> typing.Union[base.String, None]:
def mention(self) -> typing.Optional[base.String]:
"""
Get mention if a Chat has a username, or get full name if this is a Private Chat, otherwise None is returned
"""
@ -175,14 +179,15 @@ class Chat(base.TelegramObject):
Source: https://core.telegram.org/bots/api#setchatdescription
:param description: New chat description, 0-255 characters
:type description: :obj:`typing.Union[base.String, None]`
:type description: :obj:`typing.Optional[base.String]`
:return: Returns True on success.
:rtype: :obj:`base.Boolean`
"""
return await self.bot.set_chat_description(self.id, description)
async def kick(self, user_id: base.Integer,
until_date: typing.Union[base.Integer, datetime.datetime, datetime.timedelta, None] = None) -> base.Boolean:
until_date: typing.Union[
base.Integer, datetime.datetime, datetime.timedelta, None] = None) -> base.Boolean:
"""
Use this method to kick a user from a group, a supergroup or a channel.
In the case of supergroups and channels, the user will not be able to return to the group
@ -199,35 +204,49 @@ class Chat(base.TelegramObject):
:param user_id: Unique identifier of the target user
:type user_id: :obj:`base.Integer`
:param until_date: Date when the user will be unbanned, unix time.
:type until_date: :obj:`typing.Union[base.Integer, None]`
:type until_date: :obj:`typing.Optional[base.Integer]`
:return: Returns True on success.
:rtype: :obj:`base.Boolean`
"""
return await self.bot.kick_chat_member(self.id, user_id=user_id, until_date=until_date)
async def unban(self, user_id: base.Integer) -> base.Boolean:
async def unban(self,
user_id: base.Integer,
only_if_banned: typing.Optional[base.Boolean] = None,
) -> base.Boolean:
"""
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.
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. By default, this method guarantees that after the call the user is not
a member of the chat, but will be able to join it. So if the user is a member
of the chat they will also be removed from the chat. If you don't want this,
use the parameter only_if_banned. Returns True on success.
Source: https://core.telegram.org/bots/api#unbanchatmember
:param user_id: Unique identifier of the target user
:type user_id: :obj:`base.Integer`
:param only_if_banned: Do nothing if the user is not banned
:type only_if_banned: :obj:`typing.Optional[base.Boolean]`
:return: Returns True on success.
:rtype: :obj:`base.Boolean`
"""
return await self.bot.unban_chat_member(self.id, user_id=user_id)
return await self.bot.unban_chat_member(
chat_id=self.id,
user_id=user_id,
only_if_banned=only_if_banned,
)
async def restrict(self, user_id: base.Integer,
permissions: typing.Optional[ChatPermissions] = None,
until_date: typing.Union[base.Integer, datetime.datetime, datetime.timedelta, None] = None,
can_send_messages: typing.Union[base.Boolean, None] = None,
can_send_media_messages: typing.Union[base.Boolean, None] = None,
can_send_other_messages: typing.Union[base.Boolean, None] = None,
can_add_web_page_previews: typing.Union[base.Boolean, None] = None) -> base.Boolean:
can_send_messages: typing.Optional[base.Boolean] = None,
can_send_media_messages: typing.Optional[base.Boolean] = None,
can_send_other_messages: typing.Optional[base.Boolean] = None,
can_add_web_page_previews: typing.Optional[base.Boolean] = None) -> base.Boolean:
"""
Use this method to restrict a user in a supergroup.
The bot must be an administrator in the supergroup for this to work and must have the appropriate admin rights.
@ -240,18 +259,18 @@ class Chat(base.TelegramObject):
:param permissions: New user permissions
:type permissions: :obj:`ChatPermissions`
:param until_date: Date when restrictions will be lifted for the user, unix time.
:type until_date: :obj:`typing.Union[base.Integer, None]`
:type until_date: :obj:`typing.Optional[base.Integer]`
:param can_send_messages: Pass True, if the user can send text messages, contacts, locations and venues
:type can_send_messages: :obj:`typing.Union[base.Boolean, None]`
:type can_send_messages: :obj:`typing.Optional[base.Boolean]`
:param can_send_media_messages: Pass True, if the user can send audios, documents, photos, videos,
video notes and voice notes, implies can_send_messages
:type can_send_media_messages: :obj:`typing.Union[base.Boolean, None]`
:type can_send_media_messages: :obj:`typing.Optional[base.Boolean]`
:param can_send_other_messages: Pass True, if the user can send animations, games, stickers and
use inline bots, implies can_send_media_messages
:type can_send_other_messages: :obj:`typing.Union[base.Boolean, None]`
:type can_send_other_messages: :obj:`typing.Optional[base.Boolean]`
:param can_add_web_page_previews: Pass True, if the user may add web page previews to their messages,
implies can_send_media_messages
:type can_add_web_page_previews: :obj:`typing.Union[base.Boolean, None]`
:type can_add_web_page_previews: :obj:`typing.Optional[base.Boolean]`
:return: Returns True on success.
:rtype: :obj:`base.Boolean`
"""
@ -264,14 +283,14 @@ class Chat(base.TelegramObject):
can_add_web_page_previews=can_add_web_page_previews)
async def promote(self, user_id: base.Integer,
can_change_info: typing.Union[base.Boolean, None] = None,
can_post_messages: typing.Union[base.Boolean, None] = None,
can_edit_messages: typing.Union[base.Boolean, None] = None,
can_delete_messages: typing.Union[base.Boolean, None] = None,
can_invite_users: typing.Union[base.Boolean, None] = None,
can_restrict_members: typing.Union[base.Boolean, None] = None,
can_pin_messages: typing.Union[base.Boolean, None] = None,
can_promote_members: typing.Union[base.Boolean, None] = None) -> base.Boolean:
can_change_info: typing.Optional[base.Boolean] = None,
can_post_messages: typing.Optional[base.Boolean] = None,
can_edit_messages: typing.Optional[base.Boolean] = None,
can_delete_messages: typing.Optional[base.Boolean] = None,
can_invite_users: typing.Optional[base.Boolean] = None,
can_restrict_members: typing.Optional[base.Boolean] = None,
can_pin_messages: typing.Optional[base.Boolean] = None,
can_promote_members: typing.Optional[base.Boolean] = None) -> base.Boolean:
"""
Use this method to promote or demote a user in a supergroup or a channel.
The bot must be an administrator in the chat for this to work and must have the appropriate admin rights.
@ -282,23 +301,23 @@ class Chat(base.TelegramObject):
:param user_id: Unique identifier of the target user
:type user_id: :obj:`base.Integer`
:param can_change_info: Pass True, if the administrator can change chat title, photo and other settings
:type can_change_info: :obj:`typing.Union[base.Boolean, None]`
:type can_change_info: :obj:`typing.Optional[base.Boolean]`
:param can_post_messages: Pass True, if the administrator can create channel posts, channels only
:type can_post_messages: :obj:`typing.Union[base.Boolean, None]`
:type can_post_messages: :obj:`typing.Optional[base.Boolean]`
:param can_edit_messages: Pass True, if the administrator can edit messages of other users, channels only
:type can_edit_messages: :obj:`typing.Union[base.Boolean, None]`
:type can_edit_messages: :obj:`typing.Optional[base.Boolean]`
:param can_delete_messages: Pass True, if the administrator can delete messages of other users
:type can_delete_messages: :obj:`typing.Union[base.Boolean, None]`
:type can_delete_messages: :obj:`typing.Optional[base.Boolean]`
:param can_invite_users: Pass True, if the administrator can invite new users to the chat
:type can_invite_users: :obj:`typing.Union[base.Boolean, None]`
:type can_invite_users: :obj:`typing.Optional[base.Boolean]`
:param can_restrict_members: Pass True, if the administrator can restrict, ban or unban chat members
:type can_restrict_members: :obj:`typing.Union[base.Boolean, None]`
:type can_restrict_members: :obj:`typing.Optional[base.Boolean]`
:param can_pin_messages: Pass True, if the administrator can pin messages, supergroups only
:type can_pin_messages: :obj:`typing.Union[base.Boolean, None]`
:type can_pin_messages: :obj:`typing.Optional[base.Boolean]`
:param can_promote_members: Pass True, if the administrator can add new administrators
with a subset of his own privileges or demote administrators that he has promoted,
directly or indirectly (promoted by administrators that were appointed by him)
:type can_promote_members: :obj:`typing.Union[base.Boolean, None]`
:type can_promote_members: :obj:`typing.Optional[base.Boolean]`
:return: Returns True on success.
:rtype: :obj:`base.Boolean`
"""
@ -338,36 +357,73 @@ class Chat(base.TelegramObject):
:param custom_title: New custom title for the administrator; 0-16 characters, emoji are not allowed
:return: True on success.
"""
return await self.bot.set_chat_administrator_custom_title(chat_id=self.id, user_id=user_id, custom_title=custom_title)
return await self.bot.set_chat_administrator_custom_title(chat_id=self.id, user_id=user_id,
custom_title=custom_title)
async def pin_message(self, message_id: base.Integer, disable_notification: base.Boolean = False) -> base.Boolean:
async def pin_message(self,
message_id: base.Integer,
disable_notification: typing.Optional[base.Boolean] = False,
) -> base.Boolean:
"""
Use this method to pin a message in a supergroup.
The bot must be an administrator in the chat for this to work and must have the appropriate admin rights.
Use this method to add a message to the list of pinned messages in a chat.
If the chat is not a private chat, the bot must be an administrator in the
chat for this to work and must have the 'can_pin_messages' admin right in a
supergroup or 'can_edit_messages' admin right in a channel. Returns True on
success.
Source: https://core.telegram.org/bots/api#pinchatmessage
:param message_id: Identifier of a message to pin
:type message_id: :obj:`base.Integer`
:param disable_notification: Pass True, if it is not necessary to send a notification to
all group members about the new pinned message
:type disable_notification: :obj:`typing.Union[base.Boolean, None]`
:return: Returns True on success.
:param disable_notification: Pass True, if it is not necessary to send a
notification to all group members about the new pinned message
:type disable_notification: :obj:`typing.Optional[base.Boolean]`
:return: Returns True on success
:rtype: :obj:`base.Boolean`
"""
return await self.bot.pin_chat_message(self.id, message_id, disable_notification)
async def unpin_message(self) -> base.Boolean:
async def unpin_message(self,
message_id: typing.Optional[base.Integer] = None,
) -> base.Boolean:
"""
Use this method to unpin a message in a supergroup chat.
The bot must be an administrator in the chat for this to work and must have the appropriate admin rights.
Use this method to remove a message from the list of pinned messages in a
chat. If the chat is not a private chat, the bot must be an administrator in
the chat for this to work and must have the 'can_pin_messages' admin right in
a supergroup or 'can_edit_messages' admin right in a channel. Returns True on
success.
Source: https://core.telegram.org/bots/api#unpinchatmessage
:return: Returns True on success.
:param message_id: Identifier of a message to unpin. If not specified, the
most recent pinned message (by sending date) will be unpinned.
:type message_id: :obj:`typing.Optional[base.Integer]`
:return: Returns True on success
:rtype: :obj:`base.Boolean`
"""
return await self.bot.unpin_chat_message(self.id)
return await self.bot.unpin_chat_message(
chat_id=self.id,
message_id=message_id,
)
async def unpin_all_messages(self):
"""
Use this method to clear the list of pinned messages in a chat. If the chat
is not a private chat, the bot must be an administrator in the chat for this
to work and must have the 'can_pin_messages' admin right in a supergroup or
'can_edit_messages' admin right in a channel. Returns True on success.
Source: https://core.telegram.org/bots/api#unpinallchatmessages
:return: Returns True on success
:rtype: :obj:`base.Boolean`
"""
return await self.bot.unpin_all_chat_messages(
chat_id=self.id,
)
async def leave(self) -> base.Boolean:
"""
@ -494,6 +550,7 @@ class ChatType(helper.Helper):
:key: PRIVATE
:key: GROUP
:key: SUPER_GROUP
:key: SUPERGROUP
:key: CHANNEL
"""
@ -501,9 +558,14 @@ class ChatType(helper.Helper):
PRIVATE = helper.Item() # private
GROUP = helper.Item() # group
SUPER_GROUP = helper.Item() # supergroup
SUPERGROUP = helper.Item() # supergroup
CHANNEL = helper.Item() # channel
SUPER_GROUP: DeprecatedReadOnlyClassVar[ChatType, helper.Item] \
= DeprecatedReadOnlyClassVar(
"SUPER_GROUP chat type is deprecated, use SUPERGROUP instead.",
new_value_getter=lambda cls: cls.SUPERGROUP)
@staticmethod
def _check(obj, chat_types) -> bool:
if hasattr(obj, 'chat'):
@ -543,7 +605,7 @@ class ChatType(helper.Helper):
:param obj:
:return:
"""
return cls._check(obj, [cls.SUPER_GROUP])
return cls._check(obj, [cls.SUPER_GROUP, cls.SUPERGROUP])
@classmethod
@deprecated("This filter was moved to ChatTypeFilter, and will be removed in aiogram v3.0")
@ -554,7 +616,7 @@ class ChatType(helper.Helper):
:param obj:
:return:
"""
return cls._check(obj, [cls.GROUP, cls.SUPER_GROUP])
return cls._check(obj, [cls.GROUP, cls.SUPER_GROUP, cls.SUPERGROUP])
@classmethod
@deprecated("This filter was moved to ChatTypeFilter, and will be removed in aiogram v3.0")

View file

@ -0,0 +1,16 @@
from . import base
from . import fields
from .location import Location
class ChatLocation(base.TelegramObject):
"""
Represents a location to which a chat is connected.
https://core.telegram.org/bots/api#chatlocation
"""
location: Location = fields.Field()
address: base.String = fields.Field()
def __init__(self, location: Location, address: base.String):
super().__init__(location=location, address=address)

View file

@ -1,6 +1,4 @@
import datetime
import warnings
from typing import Optional
from . import base
from . import fields
@ -17,6 +15,7 @@ class ChatMember(base.TelegramObject):
user: User = fields.Field(base=User)
status: base.String = fields.Field()
custom_title: base.String = fields.Field()
is_anonymous: base.Boolean = fields.Field()
until_date: datetime.datetime = fields.DateTimeField()
can_be_edited: base.Boolean = fields.Field()
can_change_info: base.Boolean = fields.Field()

View file

@ -17,3 +17,5 @@ class DiceEmoji:
DICE = '🎲'
DART = '🎯'
BASKETBALL = '🏀'
FOOTBALL = ''
SLOT_MACHINE = '🎰'

View file

@ -23,11 +23,11 @@ class InlineQuery(base.TelegramObject):
async def answer(self,
results: typing.List[InlineQueryResult],
cache_time: typing.Union[base.Integer, None] = None,
is_personal: typing.Union[base.Boolean, None] = None,
next_offset: typing.Union[base.String, None] = None,
switch_pm_text: typing.Union[base.String, None] = None,
switch_pm_parameter: typing.Union[base.String, None] = None):
cache_time: typing.Optional[base.Integer] = None,
is_personal: typing.Optional[base.Boolean] = None,
next_offset: typing.Optional[base.String] = None,
switch_pm_text: typing.Optional[base.String] = None,
switch_pm_parameter: typing.Optional[base.String] = None):
"""
Use this method to send answers to an inline query.
No more than 50 results per query are allowed.
@ -38,22 +38,22 @@ class InlineQuery(base.TelegramObject):
:type results: :obj:`typing.List[types.InlineQueryResult]`
:param cache_time: The maximum amount of time in seconds that the result of the
inline query may be cached on the server. Defaults to 300.
:type cache_time: :obj:`typing.Union[base.Integer, None]`
:type cache_time: :obj:`typing.Optional[base.Integer]`
:param is_personal: Pass True, if results may be cached on the server side only
for the user that sent the query. By default, results may be returned to any user who sends the same query
:type is_personal: :obj:`typing.Union[base.Boolean, None]`
:type is_personal: :obj:`typing.Optional[base.Boolean]`
:param next_offset: Pass the offset that a client should send in the
next query with the same text to receive more results.
Pass an empty string if there are no more results or if you dont support pagination.
Offset length cant exceed 64 bytes.
:type next_offset: :obj:`typing.Union[base.String, None]`
:type next_offset: :obj:`typing.Optional[base.String]`
:param switch_pm_text: If passed, clients will display a button with specified text that
switches the user to a private chat with the bot and sends the bot a start message
with the parameter switch_pm_parameter
:type switch_pm_text: :obj:`typing.Union[base.String, None]`
:type switch_pm_text: :obj:`typing.Optional[base.String]`
:param switch_pm_parameter: Deep-linking parameter for the /start message sent to the bot when
user presses the switch button. 1-64 characters, only A-Z, a-z, 0-9, _ and - are allowed.
:type switch_pm_parameter: :obj:`typing.Union[base.String, None]`
:type switch_pm_parameter: :obj:`typing.Optional[base.String]`
:return: On success, True is returned
:rtype: :obj:`base.Boolean`
"""

View file

@ -4,6 +4,7 @@ from . import base
from . import fields
from .inline_keyboard import InlineKeyboardMarkup
from .input_message_content import InputMessageContent
from .message_entity import MessageEntity
class InlineQueryResult(base.TelegramObject):
@ -83,23 +84,29 @@ class InlineQueryResultPhoto(InlineQueryResult):
caption: base.String = fields.Field()
input_message_content: InputMessageContent = fields.Field(base=InputMessageContent)
def __init__(self, *,
id: base.String,
photo_url: base.String,
thumb_url: base.String,
photo_width: typing.Optional[base.Integer] = None,
photo_height: typing.Optional[base.Integer] = None,
title: typing.Optional[base.String] = None,
description: typing.Optional[base.String] = None,
caption: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
input_message_content: typing.Optional[InputMessageContent] = None):
super(InlineQueryResultPhoto, self).__init__(id=id, photo_url=photo_url, thumb_url=thumb_url,
photo_width=photo_width, photo_height=photo_height, title=title,
description=description, caption=caption,
parse_mode=parse_mode, reply_markup=reply_markup,
input_message_content=input_message_content)
def __init__(
self,
*,
id: base.String,
photo_url: base.String,
thumb_url: base.String,
photo_width: typing.Optional[base.Integer] = None,
photo_height: typing.Optional[base.Integer] = None,
title: typing.Optional[base.String] = None,
description: typing.Optional[base.String] = None,
caption: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
caption_entities: typing.Optional[typing.List[MessageEntity]] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
input_message_content: typing.Optional[InputMessageContent] = None,
):
super().__init__(
id=id, photo_url=photo_url, thumb_url=thumb_url,
photo_width=photo_width, photo_height=photo_height, title=title,
description=description, caption=caption,
parse_mode=parse_mode, caption_entities=caption_entities,
reply_markup=reply_markup, input_message_content=input_message_content,
)
class InlineQueryResultGif(InlineQueryResult):
@ -123,23 +130,29 @@ class InlineQueryResultGif(InlineQueryResult):
caption: base.String = fields.Field()
input_message_content: InputMessageContent = fields.Field(base=InputMessageContent)
def __init__(self, *,
id: base.String,
gif_url: base.String,
gif_width: typing.Optional[base.Integer] = None,
gif_height: typing.Optional[base.Integer] = None,
gif_duration: typing.Optional[base.Integer] = None,
thumb_url: typing.Optional[base.String] = None,
title: typing.Optional[base.String] = None,
caption: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
input_message_content: typing.Optional[InputMessageContent] = None):
super(InlineQueryResultGif, self).__init__(id=id, gif_url=gif_url, gif_width=gif_width,
gif_height=gif_height, gif_duration=gif_duration,
thumb_url=thumb_url, title=title, caption=caption,
parse_mode=parse_mode, reply_markup=reply_markup,
input_message_content=input_message_content)
def __init__(
self,
*,
id: base.String,
gif_url: base.String,
gif_width: typing.Optional[base.Integer] = None,
gif_height: typing.Optional[base.Integer] = None,
gif_duration: typing.Optional[base.Integer] = None,
thumb_url: typing.Optional[base.String] = None,
title: typing.Optional[base.String] = None,
caption: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
caption_entities: typing.Optional[typing.List[MessageEntity]] = None,
input_message_content: typing.Optional[InputMessageContent] = None,
):
super().__init__(
id=id, gif_url=gif_url, gif_width=gif_width, gif_height=gif_height,
gif_duration=gif_duration, thumb_url=thumb_url, title=title,
caption=caption, parse_mode=parse_mode, reply_markup=reply_markup,
caption_entities=caption_entities,
input_message_content=input_message_content,
)
class InlineQueryResultMpeg4Gif(InlineQueryResult):
@ -163,23 +176,30 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
caption: base.String = fields.Field()
input_message_content: InputMessageContent = fields.Field(base=InputMessageContent)
def __init__(self, *,
id: base.String,
mpeg4_url: base.String,
thumb_url: base.String,
mpeg4_width: typing.Optional[base.Integer] = None,
mpeg4_height: typing.Optional[base.Integer] = None,
mpeg4_duration: typing.Optional[base.Integer] = None,
title: typing.Optional[base.String] = None,
caption: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
input_message_content: typing.Optional[InputMessageContent] = None):
super(InlineQueryResultMpeg4Gif, self).__init__(id=id, mpeg4_url=mpeg4_url, mpeg4_width=mpeg4_width,
mpeg4_height=mpeg4_height, mpeg4_duration=mpeg4_duration,
thumb_url=thumb_url, title=title, caption=caption,
parse_mode=parse_mode, reply_markup=reply_markup,
input_message_content=input_message_content)
def __init__(
self,
*,
id: base.String,
mpeg4_url: base.String,
thumb_url: base.String,
mpeg4_width: typing.Optional[base.Integer] = None,
mpeg4_height: typing.Optional[base.Integer] = None,
mpeg4_duration: typing.Optional[base.Integer] = None,
title: typing.Optional[base.String] = None,
caption: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
caption_entities: typing.Optional[typing.List[MessageEntity]] = None,
input_message_content: typing.Optional[InputMessageContent] = None,
):
super().__init__(
id=id, mpeg4_url=mpeg4_url, mpeg4_width=mpeg4_width,
mpeg4_height=mpeg4_height, mpeg4_duration=mpeg4_duration,
thumb_url=thumb_url, title=title, caption=caption,
parse_mode=parse_mode, reply_markup=reply_markup,
caption_entities=caption_entities,
input_message_content=input_message_content,
)
class InlineQueryResultVideo(InlineQueryResult):
@ -207,26 +227,32 @@ class InlineQueryResultVideo(InlineQueryResult):
description: base.String = fields.Field()
input_message_content: InputMessageContent = fields.Field(base=InputMessageContent)
def __init__(self, *,
id: base.String,
video_url: base.String,
mime_type: base.String,
thumb_url: base.String,
title: base.String,
caption: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
video_width: typing.Optional[base.Integer] = None,
video_height: typing.Optional[base.Integer] = None,
video_duration: typing.Optional[base.Integer] = None,
description: typing.Optional[base.String] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
input_message_content: typing.Optional[InputMessageContent] = None):
super(InlineQueryResultVideo, self).__init__(id=id, video_url=video_url, mime_type=mime_type,
thumb_url=thumb_url, title=title, caption=caption,
video_width=video_width, video_height=video_height,
video_duration=video_duration, description=description,
parse_mode=parse_mode, reply_markup=reply_markup,
input_message_content=input_message_content)
def __init__(
self,
*,
id: base.String,
video_url: base.String,
mime_type: base.String,
thumb_url: base.String,
title: base.String,
caption: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
video_width: typing.Optional[base.Integer] = None,
video_height: typing.Optional[base.Integer] = None,
video_duration: typing.Optional[base.Integer] = None,
description: typing.Optional[base.String] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
caption_entities: typing.Optional[typing.List[MessageEntity]] = None,
input_message_content: typing.Optional[InputMessageContent] = None,
):
super().__init__(
id=id, video_url=video_url, mime_type=mime_type, thumb_url=thumb_url,
title=title, caption=caption, video_width=video_width,
video_height=video_height, video_duration=video_duration,
description=description, parse_mode=parse_mode,
reply_markup=reply_markup, caption_entities=caption_entities,
input_message_content=input_message_content,
)
class InlineQueryResultAudio(InlineQueryResult):
@ -248,21 +274,27 @@ class InlineQueryResultAudio(InlineQueryResult):
audio_duration: base.Integer = fields.Field()
input_message_content: InputMessageContent = fields.Field(base=InputMessageContent)
def __init__(self, *,
id: base.String,
audio_url: base.String,
title: base.String,
caption: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
performer: typing.Optional[base.String] = None,
audio_duration: typing.Optional[base.Integer] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
input_message_content: typing.Optional[InputMessageContent] = None):
super(InlineQueryResultAudio, self).__init__(id=id, audio_url=audio_url, title=title,
caption=caption, parse_mode=parse_mode,
performer=performer, audio_duration=audio_duration,
reply_markup=reply_markup,
input_message_content=input_message_content)
def __init__(
self,
*,
id: base.String,
audio_url: base.String,
title: base.String,
caption: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
performer: typing.Optional[base.String] = None,
audio_duration: typing.Optional[base.Integer] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
caption_entities: typing.Optional[typing.List[MessageEntity]] = None,
input_message_content: typing.Optional[InputMessageContent] = None,
):
super().__init__(
id=id, audio_url=audio_url, title=title,
caption=caption, parse_mode=parse_mode,
performer=performer, audio_duration=audio_duration,
reply_markup=reply_markup, caption_entities=caption_entities,
input_message_content=input_message_content,
)
class InlineQueryResultVoice(InlineQueryResult):
@ -285,19 +317,25 @@ class InlineQueryResultVoice(InlineQueryResult):
voice_duration: base.Integer = fields.Field()
input_message_content: InputMessageContent = fields.Field(base=InputMessageContent)
def __init__(self, *,
id: base.String,
voice_url: base.String,
title: base.String,
caption: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
voice_duration: typing.Optional[base.Integer] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
input_message_content: typing.Optional[InputMessageContent] = None):
super(InlineQueryResultVoice, self).__init__(id=id, voice_url=voice_url, title=title,
caption=caption, voice_duration=voice_duration,
parse_mode=parse_mode, reply_markup=reply_markup,
input_message_content=input_message_content)
def __init__(
self,
*,
id: base.String,
voice_url: base.String,
title: base.String,
caption: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
voice_duration: typing.Optional[base.Integer] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
caption_entities: typing.Optional[typing.List[MessageEntity]] = None,
input_message_content: typing.Optional[InputMessageContent] = None,
):
super().__init__(
id=id, voice_url=voice_url, title=title, caption=caption,
voice_duration=voice_duration, parse_mode=parse_mode,
reply_markup=reply_markup, caption_entities=caption_entities,
input_message_content=input_message_content,
)
class InlineQueryResultDocument(InlineQueryResult):
@ -323,25 +361,31 @@ class InlineQueryResultDocument(InlineQueryResult):
thumb_width: base.Integer = fields.Field()
thumb_height: base.Integer = fields.Field()
def __init__(self, *,
id: base.String,
title: base.String,
caption: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
document_url: typing.Optional[base.String] = None,
mime_type: typing.Optional[base.String] = None,
description: typing.Optional[base.String] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
input_message_content: typing.Optional[InputMessageContent] = None,
thumb_url: typing.Optional[base.String] = None,
thumb_width: typing.Optional[base.Integer] = None,
thumb_height: typing.Optional[base.Integer] = None):
super(InlineQueryResultDocument, self).__init__(id=id, title=title, caption=caption,
document_url=document_url, mime_type=mime_type,
description=description, reply_markup=reply_markup,
input_message_content=input_message_content,
thumb_url=thumb_url, thumb_width=thumb_width,
thumb_height=thumb_height, parse_mode=parse_mode)
def __init__(
self,
*,
id: base.String,
title: base.String,
caption: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
caption_entities: typing.Optional[typing.List[MessageEntity]] = None,
document_url: typing.Optional[base.String] = None,
mime_type: typing.Optional[base.String] = None,
description: typing.Optional[base.String] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
input_message_content: typing.Optional[InputMessageContent] = None,
thumb_url: typing.Optional[base.String] = None,
thumb_width: typing.Optional[base.Integer] = None,
thumb_height: typing.Optional[base.Integer] = None,
):
super().__init__(
id=id, title=title, caption=caption, parse_mode=parse_mode,
caption_entities=caption_entities, document_url=document_url,
mime_type=mime_type, description=description, reply_markup=reply_markup,
input_message_content=input_message_content,
thumb_url=thumb_url, thumb_width=thumb_width,
thumb_height=thumb_height,
)
class InlineQueryResultLocation(InlineQueryResult):
@ -352,16 +396,16 @@ class InlineQueryResultLocation(InlineQueryResult):
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.
https://core.telegram.org/bots/api#inlinequeryresultlocation
"""
type: base.String = fields.Field(alias='type', default='location')
latitude: base.Float = fields.Field()
longitude: base.Float = fields.Field()
title: base.String = fields.Field()
horizontal_accuracy: typing.Optional[base.Float] = fields.Field()
live_period: base.Integer = fields.Field()
heading: typing.Optional[base.Integer] = fields.Field()
proximity_alert_radius: typing.Optional[base.Integer] = fields.Field()
input_message_content: InputMessageContent = fields.Field(base=InputMessageContent)
thumb_url: base.String = fields.Field()
thumb_width: base.Integer = fields.Field()
@ -372,18 +416,31 @@ class InlineQueryResultLocation(InlineQueryResult):
latitude: base.Float,
longitude: base.Float,
title: base.String,
horizontal_accuracy: typing.Optional[base.Float] = None,
live_period: typing.Optional[base.Integer] = None,
heading: typing.Optional[base.Integer] = None,
proximity_alert_radius: typing.Optional[base.Integer] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
input_message_content: typing.Optional[InputMessageContent] = None,
thumb_url: typing.Optional[base.String] = None,
thumb_width: typing.Optional[base.Integer] = None,
thumb_height: typing.Optional[base.Integer] = None):
super(InlineQueryResultLocation, self).__init__(id=id, latitude=latitude, longitude=longitude,
title=title, live_period=live_period,
reply_markup=reply_markup,
input_message_content=input_message_content,
thumb_url=thumb_url, thumb_width=thumb_width,
thumb_height=thumb_height)
thumb_height: typing.Optional[base.Integer] = None,
):
super().__init__(
id=id,
latitude=latitude,
longitude=longitude,
title=title,
horizontal_accuracy=horizontal_accuracy,
live_period=live_period,
heading=heading,
proximity_alert_radius=proximity_alert_radius,
reply_markup=reply_markup,
input_message_content=input_message_content,
thumb_url=thumb_url,
thumb_width=thumb_width,
thumb_height=thumb_height
)
class InlineQueryResultVenue(InlineQueryResult):
@ -404,31 +461,40 @@ class InlineQueryResultVenue(InlineQueryResult):
title: base.String = fields.Field()
address: base.String = fields.Field()
foursquare_id: base.String = fields.Field()
foursquare_type: base.String = fields.Field()
google_place_id: base.String = fields.Field()
google_place_type: base.String = fields.Field()
input_message_content: InputMessageContent = fields.Field(base=InputMessageContent)
thumb_url: base.String = fields.Field()
thumb_width: base.Integer = fields.Field()
thumb_height: base.Integer = fields.Field()
foursquare_type: base.String = fields.Field()
def __init__(self, *,
id: base.String,
latitude: base.Float,
longitude: base.Float,
title: base.String,
address: base.String,
foursquare_id: typing.Optional[base.String] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
input_message_content: typing.Optional[InputMessageContent] = None,
thumb_url: typing.Optional[base.String] = None,
thumb_width: typing.Optional[base.Integer] = None,
thumb_height: typing.Optional[base.Integer] = None,
foursquare_type: typing.Optional[base.String] = None):
super(InlineQueryResultVenue, self).__init__(id=id, latitude=latitude, longitude=longitude,
title=title, address=address, foursquare_id=foursquare_id,
reply_markup=reply_markup,
input_message_content=input_message_content, thumb_url=thumb_url,
thumb_width=thumb_width, thumb_height=thumb_height,
foursquare_type=foursquare_type)
def __init__(
self,
*,
id: base.String,
latitude: base.Float,
longitude: base.Float,
title: base.String,
address: base.String,
foursquare_id: typing.Optional[base.String] = None,
foursquare_type: typing.Optional[base.String] = None,
google_place_id: typing.Optional[base.String] = None,
google_place_type: typing.Optional[base.String] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
input_message_content: typing.Optional[InputMessageContent] = None,
thumb_url: typing.Optional[base.String] = None,
thumb_width: typing.Optional[base.Integer] = None,
thumb_height: typing.Optional[base.Integer] = None,
):
super().__init__(
id=id, latitude=latitude, longitude=longitude, title=title,
address=address, foursquare_id=foursquare_id,
foursquare_type=foursquare_type, google_place_id=google_place_id,
google_place_type=google_place_type, reply_markup=reply_markup,
input_message_content=input_message_content, thumb_url=thumb_url,
thumb_width=thumb_width, thumb_height=thumb_height,
)
class InlineQueryResultContact(InlineQueryResult):
@ -510,19 +576,24 @@ class InlineQueryResultCachedPhoto(InlineQueryResult):
caption: base.String = fields.Field()
input_message_content: InputMessageContent = fields.Field(base=InputMessageContent)
def __init__(self, *,
id: base.String,
photo_file_id: base.String,
title: typing.Optional[base.String] = None,
description: typing.Optional[base.String] = None,
caption: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
input_message_content: typing.Optional[InputMessageContent] = None):
super(InlineQueryResultCachedPhoto, self).__init__(id=id, photo_file_id=photo_file_id, title=title,
description=description, caption=caption,
parse_mode=parse_mode, reply_markup=reply_markup,
input_message_content=input_message_content)
def __init__(
self,
*,
id: base.String,
photo_file_id: base.String,
title: typing.Optional[base.String] = None,
description: typing.Optional[base.String] = None,
caption: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
caption_entities: typing.Optional[typing.List[MessageEntity]] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
input_message_content: typing.Optional[InputMessageContent] = None,
):
super().__init__(
id=id, photo_file_id=photo_file_id, title=title, description=description,
caption=caption, parse_mode=parse_mode, caption_entities=caption_entities,
reply_markup=reply_markup, input_message_content=input_message_content,
)
class InlineQueryResultCachedGif(InlineQueryResult):
@ -541,18 +612,23 @@ class InlineQueryResultCachedGif(InlineQueryResult):
caption: base.String = fields.Field()
input_message_content: InputMessageContent = fields.Field(base=InputMessageContent)
def __init__(self, *,
id: base.String,
gif_file_id: base.String,
title: typing.Optional[base.String] = None,
caption: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
input_message_content: typing.Optional[InputMessageContent] = None):
super(InlineQueryResultCachedGif, self).__init__(id=id, gif_file_id=gif_file_id,
title=title, caption=caption,
parse_mode=parse_mode, reply_markup=reply_markup,
input_message_content=input_message_content)
def __init__(
self,
*,
id: base.String,
gif_file_id: base.String,
title: typing.Optional[base.String] = None,
caption: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
caption_entities: typing.Optional[typing.List[MessageEntity]] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
input_message_content: typing.Optional[InputMessageContent] = None,
):
super().__init__(
id=id, gif_file_id=gif_file_id, title=title, caption=caption,
parse_mode=parse_mode, caption_entities=caption_entities,
reply_markup=reply_markup, input_message_content=input_message_content,
)
class InlineQueryResultCachedMpeg4Gif(InlineQueryResult):
@ -571,18 +647,23 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult):
caption: base.String = fields.Field()
input_message_content: InputMessageContent = fields.Field(base=InputMessageContent)
def __init__(self, *,
id: base.String,
mpeg4_file_id: base.String,
title: typing.Optional[base.String] = None,
caption: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
input_message_content: typing.Optional[InputMessageContent] = None):
super(InlineQueryResultCachedMpeg4Gif, self).__init__(id=id, mpeg4_file_id=mpeg4_file_id,
title=title, caption=caption,
parse_mode=parse_mode, reply_markup=reply_markup,
input_message_content=input_message_content)
def __init__(
self,
*,
id: base.String,
mpeg4_file_id: base.String,
title: typing.Optional[base.String] = None,
caption: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
caption_entities: typing.Optional[typing.List[MessageEntity]] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
input_message_content: typing.Optional[InputMessageContent] = None,
):
super().__init__(
id=id, mpeg4_file_id=mpeg4_file_id, title=title, caption=caption,
parse_mode=parse_mode, caption_entities=caption_entities,
reply_markup=reply_markup, input_message_content=input_message_content,
)
class InlineQueryResultCachedSticker(InlineQueryResult):
@ -631,20 +712,25 @@ class InlineQueryResultCachedDocument(InlineQueryResult):
caption: base.String = fields.Field()
input_message_content: InputMessageContent = fields.Field(base=InputMessageContent)
def __init__(self, *,
id: base.String,
title: base.String,
document_file_id: base.String,
description: typing.Optional[base.String] = None,
caption: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
input_message_content: typing.Optional[InputMessageContent] = None):
super(InlineQueryResultCachedDocument, self).__init__(id=id, title=title,
document_file_id=document_file_id,
description=description, caption=caption,
parse_mode=parse_mode, reply_markup=reply_markup,
input_message_content=input_message_content)
def __init__(
self,
*,
id: base.String,
title: base.String,
document_file_id: base.String,
description: typing.Optional[base.String] = None,
caption: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
caption_entities: typing.Optional[typing.List[MessageEntity]] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
input_message_content: typing.Optional[InputMessageContent] = None,
):
super().__init__(
id=id, title=title, document_file_id=document_file_id,
description=description, caption=caption, parse_mode=parse_mode,
caption_entities=caption_entities, reply_markup=reply_markup,
input_message_content=input_message_content,
)
class InlineQueryResultCachedVideo(InlineQueryResult):
@ -664,19 +750,24 @@ class InlineQueryResultCachedVideo(InlineQueryResult):
caption: base.String = fields.Field()
input_message_content: InputMessageContent = fields.Field(base=InputMessageContent)
def __init__(self, *,
id: base.String,
video_file_id: base.String,
title: base.String,
description: typing.Optional[base.String] = None,
caption: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
input_message_content: typing.Optional[InputMessageContent] = None):
super(InlineQueryResultCachedVideo, self).__init__(id=id, video_file_id=video_file_id, title=title,
description=description, caption=caption,
parse_mode=parse_mode, reply_markup=reply_markup,
input_message_content=input_message_content)
def __init__(
self,
*,
id: base.String,
video_file_id: base.String,
title: base.String,
description: typing.Optional[base.String] = None,
caption: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
caption_entities: typing.Optional[typing.List[MessageEntity]] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
input_message_content: typing.Optional[InputMessageContent] = None,
):
super().__init__(
id=id, video_file_id=video_file_id, title=title, description=description,
caption=caption, parse_mode=parse_mode, caption_entities=caption_entities,
reply_markup=reply_markup, input_message_content=input_message_content,
)
class InlineQueryResultCachedVoice(InlineQueryResult):
@ -697,18 +788,23 @@ class InlineQueryResultCachedVoice(InlineQueryResult):
caption: base.String = fields.Field()
input_message_content: InputMessageContent = fields.Field(base=InputMessageContent)
def __init__(self, *,
id: base.String,
voice_file_id: base.String,
title: base.String,
caption: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
input_message_content: typing.Optional[InputMessageContent] = None):
super(InlineQueryResultCachedVoice, self).__init__(id=id, voice_file_id=voice_file_id,
title=title, caption=caption,
parse_mode=parse_mode, reply_markup=reply_markup,
input_message_content=input_message_content)
def __init__(
self,
*,
id: base.String,
voice_file_id: base.String,
title: base.String,
caption: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
caption_entities: typing.Optional[typing.List[MessageEntity]] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
input_message_content: typing.Optional[InputMessageContent] = None,
):
super().__init__(
id=id, voice_file_id=voice_file_id, title=title, caption=caption,
parse_mode=parse_mode, caption_entities=caption_entities,
reply_markup=reply_markup, input_message_content=input_message_content,
)
class InlineQueryResultCachedAudio(InlineQueryResult):
@ -729,14 +825,19 @@ class InlineQueryResultCachedAudio(InlineQueryResult):
caption: base.String = fields.Field()
input_message_content: InputMessageContent = fields.Field(base=InputMessageContent)
def __init__(self, *,
id: base.String,
audio_file_id: base.String,
caption: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
input_message_content: typing.Optional[InputMessageContent] = None):
super(InlineQueryResultCachedAudio, self).__init__(id=id, audio_file_id=audio_file_id,
caption=caption, parse_mode=parse_mode,
reply_markup=reply_markup,
input_message_content=input_message_content)
def __init__(
self,
*,
id: base.String,
audio_file_id: base.String,
caption: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
caption_entities: typing.Optional[typing.List[MessageEntity]] = None,
reply_markup: typing.Optional[InlineKeyboardMarkup] = None,
input_message_content: typing.Optional[InputMessageContent] = None,
):
super().__init__(
id=id, audio_file_id=audio_file_id, caption=caption,
parse_mode=parse_mode, caption_entities=caption_entities,
reply_markup=reply_markup, input_message_content=input_message_content,
)

View file

@ -4,6 +4,8 @@ import io
import logging
import os
import secrets
from pathlib import Path
from typing import Union
import aiohttp
@ -25,7 +27,7 @@ class InputFile(base.TelegramObject):
https://core.telegram.org/bots/api#inputfile
"""
def __init__(self, path_or_bytesio, filename=None, conf=None):
def __init__(self, path_or_bytesio: Union[str, io.IOBase, Path], filename=None, conf=None):
"""
:param path_or_bytesio:
@ -45,6 +47,12 @@ class InputFile(base.TelegramObject):
elif isinstance(path_or_bytesio, _WebPipe):
self._path = None
self._file = path_or_bytesio
elif isinstance(path_or_bytesio, Path):
self._file = path_or_bytesio.open("rb")
self._path = path_or_bytesio.resolve()
if filename is None:
filename = path_or_bytesio.name
else:
raise TypeError('Not supported file type.')

View file

@ -5,6 +5,7 @@ import typing
from . import base
from . import fields
from .input_file import InputFile
from .message_entity import MessageEntity
ATTACHMENT_PREFIX = 'attach://'
@ -106,28 +107,48 @@ class InputMediaAnimation(InputMedia):
height: base.Integer = fields.Field()
duration: base.Integer = fields.Field()
def __init__(self, media: base.InputFile,
thumb: typing.Union[base.InputFile, base.String] = None,
caption: base.String = None,
width: base.Integer = None, height: base.Integer = None, duration: base.Integer = None,
parse_mode: base.String = None, **kwargs):
super(InputMediaAnimation, self).__init__(type='animation', media=media, thumb=thumb, caption=caption,
width=width, height=height, duration=duration,
parse_mode=parse_mode, conf=kwargs)
def __init__(
self,
media: base.InputFile,
thumb: typing.Union[base.InputFile, base.String] = None,
caption: base.String = None,
width: base.Integer = None,
height: base.Integer = None,
duration: base.Integer = None,
parse_mode: base.String = None,
caption_entities: typing.Optional[typing.List[MessageEntity]] = None,
**kwargs,
):
super().__init__(
type='animation', media=media, thumb=thumb, caption=caption, width=width,
height=height, duration=duration, parse_mode=parse_mode,
caption_entities=caption_entities, conf=kwargs,
)
class InputMediaDocument(InputMedia):
"""
Represents a photo to be sent.
Represents a general file to be sent.
https://core.telegram.org/bots/api#inputmediadocument
"""
def __init__(self, media: base.InputFile, thumb: typing.Union[base.InputFile, base.String] = None,
caption: base.String = None, parse_mode: base.String = None, **kwargs):
super(InputMediaDocument, self).__init__(type='document', media=media, thumb=thumb,
caption=caption, parse_mode=parse_mode,
conf=kwargs)
def __init__(
self,
media: base.InputFile,
thumb: typing.Union[base.InputFile, base.String, None] = None,
caption: base.String = None,
parse_mode: base.String = None,
caption_entities: typing.Optional[typing.List[MessageEntity]] = None,
disable_content_type_detection: typing.Optional[base.Boolean] = None,
**kwargs,
):
super().__init__(
type='document', media=media, thumb=thumb, caption=caption,
parse_mode=parse_mode, caption_entities=caption_entities,
disable_content_type_detection=disable_content_type_detection,
conf=kwargs,
)
class InputMediaAudio(InputMedia):
@ -141,17 +162,23 @@ class InputMediaAudio(InputMedia):
performer: base.String = fields.Field()
title: base.String = fields.Field()
def __init__(self, media: base.InputFile,
thumb: typing.Union[base.InputFile, base.String] = None,
caption: base.String = None,
duration: base.Integer = None,
performer: base.String = None,
title: base.String = None,
parse_mode: base.String = None, **kwargs):
super(InputMediaAudio, self).__init__(type='audio', media=media, thumb=thumb,
caption=caption, duration=duration,
performer=performer, title=title,
parse_mode=parse_mode, conf=kwargs)
def __init__(
self,
media: base.InputFile,
thumb: typing.Union[base.InputFile, base.String] = None,
caption: base.String = None,
duration: base.Integer = None,
performer: base.String = None,
title: base.String = None,
parse_mode: base.String = None,
caption_entities: typing.Optional[typing.List[MessageEntity]] = None,
**kwargs,
):
super().__init__(
type='audio', media=media, thumb=thumb, caption=caption,
duration=duration, performer=performer, title=title,
parse_mode=parse_mode, caption_entities=caption_entities, conf=kwargs,
)
class InputMediaPhoto(InputMedia):
@ -161,11 +188,18 @@ class InputMediaPhoto(InputMedia):
https://core.telegram.org/bots/api#inputmediaphoto
"""
def __init__(self, media: base.InputFile, thumb: typing.Union[base.InputFile, base.String] = None,
caption: base.String = None, parse_mode: base.String = None, **kwargs):
super(InputMediaPhoto, self).__init__(type='photo', media=media, thumb=thumb,
caption=caption, parse_mode=parse_mode,
conf=kwargs)
def __init__(
self,
media: base.InputFile,
caption: base.String = None,
parse_mode: base.String = None,
caption_entities: typing.Optional[typing.List[MessageEntity]] = None,
**kwargs,
):
super().__init__(
type='photo', media=media, caption=caption, parse_mode=parse_mode,
caption_entities=caption_entities, conf=kwargs,
)
class InputMediaVideo(InputMedia):
@ -179,16 +213,25 @@ class InputMediaVideo(InputMedia):
duration: base.Integer = fields.Field()
supports_streaming: base.Boolean = fields.Field()
def __init__(self, media: base.InputFile,
thumb: typing.Union[base.InputFile, base.String] = None,
caption: base.String = None,
width: base.Integer = None, height: base.Integer = None, duration: base.Integer = None,
parse_mode: base.String = None,
supports_streaming: base.Boolean = None, **kwargs):
super(InputMediaVideo, self).__init__(type='video', media=media, thumb=thumb, caption=caption,
width=width, height=height, duration=duration,
parse_mode=parse_mode,
supports_streaming=supports_streaming, conf=kwargs)
def __init__(
self,
media: base.InputFile,
thumb: typing.Union[base.InputFile, base.String] = None,
caption: base.String = None,
width: base.Integer = None,
height: base.Integer = None,
duration: base.Integer = None,
parse_mode: base.String = None,
caption_entities: typing.Optional[typing.List[MessageEntity]] = None,
supports_streaming: base.Boolean = None,
**kwargs,
):
super().__init__(
type='video', media=media, thumb=thumb, caption=caption,
width=width, height=height, duration=duration,
parse_mode=parse_mode, caption_entities=caption_entities,
supports_streaming=supports_streaming, conf=kwargs
)
class MediaGroup(base.TelegramObject):

View file

@ -2,6 +2,7 @@ import typing
from . import base
from . import fields
from .message_entity import MessageEntity
class InputMessageContent(base.TelegramObject):
@ -40,17 +41,31 @@ class InputLocationMessageContent(InputMessageContent):
"""
Represents the content of a location message to be sent as the result of an inline query.
Note: This will only work in Telegram versions released after 9 April, 2016.
Older clients will ignore them.
https://core.telegram.org/bots/api#inputlocationmessagecontent
"""
latitude: base.Float = fields.Field()
longitude: base.Float = fields.Field()
horizontal_accuracy: typing.Optional[base.Float] = fields.Field()
live_period: typing.Optional[base.Integer] = fields.Field()
heading: typing.Optional[base.Integer] = fields.Field()
proximity_alert_radius: typing.Optional[base.Integer] = fields.Field()
def __init__(self, latitude: base.Float,
longitude: base.Float):
super(InputLocationMessageContent, self).__init__(latitude=latitude, longitude=longitude)
def __init__(self,
latitude: base.Float,
longitude: base.Float,
horizontal_accuracy: typing.Optional[base.Float] = None,
live_period: typing.Optional[base.Integer] = None,
heading: typing.Optional[base.Integer] = None,
proximity_alert_radius: typing.Optional[base.Integer] = None,
):
super().__init__(
latitude=latitude,
longitude=longitude,
horizontal_accuracy=horizontal_accuracy,
live_period=live_period,
heading=heading,
proximity_alert_radius=proximity_alert_radius,
)
class InputTextMessageContent(InputMessageContent):
@ -69,14 +84,21 @@ class InputTextMessageContent(InputMessageContent):
except RuntimeError:
pass
def __init__(self, message_text: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
disable_web_page_preview: typing.Optional[base.Boolean] = None):
def __init__(
self,
message_text: typing.Optional[base.String] = None,
parse_mode: typing.Optional[base.String] = None,
caption_entities: typing.Optional[typing.List[MessageEntity]] = None,
disable_web_page_preview: typing.Optional[base.Boolean] = None,
):
if parse_mode is None:
parse_mode = self.safe_get_parse_mode()
super(InputTextMessageContent, self).__init__(message_text=message_text, parse_mode=parse_mode,
disable_web_page_preview=disable_web_page_preview)
super().__init__(
message_text=message_text, parse_mode=parse_mode,
caption_entities=caption_entities,
disable_web_page_preview=disable_web_page_preview,
)
class InputVenueMessageContent(InputMessageContent):
@ -93,11 +115,24 @@ class InputVenueMessageContent(InputMessageContent):
title: base.String = fields.Field()
address: base.String = fields.Field()
foursquare_id: base.String = fields.Field()
foursquare_type: base.String = fields.Field()
google_place_id: base.String = fields.Field()
google_place_type: base.String = fields.Field()
def __init__(self, latitude: typing.Optional[base.Float] = None,
longitude: typing.Optional[base.Float] = None,
title: typing.Optional[base.String] = None,
address: typing.Optional[base.String] = None,
foursquare_id: typing.Optional[base.String] = None):
super(InputVenueMessageContent, self).__init__(latitude=latitude, longitude=longitude, title=title,
address=address, foursquare_id=foursquare_id)
def __init__(
self,
latitude: typing.Optional[base.Float] = None,
longitude: typing.Optional[base.Float] = None,
title: typing.Optional[base.String] = None,
address: typing.Optional[base.String] = None,
foursquare_id: typing.Optional[base.String] = None,
foursquare_type: typing.Optional[base.String] = None,
google_place_id: typing.Optional[base.String] = None,
google_place_type: typing.Optional[base.String] = None,
):
super().__init__(
latitude=latitude, longitude=longitude, title=title,
address=address, foursquare_id=foursquare_id,
foursquare_type=foursquare_type, google_place_id=google_place_id,
google_place_type=google_place_type,
)

View file

@ -1,3 +1,5 @@
import typing
from . import base
from . import fields
@ -10,3 +12,7 @@ class Location(base.TelegramObject):
"""
longitude: base.Float = fields.Field()
latitude: base.Float = fields.Field()
horizontal_accuracy: typing.Optional[base.Float] = fields.Field()
live_period: typing.Optional[base.Integer] = fields.Field()
heading: typing.Optional[base.Integer] = fields.Field()
proximity_alert_radius: typing.Optional[base.Integer] = fields.Field()

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,10 @@
from . import base, fields
class MessageId(base.TelegramObject):
"""
This object represents a unique message identifier.
https://core.telegram.org/bots/api#messageid
"""
message_id: base.String = fields.Field()

View file

@ -0,0 +1,15 @@
from . import base
from . import fields
from .user import User
class ProximityAlertTriggered(base.TelegramObject):
"""
This object represents the content of a service message, sent whenever a user in
the chat triggers a proximity alert set by another user.
https://core.telegram.org/bots/api#proximityalerttriggered
"""
traveler: User = fields.Field()
watcher: User = fields.Field()
distance: base.Integer = fields.Field()

View file

@ -14,3 +14,5 @@ class Venue(base.TelegramObject):
address: base.String = fields.Field()
foursquare_id: base.String = fields.Field()
foursquare_type: base.String = fields.Field()
google_place_id: base.String = fields.Field()
google_place_type: base.String = fields.Field()

View file

@ -16,5 +16,6 @@ class Video(base.TelegramObject, mixins.Downloadable):
height: base.Integer = fields.Field()
duration: base.Integer = fields.Field()
thumb: PhotoSize = fields.Field(base=PhotoSize)
file_name: base.String = fields.Field()
mime_type: base.String = fields.Field()
file_size: base.Integer = fields.Field()

View file

@ -13,6 +13,7 @@ class WebhookInfo(base.TelegramObject):
url: base.String = fields.Field()
has_custom_certificate: base.Boolean = fields.Field()
pending_update_count: base.Integer = fields.Field()
ip_address: base.String = fields.Field()
last_error_date: base.Integer = fields.Field()
last_error_message: base.String = fields.Field()
max_connections: base.Integer = fields.Field()

View file

@ -12,6 +12,7 @@
- MessageTextIsEmpty
- MessageCantBeEdited
- MessageCantBeDeleted
- MessageCantBeForwarded
- MessageToEditNotFound
- MessageToReplyNotFound
- ToMuchMessages
@ -38,6 +39,7 @@
- URLHostIsEmpty
- StartParamInvalid
- ButtonDataInvalid
- FileIsTooBig
- WrongFileIdentifier
- GroupDeactivated
- BadWebhook
@ -194,7 +196,7 @@ class MessageToReplyNotFound(MessageError):
"""
Will be raised when you try to reply to very old or deleted or unknown message.
"""
match = 'message to reply not found'
match = 'Reply message not found'
class MessageIdentifierNotSpecified(MessageError):
@ -213,6 +215,10 @@ class MessageCantBeDeleted(MessageError):
match = 'message can\'t be deleted'
class MessageCantBeForwarded(MessageError):
match = 'message can\'t be forwarded'
class MessageToEditNotFound(MessageError):
match = 'message to edit not found'
@ -347,6 +353,10 @@ class ButtonDataInvalid(BadRequest):
text = 'Button data invalid'
class FileIsTooBig(BadRequest):
match = 'File is too big'
class WrongFileIdentifier(BadRequest):
match = 'wrong file identifier/HTTP URL specified'
@ -505,7 +515,7 @@ class Unauthorized(TelegramAPIError, _MatchErrorMixin):
class BotKicked(Unauthorized):
match = 'bot was kicked from a chat'
match = 'bot was kicked from'
class BotBlocked(Unauthorized):

View file

@ -354,7 +354,7 @@ class Executor:
self.dispatcher.stop_polling()
await self.dispatcher.storage.close()
await self.dispatcher.storage.wait_closed()
await self.dispatcher.bot.close()
await self.dispatcher.bot.session.close()
async def _startup_polling(self):
await self._welcome()

View file

@ -16,3 +16,4 @@ sphinxcontrib-programoutput>=0.14
aiohttp-socks>=0.3.4
rethinkdb>=2.4.1
coverage==4.5.3
motor>=2.2.0

View file

@ -10,7 +10,7 @@ Filter factory greatly simplifies the reuse of filters when registering handlers
Filters factory
===============
.. autoclass:: aiogram.dispatcher.filters.factory.FiltersFactory
.. autoclass:: aiogram.dispatcher.filters.FiltersFactory
:members:
:show-inheritance:
@ -21,28 +21,28 @@ Builtin filters
Command
-------
.. autoclass:: aiogram.dispatcher.filters.builtin.Command
.. autoclass:: aiogram.dispatcher.filters.Command
:members:
:show-inheritance:
CommandStart
------------
.. autoclass:: aiogram.dispatcher.filters.builtin.CommandStart
.. autoclass:: aiogram.dispatcher.filters.CommandStart
:members:
:show-inheritance:
CommandHelp
-----------
.. autoclass:: aiogram.dispatcher.filters.builtin.CommandHelp
.. autoclass:: aiogram.dispatcher.filters.CommandHelp
:members:
:show-inheritance:
CommandSettings
---------------
.. autoclass:: aiogram.dispatcher.filters.builtin.CommandSettings
.. autoclass:: aiogram.dispatcher.filters.CommandSettings
:members:
:show-inheritance:
@ -50,7 +50,7 @@ CommandSettings
CommandPrivacy
--------------
.. autoclass:: aiogram.dispatcher.filters.builtin.CommandPrivacy
.. autoclass:: aiogram.dispatcher.filters.CommandPrivacy
:members:
:show-inheritance:
@ -58,7 +58,7 @@ CommandPrivacy
Text
----
.. autoclass:: aiogram.dispatcher.filters.builtin.Text
.. autoclass:: aiogram.dispatcher.filters.Text
:members:
:show-inheritance:
@ -66,7 +66,7 @@ Text
HashTag
-------
.. autoclass:: aiogram.dispatcher.filters.builtin.HashTag
.. autoclass:: aiogram.dispatcher.filters.HashTag
:members:
:show-inheritance:
@ -74,7 +74,7 @@ HashTag
Regexp
------
.. autoclass:: aiogram.dispatcher.filters.builtin.Regexp
.. autoclass:: aiogram.dispatcher.filters.Regexp
:members:
:show-inheritance:
@ -82,7 +82,7 @@ Regexp
RegexpCommandsFilter
--------------------
.. autoclass:: aiogram.dispatcher.filters.builtin.RegexpCommandsFilter
.. autoclass:: aiogram.dispatcher.filters.RegexpCommandsFilter
:members:
:show-inheritance:
@ -90,21 +90,21 @@ RegexpCommandsFilter
ContentTypeFilter
-----------------
.. autoclass:: aiogram.dispatcher.filters.builtin.ContentTypeFilter
.. autoclass:: aiogram.dispatcher.filters.ContentTypeFilter
:members:
:show-inheritance:
IsSenderContact
---------------
.. autoclass:: aiogram.dispatcher.filters.builtin.IsSenderContact
.. autoclass:: aiogram.dispatcher.filters.IsSenderContact
:members:
:show-inheritance:
StateFilter
-----------
.. autoclass:: aiogram.dispatcher.filters.builtin.StateFilter
.. autoclass:: aiogram.dispatcher.filters.StateFilter
:members:
:show-inheritance:
@ -112,13 +112,13 @@ StateFilter
ExceptionsFilter
----------------
.. autoclass:: aiogram.dispatcher.filters.builtin.ExceptionsFilter
.. autoclass:: aiogram.dispatcher.filters.ExceptionsFilter
:members:
:show-inheritance:
IDFilter
----------------
--------
.. autoclass:: aiogram.dispatcher.filters.builtin.IDFilter
:members:
@ -126,9 +126,9 @@ IDFilter
AdminFilter
----------------
-----------
.. autoclass:: aiogram.dispatcher.filters.builtin.AdminFilter
.. autoclass:: aiogram.dispatcher.filters.AdminFilter
:members:
:show-inheritance:
@ -136,23 +136,23 @@ AdminFilter
IsReplyFilter
-------------
.. autoclass:: aiogram.dispatcher.filters.filters.IsReplyFilter
.. autoclass:: aiogram.dispatcher.filters.IsReplyFilter
:members:
:show-inheritance:
ForwardedMessageFilter
-------------
----------------------
.. autoclass:: aiogram.dispatcher.filters.filters.ForwardedMessageFilter
.. autoclass:: aiogram.dispatcher.filters.ForwardedMessageFilter
:members:
:show-inheritance:
ChatTypeFilter
-------------
--------------
.. autoclass:: aiogram.dispatcher.filters.filters.ChatTypeFilter
.. autoclass:: aiogram.dispatcher.filters.ChatTypeFilter
:members:
:show-inheritance:
@ -170,19 +170,19 @@ Own filter can be:
AbstractFilter
--------------
.. autoclass:: aiogram.dispatcher.filters.filters.AbstractFilter
.. autoclass:: aiogram.dispatcher.filters.AbstractFilter
:members:
:show-inheritance:
Filter
------
.. autoclass:: aiogram.dispatcher.filters.filters.Filter
.. autoclass:: aiogram.dispatcher.filters.Filter
:members:
:show-inheritance:
BoundFilter
-----------
.. autoclass:: aiogram.dispatcher.filters.filters.BoundFilter
.. autoclass:: aiogram.dispatcher.filters.BoundFilter
:members:
:show-inheritance:

View file

@ -12,15 +12,29 @@ Coming soon...
Memory storage
~~~~~~~~~~~~~~
Coming soon...
.. autoclass:: aiogram.contrib.fsm_storage.memory.MemoryStorage
:show-inheritance:
Redis storage
~~~~~~~~~~~~~
Coming soon...
.. autoclass:: aiogram.contrib.fsm_storage.redis.RedisStorage
:show-inheritance:
Mongo storage
~~~~~~~~~~~~~
.. autoclass:: aiogram.contrib.fsm_storage.mongo.MongoStorage
:show-inheritance:
Rethink DB storage
~~~~~~~~~~~~~~~~~~
Coming soon...
.. autoclass:: aiogram.contrib.fsm_storage.rethinkdb.RethinkDBStorage
:show-inheritance:
Making own storage's
~~~~~~~~~~~~~~~~~~~~

View file

@ -1,28 +1,8 @@
.. Autogenerated file at 2018-10-28 19:31:48.335963
=========================
Advanced executor example
=========================
!/usr/bin/env python3
**This example is outdated**
In this example used ArgumentParser for configuring Your bot.
Provided to start bot with webhook:
python advanced_executor_example.py \
--token TOKEN_HERE \
--host 0.0.0.0 \
--port 8084 \
--host-name example.com \
--webhook-port 443
Or long polling:
python advanced_executor_example.py --token TOKEN_HERE
So... In this example found small trouble:
can't get bot instance in handlers.
If you want to automatic change getting updates method use executor utils (from aiogram.utils.executor)
TODO: Move token to environment variables.
.. literalinclude:: ../../../examples/advanced_executor_example.py
:caption: advanced_executor_example.py
:language: python
:linenos:
:lines: 25-

View file

@ -1,5 +1,3 @@
.. Autogenerated file at 2018-09-08 02:07:37.593501
=================
Broadcast example
=================

View file

@ -1,5 +1,3 @@
.. Autogenerated file at 2018-09-08 02:07:37.558059
===================
Check user language
===================
@ -10,4 +8,3 @@ Babel is required.
:caption: check_user_language.py
:language: python
:linenos:
:lines: 5-

View file

@ -1,8 +1,7 @@
========
Echo bot
========
Very simple example of the bot which will sent text of the received messages to the sender
.. literalinclude:: ../../../examples/echo_bot.py
:caption: echo_bot.py
:language: python

View file

@ -1,5 +1,3 @@
.. Autogenerated file at 2018-09-08 02:07:37.595032
============================
Finite state machine example
============================

View file

@ -1,28 +1,8 @@
.. Autogenerated file at 2018-09-08 02:07:37.591007
============
I18n example
============
Internalize your bot
Step 1: extract texts
# pybabel extract i18n_example.py -o locales/mybot.pot
Step 2: create *.po files. For e.g. create en, ru, uk locales.
# echo {en,ru,uk} | xargs -n1 pybabel init -i locales/mybot.pot -d locales -D mybot -l
Step 3: translate texts
Step 4: compile translations
# pybabel compile -d locales -D mybot
Step 5: When you change the code of your bot you need to update po & mo files.
Step 5.1: regenerate pot file:
command from step 1
Step 5.2: update po files
# pybabel update -d locales -D mybot -i locales/mybot.pot
Step 5.3: update your translations
Step 5.4: compile mo files
command from step 4
.. literalinclude:: ../../../examples/i18n_example.py
:caption: i18n_example.py
:language: python
:linenos:
:lines: 22-

View file

@ -19,3 +19,4 @@ Examples
payments
broadcast_example
media_group
local_server

View file

@ -1,5 +1,3 @@
.. Autogenerated file at 2018-09-08 02:07:37.561907
==========
Inline bot
==========

View file

@ -0,0 +1,8 @@
============
Local server
============
.. literalinclude:: ../../../examples/local_server.py
:caption: local_server.py
:language: python
:linenos:

View file

@ -1,5 +1,3 @@
.. Autogenerated file at 2018-09-08 02:07:37.566615
===========
Media group
===========

View file

@ -1,5 +1,3 @@
.. Autogenerated file at 2018-09-08 02:07:37.560132
========================
Middleware and antiflood
========================

View file

@ -1,5 +1,3 @@
.. Autogenerated file at 2018-09-08 02:07:37.579017
========
Payments
========

View file

@ -1,5 +1,3 @@
.. Autogenerated file at 2018-09-08 02:07:37.555359
=================
Proxy and emojize
=================

View file

@ -1,5 +1,3 @@
.. Autogenerated file at 2018-09-08 02:07:37.568530
==============================
Regexp commands filter example
==============================

View file

@ -1,14 +1,8 @@
.. Autogenerated file at 2018-09-08 02:07:37.563878
==================
Throttling example
==================
=================
Throtling example
=================
Example for throttling manager.
You can use that for flood controlling.
.. literalinclude:: ../../../examples/throtling_example.py
:caption: throtling_example.py
.. literalinclude:: ../../../examples/throttling_example.py
:caption: throttling_example.py
:language: python
:linenos:
:lines: 7-

View file

@ -1,13 +1,8 @@
.. Autogenerated file at 2018-10-28 19:31:48.341172
===============
Webhook example
===============
Example outdated
.. literalinclude:: ../../../examples/webhook_example.py
:caption: webhook_example.py
:language: python
:linenos:
:lines: 5-

View file

@ -1,10 +1,8 @@
.. Autogenerated file at 2018-09-08 02:07:37.576034
===================
Webhook example old
===================
=================
Webhook example 2
=================
.. literalinclude:: ../../../examples/webhook_example_2.py
.. literalinclude:: ../../../examples/webhook_example_old.py
:caption: webhook_example_2.py
:language: python
:linenos:

View file

@ -22,7 +22,7 @@ Welcome to aiogram's documentation!
:target: https://pypi.python.org/pypi/aiogram
:alt: Supported python versions
.. image:: https://img.shields.io/badge/Telegram%20Bot%20API-4.9-blue.svg?style=flat-square&logo=telegram
.. image:: https://img.shields.io/badge/Telegram%20Bot%20API-5.0-blue.svg?style=flat-square&logo=telegram
:target: https://core.telegram.org/bots/api
:alt: Telegram Bot API

View file

@ -24,7 +24,7 @@ Next step: interaction with bots starts with one command. Register your first co
:language: python
:lines: 20-25
If you want to handle all messages in the chat simply add handler without filters:
If you want to handle all text messages in the chat simply add handler without filters:
.. literalinclude:: ../../examples/echo_bot.py
:language: python

View file

@ -13,3 +13,4 @@ Utils
parts
json
emoji
deep_linking

27
examples/local_server.py Normal file
View file

@ -0,0 +1,27 @@
import logging
from aiogram import Bot, Dispatcher, executor, types
from aiogram.bot.api import TelegramAPIServer
from aiogram.types import ContentType
API_TOKEN = 'BOT TOKEN HERE'
# Configure logging
logging.basicConfig(level=logging.INFO)
# Create private Bot API server endpoints wrapper
local_server = TelegramAPIServer.from_base('http://localhost')
# Initialize bot with using local server
bot = Bot(token=API_TOKEN, server=local_server)
# ... and dispatcher
dp = Dispatcher(bot)
@dp.message_handler(content_types=ContentType.ANY)
async def echo(message: types.Message):
await message.copy_to(message.chat.id)
if __name__ == '__main__':
executor.start_polling(dp, skip_updates=True)

View file

@ -1,3 +1,3 @@
aiohttp>=3.5.4,<4.0.0
Babel>=2.6.0
certifi>=2019.3.9
aiohttp>=3.7.2,<4.0.0
Babel>=2.8.0
certifi>=2020.6.20

View file

@ -57,12 +57,13 @@ setup(
'License :: OSI Approved :: MIT License',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Topic :: Software Development :: Libraries :: Application Frameworks',
],
install_requires=[
'aiohttp>=3.5.4,<4.0.0',
'Babel>=2.6.0',
'certifi>=2019.3.9',
'aiohttp>=3.7.2,<4.0.0',
'Babel>=2.8.0',
'certifi>=2020.6.20',
],
extras_require={
'proxy': [

View file

@ -24,6 +24,22 @@ async def test_get_me(bot: Bot, event_loop):
assert result == user
async def test_log_out(bot: Bot, event_loop):
""" logOut method test """
async with FakeTelegram(message_data=True, loop=event_loop):
result = await bot.log_out()
assert result is True
async def test_close_bot(bot: Bot, event_loop):
""" close method test """
async with FakeTelegram(message_data=True, loop=event_loop):
result = await bot.close_bot()
assert result is True
async def test_send_message(bot: Bot, event_loop):
""" sendMessage method test """
from .types.dataset import MESSAGE

View file

@ -1,5 +1,5 @@
[tox]
envlist = py37
envlist = py38
[testenv]
deps = -rdev_requirements.txt