mirror of
https://github.com/aiogram/aiogram.git
synced 2026-04-08 16:37:47 +00:00
Merge branch 'master' into dev-2.x
# Conflicts: # aiogram/__init__.py # aiogram/utils/exceptions.py
This commit is contained in:
commit
2d7a142d84
5 changed files with 220 additions and 92 deletions
|
|
@ -88,9 +88,13 @@ class LoggingMiddleware(BaseMiddleware):
|
|||
|
||||
async def on_pre_process_callback_query(self, callback_query: types.CallbackQuery, data: dict):
|
||||
if callback_query.message:
|
||||
self.logger.info(f"Received callback query [ID:{callback_query.id}] "
|
||||
f"in chat [{callback_query.message.chat.type}:{callback_query.message.chat.id}] "
|
||||
f"from user [ID:{callback_query.message.from_user.id}]")
|
||||
if callback_query.message.from_user:
|
||||
self.logger.info(f"Received callback query [ID:{callback_query.id}] "
|
||||
f"in chat [{callback_query.message.chat.type}:{callback_query.message.chat.id}] "
|
||||
f"from user [ID:{callback_query.message.from_user.id}]")
|
||||
else:
|
||||
self.logger.info(f"Received callback query [ID:{callback_query.id}] "
|
||||
f"in chat [{callback_query.message.chat.type}:{callback_query.message.chat.id}]")
|
||||
else:
|
||||
self.logger.info(f"Received callback query [ID:{callback_query.id}] "
|
||||
f"from inline message [ID:{callback_query.inline_message_id}] "
|
||||
|
|
@ -98,10 +102,15 @@ class LoggingMiddleware(BaseMiddleware):
|
|||
|
||||
async def on_post_process_callback_query(self, callback_query, results, data: dict):
|
||||
if callback_query.message:
|
||||
self.logger.debug(f"{HANDLED_STR[bool(len(results))]} "
|
||||
f"callback query [ID:{callback_query.id}] "
|
||||
f"in chat [{callback_query.message.chat.type}:{callback_query.message.chat.id}] "
|
||||
f"from user [ID:{callback_query.message.from_user.id}]")
|
||||
if callback_query.message.from_user:
|
||||
self.logger.debug(f"{HANDLED_STR[bool(len(results))]} "
|
||||
f"callback query [ID:{callback_query.id}] "
|
||||
f"in chat [{callback_query.message.chat.type}:{callback_query.message.chat.id}] "
|
||||
f"from user [ID:{callback_query.message.from_user.id}]")
|
||||
else:
|
||||
self.logger.debug(f"{HANDLED_STR[bool(len(results))]} "
|
||||
f"callback query [ID:{callback_query.id}] "
|
||||
f"in chat [{callback_query.message.chat.type}:{callback_query.message.chat.id}]")
|
||||
else:
|
||||
self.logger.debug(f"{HANDLED_STR[bool(len(results))]} "
|
||||
f"callback query [ID:{callback_query.id}] "
|
||||
|
|
|
|||
|
|
@ -358,6 +358,7 @@ class InlineQueryResultLocation(InlineQueryResult):
|
|||
latitude: base.Float = fields.Field()
|
||||
longitude: base.Float = fields.Field()
|
||||
title: base.String = fields.Field()
|
||||
live_period: base.Integer = fields.Field()
|
||||
input_message_content: InputMessageContent = fields.Field(base=InputMessageContent)
|
||||
thumb_url: base.String = fields.Field()
|
||||
thumb_width: base.Integer = fields.Field()
|
||||
|
|
@ -368,13 +369,15 @@ class InlineQueryResultLocation(InlineQueryResult):
|
|||
latitude: base.Float,
|
||||
longitude: base.Float,
|
||||
title: base.String,
|
||||
live_period: 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, reply_markup=reply_markup,
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ from __future__ import annotations
|
|||
|
||||
import datetime
|
||||
import functools
|
||||
import sys
|
||||
import typing
|
||||
|
||||
from . import base
|
||||
|
|
@ -23,6 +24,7 @@ from .video import Video
|
|||
from .video_note import VideoNote
|
||||
from .voice import Voice
|
||||
from ..utils import helper
|
||||
from ..utils import markdown as md
|
||||
|
||||
|
||||
class Message(base.TelegramObject):
|
||||
|
|
@ -79,42 +81,52 @@ class Message(base.TelegramObject):
|
|||
def content_type(self):
|
||||
if self.text:
|
||||
return ContentType.TEXT[0]
|
||||
if self.audio:
|
||||
elif self.audio:
|
||||
return ContentType.AUDIO[0]
|
||||
if self.document:
|
||||
elif self.document:
|
||||
return ContentType.DOCUMENT[0]
|
||||
if self.game:
|
||||
elif self.game:
|
||||
return ContentType.GAME[0]
|
||||
if self.photo:
|
||||
elif self.photo:
|
||||
return ContentType.PHOTO[0]
|
||||
if self.sticker:
|
||||
elif self.sticker:
|
||||
return ContentType.STICKER[0]
|
||||
if self.video:
|
||||
elif self.video:
|
||||
return ContentType.VIDEO[0]
|
||||
if self.video_note:
|
||||
elif self.video_note:
|
||||
return ContentType.VIDEO_NOTE[0]
|
||||
if self.voice:
|
||||
elif self.voice:
|
||||
return ContentType.VOICE[0]
|
||||
if self.contact:
|
||||
elif self.contact:
|
||||
return ContentType.CONTACT[0]
|
||||
if self.venue:
|
||||
elif self.venue:
|
||||
return ContentType.VENUE[0]
|
||||
if self.location:
|
||||
elif self.location:
|
||||
return ContentType.LOCATION[0]
|
||||
if self.new_chat_members:
|
||||
elif self.new_chat_members:
|
||||
return ContentType.NEW_CHAT_MEMBERS[0]
|
||||
if self.left_chat_member:
|
||||
elif self.left_chat_member:
|
||||
return ContentType.LEFT_CHAT_MEMBER[0]
|
||||
if self.invoice:
|
||||
elif self.invoice:
|
||||
return ContentType.INVOICE[0]
|
||||
if self.successful_payment:
|
||||
elif self.successful_payment:
|
||||
return ContentType.SUCCESSFUL_PAYMENT[0]
|
||||
if self.connected_website:
|
||||
elif self.connected_website:
|
||||
return ContentType.CONNECTED_WEBSITE[0]
|
||||
if self.migrate_from_chat_id:
|
||||
elif self.migrate_from_chat_id:
|
||||
return ContentType.MIGRATE_FROM_CHAT_ID[0]
|
||||
if self.migrate_to_chat_id:
|
||||
elif self.migrate_to_chat_id:
|
||||
return ContentType.MIGRATE_TO_CHAT_ID[0]
|
||||
elif self.pinned_message:
|
||||
return ContentType.PINNED_MESSAGE[0]
|
||||
elif self.new_chat_title:
|
||||
return ContentType.NEW_CHAT_TITLE[0]
|
||||
elif self.new_chat_photo:
|
||||
return ContentType.NEW_CHAT_PHOTO[0]
|
||||
elif self.delete_chat_photo:
|
||||
return ContentType.DELETE_CHAT_PHOTO[0]
|
||||
elif self.group_chat_created:
|
||||
return ContentType.GROUP_CHAT_CREATED[0]
|
||||
else:
|
||||
return ContentType.UNKNOWN[0]
|
||||
|
||||
|
|
@ -159,6 +171,49 @@ class Message(base.TelegramObject):
|
|||
if command:
|
||||
return command[1].strip()
|
||||
|
||||
def parse_entities(self, as_html=True):
|
||||
"""
|
||||
Text or caption formatted as HTML or Markdown.
|
||||
|
||||
:return: str
|
||||
"""
|
||||
|
||||
text = self.text or self.caption
|
||||
if text is None:
|
||||
raise TypeError("This message doesn't have any text.")
|
||||
|
||||
quote_fn = md.quote_html if as_html else md.escape_md
|
||||
|
||||
if not self.entities:
|
||||
return quote_fn(text)
|
||||
|
||||
if not sys.maxunicode == 0xffff:
|
||||
text = text.encode('utf-16-le')
|
||||
|
||||
result = ''
|
||||
offset = 0
|
||||
|
||||
for entity in sorted(self.entities, key=lambda item: item.offset):
|
||||
entity_text = entity.parse(text, as_html=as_html)
|
||||
|
||||
if sys.maxunicode == 0xffff:
|
||||
part = text[offset:entity.offset]
|
||||
result += quote_fn(part) + entity_text
|
||||
else:
|
||||
part = text[offset * 2:entity.offset * 2]
|
||||
result += quote_fn(part.decode('utf-16-le')) + entity_text
|
||||
|
||||
offset = entity.offset + entity.length
|
||||
|
||||
if sys.maxunicode == 0xffff:
|
||||
part = text[offset:]
|
||||
result += quote_fn(part)
|
||||
else:
|
||||
part = text[offset * 2:]
|
||||
result += quote_fn(part.decode('utf-16-le'))
|
||||
|
||||
return result
|
||||
|
||||
@property
|
||||
def md_text(self) -> str:
|
||||
"""
|
||||
|
|
@ -166,28 +221,16 @@ class Message(base.TelegramObject):
|
|||
|
||||
:return: str
|
||||
"""
|
||||
text = self.caption if self.caption else self.text
|
||||
|
||||
if self.text and self.entities:
|
||||
for entity in reversed(self.entities):
|
||||
text = entity.apply_md(text)
|
||||
|
||||
return text
|
||||
return self.parse_entities(False)
|
||||
|
||||
@property
|
||||
def html_text(self) -> str:
|
||||
"""
|
||||
Text or caption formatted as HTML.
|
||||
Text or caption formatted as HTML
|
||||
|
||||
:return: str
|
||||
"""
|
||||
text = self.caption if self.caption else self.text
|
||||
|
||||
if self.text and self.entities:
|
||||
for entity in reversed(self.entities):
|
||||
text = entity.apply_html(text)
|
||||
|
||||
return text
|
||||
return self.parse_entities()
|
||||
|
||||
async def reply(self, text, parse_mode=None, disable_web_page_preview=None,
|
||||
disable_notification=None, reply_markup=None, reply=True) -> Message:
|
||||
|
|
@ -723,6 +766,11 @@ class ContentType(helper.Helper):
|
|||
CONNECTED_WEBSITE = helper.ListItem() # connected_website
|
||||
MIGRATE_TO_CHAT_ID = helper.ListItem() # migrate_to_chat_id
|
||||
MIGRATE_FROM_CHAT_ID = helper.ListItem() # migrate_from_chat_id
|
||||
PINNED_MESSAGE = helper.ListItem() # pinned_message
|
||||
NEW_CHAT_TITLE = helper.ListItem() # new_chat_title
|
||||
NEW_CHAT_PHOTO = helper.ListItem() # new_chat_photo
|
||||
DELETE_CHAT_PHOTO = helper.ListItem() # delete_chat_photo
|
||||
GROUP_CHAT_CREATED = helper.ListItem() # group_chat_created
|
||||
|
||||
UNKNOWN = helper.ListItem() # unknown
|
||||
ANY = helper.ListItem() # any
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import sys
|
||||
|
||||
from . import base
|
||||
from . import fields
|
||||
from .user import User
|
||||
|
|
@ -16,56 +18,63 @@ class MessageEntity(base.TelegramObject):
|
|||
url: base.String = fields.Field()
|
||||
user: User = fields.Field(base=User)
|
||||
|
||||
def _apply(self, text, func):
|
||||
return text[:self.offset] + \
|
||||
func(text[self.offset:self.offset + self.length]) + \
|
||||
text[self.offset + self.length:]
|
||||
|
||||
def apply_md(self, text):
|
||||
def get_text(self, text):
|
||||
"""
|
||||
Apply entity for text as Markdown
|
||||
Get value of entity
|
||||
|
||||
:param text:
|
||||
:return:
|
||||
:param text: full text
|
||||
:return: part of text
|
||||
"""
|
||||
if sys.maxunicode == 0xffff:
|
||||
return text[self.offset:self.offset + self.length]
|
||||
|
||||
if not isinstance(text, bytes):
|
||||
entity_text = text.encode('utf-16-le')
|
||||
else:
|
||||
entity_text = text
|
||||
|
||||
entity_text = entity_text[self.offset * 2:(self.offset + self.length) * 2]
|
||||
return entity_text.decode('utf-16-le')
|
||||
|
||||
def parse(self, text, as_html=True):
|
||||
"""
|
||||
Get entity value with markup
|
||||
|
||||
:param text: original text
|
||||
:param as_html: as html?
|
||||
:return: entity text with markup
|
||||
"""
|
||||
if not text:
|
||||
return text
|
||||
entity_text = self.get_text(text)
|
||||
|
||||
if self.type == MessageEntityType.BOLD:
|
||||
return self._apply(text, markdown.bold)
|
||||
if as_html:
|
||||
return markdown.hbold(entity_text)
|
||||
return markdown.bold(entity_text)
|
||||
elif self.type == MessageEntityType.ITALIC:
|
||||
return self._apply(text, markdown.italic)
|
||||
if as_html:
|
||||
return markdown.hitalic(entity_text)
|
||||
return markdown.italic(entity_text)
|
||||
elif self.type == MessageEntityType.PRE:
|
||||
return self._apply(text, markdown.pre)
|
||||
if as_html:
|
||||
return markdown.hpre(entity_text)
|
||||
return markdown.pre(entity_text)
|
||||
elif self.type == MessageEntityType.CODE:
|
||||
return self._apply(text, markdown.code)
|
||||
if as_html:
|
||||
return markdown.hcode(entity_text)
|
||||
return markdown.code(entity_text)
|
||||
elif self.type == MessageEntityType.URL:
|
||||
return self._apply(text, lambda url: markdown.link(url, url))
|
||||
if as_html:
|
||||
return markdown.hlink(entity_text, entity_text)
|
||||
return markdown.link(entity_text, entity_text)
|
||||
elif self.type == MessageEntityType.TEXT_LINK:
|
||||
return self._apply(text, lambda url: markdown.link(url, self.url))
|
||||
if self.type == MessageEntityType.TEXT_MENTION and self.user:
|
||||
return self._apply(text, lambda name: self.user.get_mention(name, as_html=False))
|
||||
return text
|
||||
|
||||
def apply_html(self, text):
|
||||
"""
|
||||
Apply entity for text as HTML
|
||||
|
||||
:param text:
|
||||
:return:
|
||||
"""
|
||||
if self.type == MessageEntityType.BOLD:
|
||||
return self._apply(text, markdown.hbold)
|
||||
elif self.type == MessageEntityType.ITALIC:
|
||||
return self._apply(text, markdown.hitalic)
|
||||
elif self.type == MessageEntityType.PRE:
|
||||
return self._apply(text, markdown.hpre)
|
||||
elif self.type == MessageEntityType.CODE:
|
||||
return self._apply(text, markdown.hcode)
|
||||
elif self.type == MessageEntityType.URL:
|
||||
return self._apply(text, lambda url: markdown.hlink(url, url))
|
||||
elif self.type == MessageEntityType.TEXT_LINK:
|
||||
return self._apply(text, lambda url: markdown.hlink(url, self.url))
|
||||
if self.type == MessageEntityType.TEXT_MENTION and self.user:
|
||||
return self._apply(text, lambda name: self.user.get_mention(name, as_html=True))
|
||||
return text
|
||||
if as_html:
|
||||
return markdown.hlink(entity_text, self.url)
|
||||
return markdown.link(entity_text, self.url)
|
||||
elif self.type == MessageEntityType.TEXT_MENTION and self.user:
|
||||
return self.user.get_mention(entity_text)
|
||||
return entity_text
|
||||
|
||||
|
||||
class MessageEntityType(helper.Helper):
|
||||
|
|
|
|||
|
|
@ -9,8 +9,13 @@ TelegramAPIError
|
|||
MessageToDeleteNotFound
|
||||
MessageIdentifierNotSpecified
|
||||
MessageTextIsEmpty
|
||||
MessageCantBeEdited
|
||||
MessageToEditNotFound
|
||||
ToMuchMessages
|
||||
ObjectExpectedAsReplyMarkup
|
||||
InlineKeyboardExpected
|
||||
ChatNotFound
|
||||
ChatDescriptionIsNotModified
|
||||
InvalidQueryID
|
||||
InvalidPeerID
|
||||
InvalidHTTPUrlContent
|
||||
|
|
@ -29,11 +34,19 @@ TelegramAPIError
|
|||
MethodNotKnown
|
||||
PhotoAsInputFileRequired
|
||||
InvalidStickersSet
|
||||
NoStickerInRequest
|
||||
ChatAdminRequired
|
||||
NotEnoughRightsToPinMessage
|
||||
CantDemoteChatCreator
|
||||
CantRestrictSelf
|
||||
PhotoDimensions
|
||||
UnavailableMembers
|
||||
TypeOfFileMismatch
|
||||
WrongRemoteFileIdSpecified
|
||||
PaymentProviderInvalid
|
||||
CurrencyTotalAmountInvalid
|
||||
CantParseUrl
|
||||
CantParseEntities
|
||||
ConflictError
|
||||
TerminatedByOtherGetUpdates
|
||||
CantGetUpdates
|
||||
|
|
@ -42,6 +55,7 @@ TelegramAPIError
|
|||
BotBlocked
|
||||
UserDeactivated
|
||||
CantInitiateConversation
|
||||
CantTalkWithBots
|
||||
NetworkError
|
||||
RetryAfter
|
||||
MigrateToChat
|
||||
|
|
@ -91,15 +105,6 @@ class _MatchErrorMixin:
|
|||
"""
|
||||
return cls.match.lower() in message
|
||||
|
||||
@classmethod
|
||||
def throw(cls):
|
||||
"""
|
||||
Throw error
|
||||
|
||||
:raise: this
|
||||
"""
|
||||
raise cls(cls.text or cls.match)
|
||||
|
||||
@classmethod
|
||||
def detect(cls, description):
|
||||
description = description.lower()
|
||||
|
|
@ -107,7 +112,7 @@ class _MatchErrorMixin:
|
|||
if err is cls:
|
||||
continue
|
||||
if err.check(description):
|
||||
err.throw()
|
||||
raise err(cls.text or description)
|
||||
raise cls(description)
|
||||
|
||||
|
||||
|
|
@ -164,6 +169,14 @@ class MessageTextIsEmpty(MessageError):
|
|||
match = 'Message text is empty'
|
||||
|
||||
|
||||
class MessageCantBeEdited(MessageError):
|
||||
match = 'message can\'t be edited'
|
||||
|
||||
|
||||
class MessageToEditNotFound(MessageError):
|
||||
match = 'message to edit not found'
|
||||
|
||||
|
||||
class MessageIsTooLong(MessageError):
|
||||
match = 'message is too long'
|
||||
|
||||
|
|
@ -175,10 +188,22 @@ class ToMuchMessages(MessageError):
|
|||
match = 'Too much messages to send as an album'
|
||||
|
||||
|
||||
class ObjectExpectedAsReplyMarkup(BadRequest):
|
||||
match = 'object expected as reply markup'
|
||||
|
||||
|
||||
class InlineKeyboardExpected(BadRequest):
|
||||
match = 'inline keyboard expected'
|
||||
|
||||
|
||||
class ChatNotFound(BadRequest):
|
||||
match = 'chat not found'
|
||||
|
||||
|
||||
class ChatDescriptionIsNotModified(BadRequest):
|
||||
match = 'chat description is not modified'
|
||||
|
||||
|
||||
class InvalidQueryID(BadRequest):
|
||||
match = 'QUERY_ID_INVALID'
|
||||
text = 'Invalid query ID'
|
||||
|
|
@ -232,11 +257,23 @@ class InvalidStickersSet(BadRequest):
|
|||
text = 'Stickers set is invalid'
|
||||
|
||||
|
||||
class NoStickerInRequest(BadRequest):
|
||||
match = 'there is no sticker in the request'
|
||||
|
||||
|
||||
class ChatAdminRequired(BadRequest):
|
||||
match = 'CHAT_ADMIN_REQUIRED'
|
||||
text = 'Admin permissions is required!'
|
||||
|
||||
|
||||
class NotEnoughRightsToPinMessage(BadRequest):
|
||||
match = 'not enough rights to pin a message'
|
||||
|
||||
|
||||
class CantDemoteChatCreator(BadRequest):
|
||||
match = 'can\'t demote chat creator'
|
||||
|
||||
|
||||
class CantRestrictSelf(BadRequest):
|
||||
match = "can't restrict self"
|
||||
text = "Admin can't restrict self."
|
||||
|
|
@ -255,6 +292,20 @@ class TypeOfFileMismatch(BadRequest):
|
|||
match = 'type of file mismatch'
|
||||
|
||||
|
||||
class WrongRemoteFileIdSpecified(BadRequest):
|
||||
match = 'wrong remote file id specified'
|
||||
|
||||
|
||||
class PaymentProviderInvalid(BadRequest):
|
||||
match = 'PAYMENT_PROVIDER_INVALID'
|
||||
text = 'payment provider invalid'
|
||||
|
||||
|
||||
class CurrencyTotalAmountInvalid(BadRequest):
|
||||
match = 'currency_total_amount_invalid'
|
||||
text = 'currency total amount invalid'
|
||||
|
||||
|
||||
class BadWebhook(BadRequest):
|
||||
__group = True
|
||||
|
||||
|
|
@ -278,6 +329,10 @@ class CantParseUrl(BadRequest):
|
|||
match = 'can\'t parse URL'
|
||||
|
||||
|
||||
class CantParseEntities(BadRequest):
|
||||
match = 'can\'t parse entities'
|
||||
|
||||
|
||||
class NotFound(TelegramAPIError, _MatchErrorMixin):
|
||||
__group = True
|
||||
|
||||
|
|
@ -320,6 +375,10 @@ class CantInitiateConversation(Unauthorized):
|
|||
match = 'bot can\'t initiate conversation with a user'
|
||||
|
||||
|
||||
class CantTalkWithBots(Unauthorized):
|
||||
match = 'bot can\'t send messages to bots'
|
||||
|
||||
|
||||
class NetworkError(TelegramAPIError):
|
||||
pass
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue