Merge branch 'master' into dev-2.x

# Conflicts:
#	aiogram/__init__.py
#	aiogram/utils/exceptions.py
This commit is contained in:
Alex Root Junior 2018-07-16 00:15:55 +03:00
commit 2d7a142d84
5 changed files with 220 additions and 92 deletions

View file

@ -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}] "

View file

@ -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)

View file

@ -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

View file

@ -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):

View file

@ -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