From 96f895425f7d3d6e4904018c4d3aca627acb7c7d Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 9 Feb 2018 16:12:04 +0100 Subject: [PATCH 001/112] Add some types --- pyrogram/client/types/__init__.py | 2 + pyrogram/client/types/audio.py | 14 +++++ pyrogram/client/types/chat.py | 16 ++++++ pyrogram/client/types/message.py | 85 +++++++++++++++++++++++++++++++ pyrogram/client/types/user.py | 36 +++++++++++++ 5 files changed, 153 insertions(+) create mode 100644 pyrogram/client/types/__init__.py create mode 100644 pyrogram/client/types/audio.py create mode 100644 pyrogram/client/types/chat.py create mode 100644 pyrogram/client/types/message.py create mode 100644 pyrogram/client/types/user.py diff --git a/pyrogram/client/types/__init__.py b/pyrogram/client/types/__init__.py new file mode 100644 index 00000000..e858d4dc --- /dev/null +++ b/pyrogram/client/types/__init__.py @@ -0,0 +1,2 @@ +from .user import User +from .chat import Chat diff --git a/pyrogram/client/types/audio.py b/pyrogram/client/types/audio.py new file mode 100644 index 00000000..d2e53f44 --- /dev/null +++ b/pyrogram/client/types/audio.py @@ -0,0 +1,14 @@ +class Audio: + def __init__(self, + file_id: str, + duration: int = None, + performer: str = None, + title: str = None, + mime_type: str = None, + file_size: str = None): + self.file_id = file_id + self.duration = duration + self.performer = performer + self.title = title + self.mime_type = mime_type + self.file_size = file_size diff --git a/pyrogram/client/types/chat.py b/pyrogram/client/types/chat.py new file mode 100644 index 00000000..fb31bee1 --- /dev/null +++ b/pyrogram/client/types/chat.py @@ -0,0 +1,16 @@ +class Chat: + def __init__(self, + id: int, + type: str, + title: str = None, + username: str = None, + first_name: str = None, + last_name: str = None, + all_members_are_administrators: bool = None, + photo=None, + description: str = None, + invite_link: str = None, + pinned_message=None, + sticker_set_name: str = None, + can_set_sticker_set=None): + ... diff --git a/pyrogram/client/types/message.py b/pyrogram/client/types/message.py new file mode 100644 index 00000000..f617201c --- /dev/null +++ b/pyrogram/client/types/message.py @@ -0,0 +1,85 @@ +from . import User, Chat + + +class Message: + def __init__(self, + message_id: int, + from_user: User, + date: int, + chat: Chat, + forward_from: Chat = None, + forward_from_message_id: int = None, + forward_signature: str = None, + forward_date: int = None, + reply_to_message: "Message" = None, + edit_date: int = None, + media_group_id: str = None, + author_signature: str = None, + text: str = None, + entities=None, + caption_entities=None, + audio=None, + document=None, + game=None, + photo=None, + sticker=None, + video=None, + voice=None, + video_note=None, + caption: str = None, + contact=None, + location=None, + venue=None, + new_chat_members: list = None, + left_chat_member: User = None, + new_chat_title: str = None, + new_chat_photo: list = None, + delete_chat_photo: bool = None, + group_chat_created: bool = None, + supergroup_chat_created: bool = None, + channel_chat_created: bool = None, + migrate_to_chat_id: int = None, + migrate_from_chat_id: int = None, + pinned_message: "Message" = None, + invoice=None, + successful_payment=None): + self.message_id = message_id + self.from_user = from_user + self.date = date + self.chat = chat + self.forward_from = forward_from + self.forward_from_message_id = forward_from_message_id + self.forward_signature = forward_signature + self.forward_date = forward_date + self.reply_to_message = reply_to_message + self.edit_date = edit_date + self.media_group_id = media_group_id + self.author_signature = author_signature + self.text = text + self.entities = entities + self.caption_entities = caption_entities + self.audio = audio + self.document = document + self.game = game + self.photo = photo + self.sticker = sticker + self.video = video + self.voice = voice + self.video_note = video_note + self.caption = caption + self.contact = contact + self.location = location + self.venue = venue + self.new_chat_members = new_chat_members + self.left_chat_member = left_chat_member + self.new_chat_title = new_chat_title + self.new_chat_photo = new_chat_photo + self.delete_chat_photo = delete_chat_photo + self.group_chat_created = group_chat_created + self.supergroup_chat_created = supergroup_chat_created + self.channel_chat_created = channel_chat_created + self.migrate_to_chat_id = migrate_to_chat_id + self.migrate_from_chat_id = migrate_from_chat_id + self.pinned_message = pinned_message + self.invoice = invoice + self.successful_payment = successful_payment diff --git a/pyrogram/client/types/user.py b/pyrogram/client/types/user.py new file mode 100644 index 00000000..eee9113d --- /dev/null +++ b/pyrogram/client/types/user.py @@ -0,0 +1,36 @@ +class User: + """This object represents a Telegram user or bot. + + Args: + id (:obj:`int`): + Unique identifier for this user or bot. + + is_bot (:obj:`bool`): + True, if this user is a bot. + + first_name (:obj:`str`): + User's or bot’s first name. + + last_name (:obj:`str`, optional): + User's or bot’s last name. + + username (:obj:`str`, optional): + User's or bot’s username. + + language_code (:obj:`str`, optional): + IETF language tag of the user's language. + """ + + def __init__(self, + id: int, + is_bot: bool, + first_name: str, + last_name: str = None, + username: str = None, + language_code: str = None): + self.id = id + self.is_bot = is_bot + self.first_name = first_name + self.last_name = last_name + self.username = username + self.language_code = language_code From 8216201f19ccacfc08998bace78e16eabc77a2e5 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 9 Mar 2018 14:44:31 +0100 Subject: [PATCH 002/112] Add Voice type --- pyrogram/client/types/voice.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 pyrogram/client/types/voice.py diff --git a/pyrogram/client/types/voice.py b/pyrogram/client/types/voice.py new file mode 100644 index 00000000..d21559f0 --- /dev/null +++ b/pyrogram/client/types/voice.py @@ -0,0 +1,10 @@ +class Voice: + def __init__(self, + file_id: str, + duration: int, + mime_type: str = None, + file_size: int = None): + self.file_id = file_id + self.duration = duration + self.mime_type = mime_type + self.file_size = file_size From 8bd5a796438c3392c5d71c4d9dfa054d799fe04b Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 9 Mar 2018 14:44:43 +0100 Subject: [PATCH 003/112] Add Animation type --- pyrogram/client/types/animation.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 pyrogram/client/types/animation.py diff --git a/pyrogram/client/types/animation.py b/pyrogram/client/types/animation.py new file mode 100644 index 00000000..a3693396 --- /dev/null +++ b/pyrogram/client/types/animation.py @@ -0,0 +1,15 @@ +from . import PhotoSize + + +class Animation: + def __init__(self, + file_id: str, + thumb: PhotoSize = None, + file_name: str = None, + mime_type: str = None, + file_size: int = None): + self.file_id = file_id + self.thumb = thumb + self.file_name = file_name + self.mime_type = mime_type + self.file_size = file_size From 18c20f0ca5d69cca238e90eb923798688656dd77 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 9 Mar 2018 14:44:53 +0100 Subject: [PATCH 004/112] Add Game type --- pyrogram/client/types/game.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 pyrogram/client/types/game.py diff --git a/pyrogram/client/types/game.py b/pyrogram/client/types/game.py new file mode 100644 index 00000000..387af3f4 --- /dev/null +++ b/pyrogram/client/types/game.py @@ -0,0 +1,17 @@ +from . import Animation + + +class Game: + def __init__(self, + title: str, + description: str, + photo: list, + text: str = None, + text_entities: list = None, + animation: Animation = None): + self.title = title + self.description = description + self.photo = photo + self.text = text + self.text_entities = text_entities + self.animation = animation From 48bf7438b71b48bfa4bd766677a59ebe6e192aa7 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 9 Mar 2018 14:45:06 +0100 Subject: [PATCH 005/112] Add PhotoSize type --- pyrogram/client/types/photo_size.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 pyrogram/client/types/photo_size.py diff --git a/pyrogram/client/types/photo_size.py b/pyrogram/client/types/photo_size.py new file mode 100644 index 00000000..3ada21d7 --- /dev/null +++ b/pyrogram/client/types/photo_size.py @@ -0,0 +1,10 @@ +class PhotoSize: + def __init__(self, + file_id: str, + width: int, + height: int, + file_size: int): + self.file_id = file_id + self.width = width + self.height = height + self.file_size = file_size From 4e4d9e6e44c0d74401c143ed07762d1ab8ceee29 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 9 Mar 2018 14:45:18 +0100 Subject: [PATCH 006/112] Add MessageEntity type --- pyrogram/client/types/message_entity.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 pyrogram/client/types/message_entity.py diff --git a/pyrogram/client/types/message_entity.py b/pyrogram/client/types/message_entity.py new file mode 100644 index 00000000..bdc686c0 --- /dev/null +++ b/pyrogram/client/types/message_entity.py @@ -0,0 +1,15 @@ +from . import User + + +class MessageEntity: + def __init__(self, + type: str, + offset: int, + length: int, + url: str = None, + user: User = None): + self.type = type + self.offset = offset + self.length = length + self.url = url + self.user = user From 33baf91a26051ec8127742795aba48b0353bd163 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 9 Mar 2018 14:45:27 +0100 Subject: [PATCH 007/112] Add Update type --- pyrogram/client/types/update.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 pyrogram/client/types/update.py diff --git a/pyrogram/client/types/update.py b/pyrogram/client/types/update.py new file mode 100644 index 00000000..f367afc3 --- /dev/null +++ b/pyrogram/client/types/update.py @@ -0,0 +1,28 @@ +from . import Message + + +class Update: + """This object represents an incoming update. + At most one of the optional parameters can be present in any given update. + + + Args: + message (:obj:`Message `): + + edited_message (): + + channel_post (): + + edited_channel_post (): + + """ + + def __init__(self, + message: Message = None, + edited_message: Message = None, + channel_post: Message = None, + edited_channel_post: Message = None): + self.message = message + self.edited_message = edited_message + self.channel_post = channel_post + self.edited_channel_post = edited_channel_post From 3fa30b3a505a5eb014b23893bf3ded7ce53f970e Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 9 Mar 2018 14:45:40 +0100 Subject: [PATCH 008/112] Add Contact type --- pyrogram/client/types/contact.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 pyrogram/client/types/contact.py diff --git a/pyrogram/client/types/contact.py b/pyrogram/client/types/contact.py new file mode 100644 index 00000000..e07fb441 --- /dev/null +++ b/pyrogram/client/types/contact.py @@ -0,0 +1,10 @@ +class Contact: + def __init__(self, + phone_number: str, + first_name: str, + last_name: str = None, + user_id: int = None): + self.phone_number = phone_number + self.first_name = first_name + self.last_name = last_name + self.user_id = user_id From 35fcfe8266d93a353a5ea8f7235d56c810f4cc5a Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 9 Mar 2018 14:45:58 +0100 Subject: [PATCH 009/112] Add Sticker type --- pyrogram/client/types/sticker.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 pyrogram/client/types/sticker.py diff --git a/pyrogram/client/types/sticker.py b/pyrogram/client/types/sticker.py new file mode 100644 index 00000000..1e87fd84 --- /dev/null +++ b/pyrogram/client/types/sticker.py @@ -0,0 +1,21 @@ +from . import PhotoSize, MaskPosition + + +class Sticker: + def __init__(self, + file_id: str, + width: int, + height: int, + thumb: PhotoSize = None, + emoji: str = None, + set_name: str = None, + mask_position: MaskPosition = None, + file_size: int = None): + self.file_id = file_id + self.width = width + self.height = height + self.thumb = thumb + self.emoji = emoji + self.set_name = set_name + self.mask_position = mask_position + self.file_size = file_size From 281323288e385fde3ae591af528f973894f9bea6 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 9 Mar 2018 14:46:08 +0100 Subject: [PATCH 010/112] Add Document type --- pyrogram/client/types/document.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 pyrogram/client/types/document.py diff --git a/pyrogram/client/types/document.py b/pyrogram/client/types/document.py new file mode 100644 index 00000000..cc74c4e7 --- /dev/null +++ b/pyrogram/client/types/document.py @@ -0,0 +1,15 @@ +from . import PhotoSize + + +class Document: + def __init__(self, + file_id: str, + thumb: PhotoSize = None, + file_name: str = None, + mime_type: str = None, + file_size: int = None): + self.file_id = file_id + self.thumb = thumb + self.file_name = file_name + self.mime_type = mime_type + self.file_size = file_size From 24c2d20137c3601f8d835eb483f8536ead6c4cf1 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 9 Mar 2018 14:46:16 +0100 Subject: [PATCH 011/112] Add Venue type --- pyrogram/client/types/venue.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 pyrogram/client/types/venue.py diff --git a/pyrogram/client/types/venue.py b/pyrogram/client/types/venue.py new file mode 100644 index 00000000..f1c058fd --- /dev/null +++ b/pyrogram/client/types/venue.py @@ -0,0 +1,13 @@ +from . import Location + + +class Venue: + def __init__(self, + location: Location, + title: str, + address: str, + foursquare_id: str = None): + self.location = location + self.title = title + self.address = address + self.foursquare_id = foursquare_id From 472495397179c98946afb972b327d2ada7fbff43 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 9 Mar 2018 14:46:25 +0100 Subject: [PATCH 012/112] Add VideoNote type --- pyrogram/client/types/video_note.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 pyrogram/client/types/video_note.py diff --git a/pyrogram/client/types/video_note.py b/pyrogram/client/types/video_note.py new file mode 100644 index 00000000..3969c2d9 --- /dev/null +++ b/pyrogram/client/types/video_note.py @@ -0,0 +1,15 @@ +from . import PhotoSize + + +class VideoNote: + def __init__(self, + file_id: str, + length: int, + duration: int, + thumb: PhotoSize = None, + file_size: int = None): + self.file_id = file_id + self.length = length + self.duration = duration + self.thumb = thumb + self.file_size = file_size From f3f462c009caa5e684cbc50505234c36f653d5bf Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 9 Mar 2018 14:46:38 +0100 Subject: [PATCH 013/112] Add MaskPosition type --- pyrogram/client/types/mask_position.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 pyrogram/client/types/mask_position.py diff --git a/pyrogram/client/types/mask_position.py b/pyrogram/client/types/mask_position.py new file mode 100644 index 00000000..a7720dc7 --- /dev/null +++ b/pyrogram/client/types/mask_position.py @@ -0,0 +1,10 @@ +class MaskPosition: + def __init__(self, + point: str, + x_shift: float, + y_shift: float, + scale: float): + self.point = point + self.x_shift = x_shift + self.y_shift = y_shift + self.scale = scale From 4d367ce04e66f7f38abc62fbd3baae8d437b6a61 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 9 Mar 2018 14:46:49 +0100 Subject: [PATCH 014/112] Add ChatMember type --- pyrogram/client/types/chat_member.py | 37 ++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 pyrogram/client/types/chat_member.py diff --git a/pyrogram/client/types/chat_member.py b/pyrogram/client/types/chat_member.py new file mode 100644 index 00000000..e8e4d9be --- /dev/null +++ b/pyrogram/client/types/chat_member.py @@ -0,0 +1,37 @@ +from . import User + + +class ChatMember: + def __init__(self, + user: User, + status: str, + until_date: int = None, + can_be_edited: bool = None, + can_change_info: bool = None, + can_post_messages: bool = None, + can_edit_messages: bool = None, + can_delete_messages: bool = None, + can_invite_users: bool = None, + can_restrict_members: bool = None, + can_pin_messages: bool = None, + can_promote_members: bool = None, + can_send_messages: bool = None, + can_send_media_messages: bool = None, + can_send_other_messages: bool = None, + can_add_web_page_pewviews: bool = None): + self.user = user + self.status = status + self.until_date = until_date + self.can_be_edited = can_be_edited + self.can_change_info = can_change_info + self.can_post_messages = can_post_messages + self.can_edit_messages = can_edit_messages + self.can_delete_messages = can_delete_messages + self.can_invite_users = can_invite_users + self.can_restrict_members = can_restrict_members + self.can_pin_messages = can_pin_messages + self.can_promote_members = can_promote_members + self.can_send_messages = can_send_messages + self.can_send_media_messages = can_send_media_messages + self.can_send_other_messages = can_send_other_messages + self.can_add_web_page_pewviews = can_add_web_page_pewviews From 59f1fd9ee62ce5a43dec39256f7de1b5ce6cc7f9 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 9 Mar 2018 14:46:58 +0100 Subject: [PATCH 015/112] Add Location type --- pyrogram/client/types/location.py | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 pyrogram/client/types/location.py diff --git a/pyrogram/client/types/location.py b/pyrogram/client/types/location.py new file mode 100644 index 00000000..5252e67f --- /dev/null +++ b/pyrogram/client/types/location.py @@ -0,0 +1,6 @@ +class Location: + def __init__(self, + longitude: float, + latitude: float): + self.longitude = longitude + self.latitude = latitude From 345da2fccdfd6ccb0269c27f96a3aff83b4dcf5f Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 9 Mar 2018 14:47:12 +0100 Subject: [PATCH 016/112] Add UserProfilePhotos type --- pyrogram/client/types/user_profile_photos.py | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 pyrogram/client/types/user_profile_photos.py diff --git a/pyrogram/client/types/user_profile_photos.py b/pyrogram/client/types/user_profile_photos.py new file mode 100644 index 00000000..f22296d2 --- /dev/null +++ b/pyrogram/client/types/user_profile_photos.py @@ -0,0 +1,6 @@ +class UserProfilePhotos: + def __init__(self, + total_count: int, + photos: list): + self.total_count = total_count + self.photos = photos From 76b78f6a9e438cc340f1b8e6e93515b979551a60 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 9 Mar 2018 14:47:22 +0100 Subject: [PATCH 017/112] Add ChatPhoto type --- pyrogram/client/types/chat_photo.py | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 pyrogram/client/types/chat_photo.py diff --git a/pyrogram/client/types/chat_photo.py b/pyrogram/client/types/chat_photo.py new file mode 100644 index 00000000..c2d30bdf --- /dev/null +++ b/pyrogram/client/types/chat_photo.py @@ -0,0 +1,6 @@ +class ChatPhoto: + def __init__(self, + small_file_id: str, + big_file_id: str): + self.small_file_id = small_file_id + self.big_file_id = big_file_id From 140f324cd029f25e164d61c5da6d302a5da8d356 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 9 Mar 2018 14:47:36 +0100 Subject: [PATCH 018/112] Add StickerSet type --- pyrogram/client/types/sticker_set.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 pyrogram/client/types/sticker_set.py diff --git a/pyrogram/client/types/sticker_set.py b/pyrogram/client/types/sticker_set.py new file mode 100644 index 00000000..2970a111 --- /dev/null +++ b/pyrogram/client/types/sticker_set.py @@ -0,0 +1,10 @@ +class StickerSet: + def __init__(self, + name: str, + title: str, + contain_masks: bool, + stickers: list): + self.name = name + self.title = title + self.contain_masks = contain_masks + self.stickers = stickers From 7ff9f28e153969279ea18affc3af1d9664af5129 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 9 Mar 2018 14:47:45 +0100 Subject: [PATCH 019/112] Add Video type --- pyrogram/client/types/video.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 pyrogram/client/types/video.py diff --git a/pyrogram/client/types/video.py b/pyrogram/client/types/video.py new file mode 100644 index 00000000..37d0a8c8 --- /dev/null +++ b/pyrogram/client/types/video.py @@ -0,0 +1,19 @@ +from . import PhotoSize + + +class Video: + def __init__(self, + file_id: str, + width: int, + height: int, + duration: int, + thumb: PhotoSize = None, + mime_type: str = None, + file_size: int = None): + self.file_id = file_id + self.width = width + self.height = height + self.duration = duration + self.thumb = thumb + self.mime_type = mime_type + self.file_size = file_size From 3d2029e9eaf3afc8b4ca287ccefedcc10a3f9312 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 9 Mar 2018 14:48:01 +0100 Subject: [PATCH 020/112] Update Audio type --- pyrogram/client/types/audio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyrogram/client/types/audio.py b/pyrogram/client/types/audio.py index d2e53f44..10febb4b 100644 --- a/pyrogram/client/types/audio.py +++ b/pyrogram/client/types/audio.py @@ -5,7 +5,7 @@ class Audio: performer: str = None, title: str = None, mime_type: str = None, - file_size: str = None): + file_size: int = None): self.file_id = file_id self.duration = duration self.performer = performer From caf1dea22789271a382c6c33ab52bb17ed70a8f8 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 9 Mar 2018 14:48:13 +0100 Subject: [PATCH 021/112] Update Chat type --- pyrogram/client/types/chat.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/pyrogram/client/types/chat.py b/pyrogram/client/types/chat.py index fb31bee1..78853bf4 100644 --- a/pyrogram/client/types/chat.py +++ b/pyrogram/client/types/chat.py @@ -1,3 +1,6 @@ +from . import Message, ChatPhoto + + class Chat: def __init__(self, id: int, @@ -7,10 +10,22 @@ class Chat: first_name: str = None, last_name: str = None, all_members_are_administrators: bool = None, - photo=None, + photo: ChatPhoto = None, description: str = None, invite_link: str = None, - pinned_message=None, + pinned_message: Message = None, sticker_set_name: str = None, - can_set_sticker_set=None): - ... + can_set_sticker_set: bool = None): + self.id = id + self.type = type + self.title = title + self.username = username + self.first_name = first_name + self.last_name = last_name + self.all_members_are_administrators = all_members_are_administrators + self.photo = photo + self.description = description + self.invite_link = invite_link + self.pinned_message = pinned_message + self.sticker_set_name = sticker_set_name + self.can_set_sticker_set = can_set_sticker_set From d8c634152a1a538301afec8481a72eda2a5a600a Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 9 Mar 2018 14:59:08 +0100 Subject: [PATCH 022/112] Update Message type --- pyrogram/client/types/message.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/pyrogram/client/types/message.py b/pyrogram/client/types/message.py index f617201c..02cfe74c 100644 --- a/pyrogram/client/types/message.py +++ b/pyrogram/client/types/message.py @@ -1,13 +1,14 @@ -from . import User, Chat +from . import User, Chat, Audio class Message: def __init__(self, message_id: int, - from_user: User, date: int, chat: Chat, - forward_from: Chat = None, + from_user: User = None, + forward_from: User = None, + forward_from_chat: Chat = None, forward_from_message_id: int = None, forward_signature: str = None, forward_date: int = None, @@ -16,9 +17,9 @@ class Message: media_group_id: str = None, author_signature: str = None, text: str = None, - entities=None, - caption_entities=None, - audio=None, + entities: list = None, + caption_entities: list = None, + audio: Audio = None, document=None, game=None, photo=None, @@ -42,12 +43,14 @@ class Message: migrate_from_chat_id: int = None, pinned_message: "Message" = None, invoice=None, - successful_payment=None): + successful_payment=None, + connected_website=None): self.message_id = message_id - self.from_user = from_user self.date = date self.chat = chat + self.from_user = from_user self.forward_from = forward_from + self.forward_from_chat = forward_from_chat self.forward_from_message_id = forward_from_message_id self.forward_signature = forward_signature self.forward_date = forward_date @@ -83,3 +86,4 @@ class Message: self.pinned_message = pinned_message self.invoice = invoice self.successful_payment = successful_payment + self.connected_website = connected_website From 85f92120d1561376ec7eb8057469f12dbff2db55 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 9 Mar 2018 14:59:30 +0100 Subject: [PATCH 023/112] Add types to __init__.py --- pyrogram/client/types/__init__.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/pyrogram/client/types/__init__.py b/pyrogram/client/types/__init__.py index e858d4dc..f6617387 100644 --- a/pyrogram/client/types/__init__.py +++ b/pyrogram/client/types/__init__.py @@ -1,2 +1,22 @@ -from .user import User +from .animation import Animation +from .audio import Audio from .chat import Chat +from .chat_member import ChatMember +from .chat_photo import ChatPhoto +from .contact import Contact +from .document import Document +from .game import Game +from .location import Location +from .mask_position import MaskPosition +from .message import Message +from .message_entity import MessageEntity +from .photo_size import PhotoSize +from .sticker import Sticker +from .sticker_set import StickerSet +from .update import Update +from .user import User +from .user_profile_photos import UserProfilePhotos +from .venue import Venue +from .video import Video +from .video_note import VideoNote +from .voice import Voice From f26dc10ee2b390c5e475a16f69b778fe02843435 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 9 Mar 2018 15:43:54 +0100 Subject: [PATCH 024/112] Use namespace instead of importing types --- pyrogram/client/client.py | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py index 5a834ee7..e4edd0dd 100644 --- a/pyrogram/client/client.py +++ b/pyrogram/client/client.py @@ -42,12 +42,6 @@ from pyrogram.api.errors import ( PasswordHashInvalid, FloodWait, PeerIdInvalid, FilePartMissing, ChatAdminRequired, FirstnameInvalid, PhoneNumberBanned, VolumeLocNotFound) -from pyrogram.api.types import ( - User, Chat, Channel, - PeerUser, PeerChannel, - InputPeerEmpty, InputPeerSelf, - InputPeerUser, InputPeerChat, InputPeerChannel -) from pyrogram.crypto import AES from pyrogram.session import Auth, Session from pyrogram.session.internals import MsgId @@ -227,7 +221,7 @@ class Client: def fetch_peers(self, entities: list): for entity in entities: - if isinstance(entity, User): + if isinstance(entity, types.User): user_id = entity.id if user_id in self.peers_by_id: @@ -241,7 +235,7 @@ class Client: username = entity.username phone = entity.phone - input_peer = InputPeerUser( + input_peer = types.InputPeerUser( user_id=user_id, access_hash=access_hash ) @@ -254,20 +248,20 @@ class Client: if phone is not None: self.peers_by_phone[phone] = input_peer - if isinstance(entity, Chat): + if isinstance(entity, types.Chat): chat_id = entity.id peer_id = -chat_id if peer_id in self.peers_by_id: continue - input_peer = InputPeerChat( + input_peer = types.InputPeerChat( chat_id=chat_id ) self.peers_by_id[peer_id] = input_peer - if isinstance(entity, Channel): + if isinstance(entity, types.Channel): channel_id = entity.id peer_id = int("-100" + str(channel_id)) @@ -281,7 +275,7 @@ class Client: username = entity.username - input_peer = InputPeerChannel( + input_peer = types.InputPeerChannel( channel_id=channel_id, access_hash=access_hash ) @@ -791,7 +785,7 @@ class Client: dialogs = self.send( functions.messages.GetDialogs( - 0, 0, InputPeerEmpty(), + 0, 0, types.InputPeerEmpty(), self.DIALOGS_AT_ONCE, True ) ) @@ -824,14 +818,14 @@ class Client: ) ) # type: types.contacts.ResolvedPeer - if type(resolved_peer.peer) is PeerUser: - input_peer = InputPeerUser( + if type(resolved_peer.peer) is types.PeerUser: + input_peer = types.InputPeerUser( user_id=resolved_peer.users[0].id, access_hash=resolved_peer.users[0].access_hash ) peer_id = input_peer.user_id - elif type(resolved_peer.peer) is PeerChannel: - input_peer = InputPeerChannel( + elif type(resolved_peer.peer) is types.PeerChannel: + input_peer = types.InputPeerChannel( channel_id=resolved_peer.chats[0].id, access_hash=resolved_peer.chats[0].access_hash ) @@ -866,7 +860,7 @@ class Client: """ if type(peer_id) is str: if peer_id in ("self", "me"): - return InputPeerSelf() + return types.InputPeerSelf() peer_id = peer_id.lower().strip("@+") @@ -913,7 +907,7 @@ class Client: """ return self.send( functions.users.GetFullUser( - InputPeerSelf() + types.InputPeerSelf() ) ) @@ -2323,7 +2317,7 @@ class Client: ) ) - channel = InputPeerChannel( + channel = types.InputPeerChannel( channel_id=resolved_peer.chats[0].id, access_hash=resolved_peer.chats[0].access_hash ) From e12a81ebb6bf2660a63a5314d31b9dd192a3e899 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sat, 10 Mar 2018 10:50:51 +0100 Subject: [PATCH 025/112] Attempt to fix cyclic imports --- pyrogram/client/types/animation.py | 5 +-- pyrogram/client/types/chat.py | 7 ++--- pyrogram/client/types/chat_member.py | 5 +-- pyrogram/client/types/document.py | 5 +-- pyrogram/client/types/game.py | 5 +-- pyrogram/client/types/message.py | 41 ++++++++++++------------- pyrogram/client/types/message_entity.py | 5 +-- pyrogram/client/types/sticker.py | 7 ++--- pyrogram/client/types/update.py | 11 +++---- pyrogram/client/types/venue.py | 5 +-- pyrogram/client/types/video.py | 5 +-- pyrogram/client/types/video_note.py | 5 +-- 12 files changed, 35 insertions(+), 71 deletions(-) diff --git a/pyrogram/client/types/animation.py b/pyrogram/client/types/animation.py index a3693396..ea04beaf 100644 --- a/pyrogram/client/types/animation.py +++ b/pyrogram/client/types/animation.py @@ -1,10 +1,7 @@ -from . import PhotoSize - - class Animation: def __init__(self, file_id: str, - thumb: PhotoSize = None, + thumb: "PhotoSize" = None, file_name: str = None, mime_type: str = None, file_size: int = None): diff --git a/pyrogram/client/types/chat.py b/pyrogram/client/types/chat.py index 78853bf4..6a09215d 100644 --- a/pyrogram/client/types/chat.py +++ b/pyrogram/client/types/chat.py @@ -1,6 +1,3 @@ -from . import Message, ChatPhoto - - class Chat: def __init__(self, id: int, @@ -10,10 +7,10 @@ class Chat: first_name: str = None, last_name: str = None, all_members_are_administrators: bool = None, - photo: ChatPhoto = None, + photo: "ChatPhoto" = None, description: str = None, invite_link: str = None, - pinned_message: Message = None, + pinned_message: "Message" = None, sticker_set_name: str = None, can_set_sticker_set: bool = None): self.id = id diff --git a/pyrogram/client/types/chat_member.py b/pyrogram/client/types/chat_member.py index e8e4d9be..2497e10b 100644 --- a/pyrogram/client/types/chat_member.py +++ b/pyrogram/client/types/chat_member.py @@ -1,9 +1,6 @@ -from . import User - - class ChatMember: def __init__(self, - user: User, + user: "User", status: str, until_date: int = None, can_be_edited: bool = None, diff --git a/pyrogram/client/types/document.py b/pyrogram/client/types/document.py index cc74c4e7..49a9c79d 100644 --- a/pyrogram/client/types/document.py +++ b/pyrogram/client/types/document.py @@ -1,10 +1,7 @@ -from . import PhotoSize - - class Document: def __init__(self, file_id: str, - thumb: PhotoSize = None, + thumb: "PhotoSize" = None, file_name: str = None, mime_type: str = None, file_size: int = None): diff --git a/pyrogram/client/types/game.py b/pyrogram/client/types/game.py index 387af3f4..905d9ef5 100644 --- a/pyrogram/client/types/game.py +++ b/pyrogram/client/types/game.py @@ -1,6 +1,3 @@ -from . import Animation - - class Game: def __init__(self, title: str, @@ -8,7 +5,7 @@ class Game: photo: list, text: str = None, text_entities: list = None, - animation: Animation = None): + animation: "Animation" = None): self.title = title self.description = description self.photo = photo diff --git a/pyrogram/client/types/message.py b/pyrogram/client/types/message.py index 02cfe74c..0f7a9167 100644 --- a/pyrogram/client/types/message.py +++ b/pyrogram/client/types/message.py @@ -1,14 +1,11 @@ -from . import User, Chat, Audio - - class Message: def __init__(self, message_id: int, date: int, - chat: Chat, - from_user: User = None, - forward_from: User = None, - forward_from_chat: Chat = None, + chat: "Chat", + from_user: "User" = None, + forward_from: "User" = None, + forward_from_chat: "Chat" = None, forward_from_message_id: int = None, forward_signature: str = None, forward_date: int = None, @@ -19,20 +16,20 @@ class Message: text: str = None, entities: list = None, caption_entities: list = None, - audio: Audio = None, - document=None, - game=None, - photo=None, - sticker=None, - video=None, - voice=None, - video_note=None, + audio: "Audio" = None, + document: "Document" = None, + game: "Game" = None, + photo: list = None, + sticker: "Sticker" = None, + video: "Video" = None, + voice: "Voice" = None, + video_note: "VideoNote" = None, caption: str = None, - contact=None, - location=None, - venue=None, + contact: "Contact" = None, + location: "Location" = None, + venue: "Venue" = None, new_chat_members: list = None, - left_chat_member: User = None, + left_chat_member: "User" = None, new_chat_title: str = None, new_chat_photo: list = None, delete_chat_photo: bool = None, @@ -42,9 +39,9 @@ class Message: migrate_to_chat_id: int = None, migrate_from_chat_id: int = None, pinned_message: "Message" = None, - invoice=None, - successful_payment=None, - connected_website=None): + invoice: "Invoice" = None, + successful_payment: "SuccessfulPayment" = None, + connected_website: str = None): self.message_id = message_id self.date = date self.chat = chat diff --git a/pyrogram/client/types/message_entity.py b/pyrogram/client/types/message_entity.py index bdc686c0..97541ef3 100644 --- a/pyrogram/client/types/message_entity.py +++ b/pyrogram/client/types/message_entity.py @@ -1,13 +1,10 @@ -from . import User - - class MessageEntity: def __init__(self, type: str, offset: int, length: int, url: str = None, - user: User = None): + user: "User" = None): self.type = type self.offset = offset self.length = length diff --git a/pyrogram/client/types/sticker.py b/pyrogram/client/types/sticker.py index 1e87fd84..fd3910de 100644 --- a/pyrogram/client/types/sticker.py +++ b/pyrogram/client/types/sticker.py @@ -1,15 +1,12 @@ -from . import PhotoSize, MaskPosition - - class Sticker: def __init__(self, file_id: str, width: int, height: int, - thumb: PhotoSize = None, + thumb: "PhotoSize" = None, emoji: str = None, set_name: str = None, - mask_position: MaskPosition = None, + mask_position: "MaskPosition" = None, file_size: int = None): self.file_id = file_id self.width = width diff --git a/pyrogram/client/types/update.py b/pyrogram/client/types/update.py index f367afc3..cde53690 100644 --- a/pyrogram/client/types/update.py +++ b/pyrogram/client/types/update.py @@ -1,6 +1,3 @@ -from . import Message - - class Update: """This object represents an incoming update. At most one of the optional parameters can be present in any given update. @@ -18,10 +15,10 @@ class Update: """ def __init__(self, - message: Message = None, - edited_message: Message = None, - channel_post: Message = None, - edited_channel_post: Message = None): + message: "Message" = None, + edited_message: "Message" = None, + channel_post: "Message" = None, + edited_channel_post: "Message" = None): self.message = message self.edited_message = edited_message self.channel_post = channel_post diff --git a/pyrogram/client/types/venue.py b/pyrogram/client/types/venue.py index f1c058fd..2b07f132 100644 --- a/pyrogram/client/types/venue.py +++ b/pyrogram/client/types/venue.py @@ -1,9 +1,6 @@ -from . import Location - - class Venue: def __init__(self, - location: Location, + location: "Location", title: str, address: str, foursquare_id: str = None): diff --git a/pyrogram/client/types/video.py b/pyrogram/client/types/video.py index 37d0a8c8..87731593 100644 --- a/pyrogram/client/types/video.py +++ b/pyrogram/client/types/video.py @@ -1,13 +1,10 @@ -from . import PhotoSize - - class Video: def __init__(self, file_id: str, width: int, height: int, duration: int, - thumb: PhotoSize = None, + thumb: "PhotoSize" = None, mime_type: str = None, file_size: int = None): self.file_id = file_id diff --git a/pyrogram/client/types/video_note.py b/pyrogram/client/types/video_note.py index 3969c2d9..5969624d 100644 --- a/pyrogram/client/types/video_note.py +++ b/pyrogram/client/types/video_note.py @@ -1,12 +1,9 @@ -from . import PhotoSize - - class VideoNote: def __init__(self, file_id: str, length: int, duration: int, - thumb: PhotoSize = None, + thumb: "PhotoSize" = None, file_size: int = None): self.file_id = file_id self.length = length From 8d4242f08eceff64027edf96736425898741f54d Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Mon, 26 Mar 2018 13:47:41 +0200 Subject: [PATCH 026/112] Remove types --- pyrogram/client/types/__init__.py | 22 ----- pyrogram/client/types/animation.py | 12 --- pyrogram/client/types/audio.py | 14 ---- pyrogram/client/types/chat.py | 28 ------- pyrogram/client/types/chat_member.py | 34 -------- pyrogram/client/types/chat_photo.py | 6 -- pyrogram/client/types/contact.py | 10 --- pyrogram/client/types/document.py | 12 --- pyrogram/client/types/game.py | 14 ---- pyrogram/client/types/location.py | 6 -- pyrogram/client/types/mask_position.py | 10 --- pyrogram/client/types/message.py | 86 -------------------- pyrogram/client/types/message_entity.py | 12 --- pyrogram/client/types/photo_size.py | 10 --- pyrogram/client/types/sticker.py | 18 ---- pyrogram/client/types/sticker_set.py | 10 --- pyrogram/client/types/update.py | 25 ------ pyrogram/client/types/user.py | 36 -------- pyrogram/client/types/user_profile_photos.py | 6 -- pyrogram/client/types/venue.py | 10 --- pyrogram/client/types/video.py | 16 ---- pyrogram/client/types/video_note.py | 12 --- pyrogram/client/types/voice.py | 10 --- 23 files changed, 419 deletions(-) delete mode 100644 pyrogram/client/types/__init__.py delete mode 100644 pyrogram/client/types/animation.py delete mode 100644 pyrogram/client/types/audio.py delete mode 100644 pyrogram/client/types/chat.py delete mode 100644 pyrogram/client/types/chat_member.py delete mode 100644 pyrogram/client/types/chat_photo.py delete mode 100644 pyrogram/client/types/contact.py delete mode 100644 pyrogram/client/types/document.py delete mode 100644 pyrogram/client/types/game.py delete mode 100644 pyrogram/client/types/location.py delete mode 100644 pyrogram/client/types/mask_position.py delete mode 100644 pyrogram/client/types/message.py delete mode 100644 pyrogram/client/types/message_entity.py delete mode 100644 pyrogram/client/types/photo_size.py delete mode 100644 pyrogram/client/types/sticker.py delete mode 100644 pyrogram/client/types/sticker_set.py delete mode 100644 pyrogram/client/types/update.py delete mode 100644 pyrogram/client/types/user.py delete mode 100644 pyrogram/client/types/user_profile_photos.py delete mode 100644 pyrogram/client/types/venue.py delete mode 100644 pyrogram/client/types/video.py delete mode 100644 pyrogram/client/types/video_note.py delete mode 100644 pyrogram/client/types/voice.py diff --git a/pyrogram/client/types/__init__.py b/pyrogram/client/types/__init__.py deleted file mode 100644 index f6617387..00000000 --- a/pyrogram/client/types/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -from .animation import Animation -from .audio import Audio -from .chat import Chat -from .chat_member import ChatMember -from .chat_photo import ChatPhoto -from .contact import Contact -from .document import Document -from .game import Game -from .location import Location -from .mask_position import MaskPosition -from .message import Message -from .message_entity import MessageEntity -from .photo_size import PhotoSize -from .sticker import Sticker -from .sticker_set import StickerSet -from .update import Update -from .user import User -from .user_profile_photos import UserProfilePhotos -from .venue import Venue -from .video import Video -from .video_note import VideoNote -from .voice import Voice diff --git a/pyrogram/client/types/animation.py b/pyrogram/client/types/animation.py deleted file mode 100644 index ea04beaf..00000000 --- a/pyrogram/client/types/animation.py +++ /dev/null @@ -1,12 +0,0 @@ -class Animation: - def __init__(self, - file_id: str, - thumb: "PhotoSize" = None, - file_name: str = None, - mime_type: str = None, - file_size: int = None): - self.file_id = file_id - self.thumb = thumb - self.file_name = file_name - self.mime_type = mime_type - self.file_size = file_size diff --git a/pyrogram/client/types/audio.py b/pyrogram/client/types/audio.py deleted file mode 100644 index 10febb4b..00000000 --- a/pyrogram/client/types/audio.py +++ /dev/null @@ -1,14 +0,0 @@ -class Audio: - def __init__(self, - file_id: str, - duration: int = None, - performer: str = None, - title: str = None, - mime_type: str = None, - file_size: int = None): - self.file_id = file_id - self.duration = duration - self.performer = performer - self.title = title - self.mime_type = mime_type - self.file_size = file_size diff --git a/pyrogram/client/types/chat.py b/pyrogram/client/types/chat.py deleted file mode 100644 index 6a09215d..00000000 --- a/pyrogram/client/types/chat.py +++ /dev/null @@ -1,28 +0,0 @@ -class Chat: - def __init__(self, - id: int, - type: str, - title: str = None, - username: str = None, - first_name: str = None, - last_name: str = None, - all_members_are_administrators: bool = None, - photo: "ChatPhoto" = None, - description: str = None, - invite_link: str = None, - pinned_message: "Message" = None, - sticker_set_name: str = None, - can_set_sticker_set: bool = None): - self.id = id - self.type = type - self.title = title - self.username = username - self.first_name = first_name - self.last_name = last_name - self.all_members_are_administrators = all_members_are_administrators - self.photo = photo - self.description = description - self.invite_link = invite_link - self.pinned_message = pinned_message - self.sticker_set_name = sticker_set_name - self.can_set_sticker_set = can_set_sticker_set diff --git a/pyrogram/client/types/chat_member.py b/pyrogram/client/types/chat_member.py deleted file mode 100644 index 2497e10b..00000000 --- a/pyrogram/client/types/chat_member.py +++ /dev/null @@ -1,34 +0,0 @@ -class ChatMember: - def __init__(self, - user: "User", - status: str, - until_date: int = None, - can_be_edited: bool = None, - can_change_info: bool = None, - can_post_messages: bool = None, - can_edit_messages: bool = None, - can_delete_messages: bool = None, - can_invite_users: bool = None, - can_restrict_members: bool = None, - can_pin_messages: bool = None, - can_promote_members: bool = None, - can_send_messages: bool = None, - can_send_media_messages: bool = None, - can_send_other_messages: bool = None, - can_add_web_page_pewviews: bool = None): - self.user = user - self.status = status - self.until_date = until_date - self.can_be_edited = can_be_edited - self.can_change_info = can_change_info - self.can_post_messages = can_post_messages - self.can_edit_messages = can_edit_messages - self.can_delete_messages = can_delete_messages - self.can_invite_users = can_invite_users - self.can_restrict_members = can_restrict_members - self.can_pin_messages = can_pin_messages - self.can_promote_members = can_promote_members - self.can_send_messages = can_send_messages - self.can_send_media_messages = can_send_media_messages - self.can_send_other_messages = can_send_other_messages - self.can_add_web_page_pewviews = can_add_web_page_pewviews diff --git a/pyrogram/client/types/chat_photo.py b/pyrogram/client/types/chat_photo.py deleted file mode 100644 index c2d30bdf..00000000 --- a/pyrogram/client/types/chat_photo.py +++ /dev/null @@ -1,6 +0,0 @@ -class ChatPhoto: - def __init__(self, - small_file_id: str, - big_file_id: str): - self.small_file_id = small_file_id - self.big_file_id = big_file_id diff --git a/pyrogram/client/types/contact.py b/pyrogram/client/types/contact.py deleted file mode 100644 index e07fb441..00000000 --- a/pyrogram/client/types/contact.py +++ /dev/null @@ -1,10 +0,0 @@ -class Contact: - def __init__(self, - phone_number: str, - first_name: str, - last_name: str = None, - user_id: int = None): - self.phone_number = phone_number - self.first_name = first_name - self.last_name = last_name - self.user_id = user_id diff --git a/pyrogram/client/types/document.py b/pyrogram/client/types/document.py deleted file mode 100644 index 49a9c79d..00000000 --- a/pyrogram/client/types/document.py +++ /dev/null @@ -1,12 +0,0 @@ -class Document: - def __init__(self, - file_id: str, - thumb: "PhotoSize" = None, - file_name: str = None, - mime_type: str = None, - file_size: int = None): - self.file_id = file_id - self.thumb = thumb - self.file_name = file_name - self.mime_type = mime_type - self.file_size = file_size diff --git a/pyrogram/client/types/game.py b/pyrogram/client/types/game.py deleted file mode 100644 index 905d9ef5..00000000 --- a/pyrogram/client/types/game.py +++ /dev/null @@ -1,14 +0,0 @@ -class Game: - def __init__(self, - title: str, - description: str, - photo: list, - text: str = None, - text_entities: list = None, - animation: "Animation" = None): - self.title = title - self.description = description - self.photo = photo - self.text = text - self.text_entities = text_entities - self.animation = animation diff --git a/pyrogram/client/types/location.py b/pyrogram/client/types/location.py deleted file mode 100644 index 5252e67f..00000000 --- a/pyrogram/client/types/location.py +++ /dev/null @@ -1,6 +0,0 @@ -class Location: - def __init__(self, - longitude: float, - latitude: float): - self.longitude = longitude - self.latitude = latitude diff --git a/pyrogram/client/types/mask_position.py b/pyrogram/client/types/mask_position.py deleted file mode 100644 index a7720dc7..00000000 --- a/pyrogram/client/types/mask_position.py +++ /dev/null @@ -1,10 +0,0 @@ -class MaskPosition: - def __init__(self, - point: str, - x_shift: float, - y_shift: float, - scale: float): - self.point = point - self.x_shift = x_shift - self.y_shift = y_shift - self.scale = scale diff --git a/pyrogram/client/types/message.py b/pyrogram/client/types/message.py deleted file mode 100644 index 0f7a9167..00000000 --- a/pyrogram/client/types/message.py +++ /dev/null @@ -1,86 +0,0 @@ -class Message: - def __init__(self, - message_id: int, - date: int, - chat: "Chat", - from_user: "User" = None, - forward_from: "User" = None, - forward_from_chat: "Chat" = None, - forward_from_message_id: int = None, - forward_signature: str = None, - forward_date: int = None, - reply_to_message: "Message" = None, - edit_date: int = None, - media_group_id: str = None, - author_signature: str = None, - text: str = None, - entities: list = None, - caption_entities: list = None, - audio: "Audio" = None, - document: "Document" = None, - game: "Game" = None, - photo: list = None, - sticker: "Sticker" = None, - video: "Video" = None, - voice: "Voice" = None, - video_note: "VideoNote" = None, - caption: str = None, - contact: "Contact" = None, - location: "Location" = None, - venue: "Venue" = None, - new_chat_members: list = None, - left_chat_member: "User" = None, - new_chat_title: str = None, - new_chat_photo: list = None, - delete_chat_photo: bool = None, - group_chat_created: bool = None, - supergroup_chat_created: bool = None, - channel_chat_created: bool = None, - migrate_to_chat_id: int = None, - migrate_from_chat_id: int = None, - pinned_message: "Message" = None, - invoice: "Invoice" = None, - successful_payment: "SuccessfulPayment" = None, - connected_website: str = None): - self.message_id = message_id - self.date = date - self.chat = chat - self.from_user = from_user - self.forward_from = forward_from - self.forward_from_chat = forward_from_chat - self.forward_from_message_id = forward_from_message_id - self.forward_signature = forward_signature - self.forward_date = forward_date - self.reply_to_message = reply_to_message - self.edit_date = edit_date - self.media_group_id = media_group_id - self.author_signature = author_signature - self.text = text - self.entities = entities - self.caption_entities = caption_entities - self.audio = audio - self.document = document - self.game = game - self.photo = photo - self.sticker = sticker - self.video = video - self.voice = voice - self.video_note = video_note - self.caption = caption - self.contact = contact - self.location = location - self.venue = venue - self.new_chat_members = new_chat_members - self.left_chat_member = left_chat_member - self.new_chat_title = new_chat_title - self.new_chat_photo = new_chat_photo - self.delete_chat_photo = delete_chat_photo - self.group_chat_created = group_chat_created - self.supergroup_chat_created = supergroup_chat_created - self.channel_chat_created = channel_chat_created - self.migrate_to_chat_id = migrate_to_chat_id - self.migrate_from_chat_id = migrate_from_chat_id - self.pinned_message = pinned_message - self.invoice = invoice - self.successful_payment = successful_payment - self.connected_website = connected_website diff --git a/pyrogram/client/types/message_entity.py b/pyrogram/client/types/message_entity.py deleted file mode 100644 index 97541ef3..00000000 --- a/pyrogram/client/types/message_entity.py +++ /dev/null @@ -1,12 +0,0 @@ -class MessageEntity: - def __init__(self, - type: str, - offset: int, - length: int, - url: str = None, - user: "User" = None): - self.type = type - self.offset = offset - self.length = length - self.url = url - self.user = user diff --git a/pyrogram/client/types/photo_size.py b/pyrogram/client/types/photo_size.py deleted file mode 100644 index 3ada21d7..00000000 --- a/pyrogram/client/types/photo_size.py +++ /dev/null @@ -1,10 +0,0 @@ -class PhotoSize: - def __init__(self, - file_id: str, - width: int, - height: int, - file_size: int): - self.file_id = file_id - self.width = width - self.height = height - self.file_size = file_size diff --git a/pyrogram/client/types/sticker.py b/pyrogram/client/types/sticker.py deleted file mode 100644 index fd3910de..00000000 --- a/pyrogram/client/types/sticker.py +++ /dev/null @@ -1,18 +0,0 @@ -class Sticker: - def __init__(self, - file_id: str, - width: int, - height: int, - thumb: "PhotoSize" = None, - emoji: str = None, - set_name: str = None, - mask_position: "MaskPosition" = None, - file_size: int = None): - self.file_id = file_id - self.width = width - self.height = height - self.thumb = thumb - self.emoji = emoji - self.set_name = set_name - self.mask_position = mask_position - self.file_size = file_size diff --git a/pyrogram/client/types/sticker_set.py b/pyrogram/client/types/sticker_set.py deleted file mode 100644 index 2970a111..00000000 --- a/pyrogram/client/types/sticker_set.py +++ /dev/null @@ -1,10 +0,0 @@ -class StickerSet: - def __init__(self, - name: str, - title: str, - contain_masks: bool, - stickers: list): - self.name = name - self.title = title - self.contain_masks = contain_masks - self.stickers = stickers diff --git a/pyrogram/client/types/update.py b/pyrogram/client/types/update.py deleted file mode 100644 index cde53690..00000000 --- a/pyrogram/client/types/update.py +++ /dev/null @@ -1,25 +0,0 @@ -class Update: - """This object represents an incoming update. - At most one of the optional parameters can be present in any given update. - - - Args: - message (:obj:`Message `): - - edited_message (): - - channel_post (): - - edited_channel_post (): - - """ - - def __init__(self, - message: "Message" = None, - edited_message: "Message" = None, - channel_post: "Message" = None, - edited_channel_post: "Message" = None): - self.message = message - self.edited_message = edited_message - self.channel_post = channel_post - self.edited_channel_post = edited_channel_post diff --git a/pyrogram/client/types/user.py b/pyrogram/client/types/user.py deleted file mode 100644 index eee9113d..00000000 --- a/pyrogram/client/types/user.py +++ /dev/null @@ -1,36 +0,0 @@ -class User: - """This object represents a Telegram user or bot. - - Args: - id (:obj:`int`): - Unique identifier for this user or bot. - - is_bot (:obj:`bool`): - True, if this user is a bot. - - first_name (:obj:`str`): - User's or bot’s first name. - - last_name (:obj:`str`, optional): - User's or bot’s last name. - - username (:obj:`str`, optional): - User's or bot’s username. - - language_code (:obj:`str`, optional): - IETF language tag of the user's language. - """ - - def __init__(self, - id: int, - is_bot: bool, - first_name: str, - last_name: str = None, - username: str = None, - language_code: str = None): - self.id = id - self.is_bot = is_bot - self.first_name = first_name - self.last_name = last_name - self.username = username - self.language_code = language_code diff --git a/pyrogram/client/types/user_profile_photos.py b/pyrogram/client/types/user_profile_photos.py deleted file mode 100644 index f22296d2..00000000 --- a/pyrogram/client/types/user_profile_photos.py +++ /dev/null @@ -1,6 +0,0 @@ -class UserProfilePhotos: - def __init__(self, - total_count: int, - photos: list): - self.total_count = total_count - self.photos = photos diff --git a/pyrogram/client/types/venue.py b/pyrogram/client/types/venue.py deleted file mode 100644 index 2b07f132..00000000 --- a/pyrogram/client/types/venue.py +++ /dev/null @@ -1,10 +0,0 @@ -class Venue: - def __init__(self, - location: "Location", - title: str, - address: str, - foursquare_id: str = None): - self.location = location - self.title = title - self.address = address - self.foursquare_id = foursquare_id diff --git a/pyrogram/client/types/video.py b/pyrogram/client/types/video.py deleted file mode 100644 index 87731593..00000000 --- a/pyrogram/client/types/video.py +++ /dev/null @@ -1,16 +0,0 @@ -class Video: - def __init__(self, - file_id: str, - width: int, - height: int, - duration: int, - thumb: "PhotoSize" = None, - mime_type: str = None, - file_size: int = None): - self.file_id = file_id - self.width = width - self.height = height - self.duration = duration - self.thumb = thumb - self.mime_type = mime_type - self.file_size = file_size diff --git a/pyrogram/client/types/video_note.py b/pyrogram/client/types/video_note.py deleted file mode 100644 index 5969624d..00000000 --- a/pyrogram/client/types/video_note.py +++ /dev/null @@ -1,12 +0,0 @@ -class VideoNote: - def __init__(self, - file_id: str, - length: int, - duration: int, - thumb: "PhotoSize" = None, - file_size: int = None): - self.file_id = file_id - self.length = length - self.duration = duration - self.thumb = thumb - self.file_size = file_size diff --git a/pyrogram/client/types/voice.py b/pyrogram/client/types/voice.py deleted file mode 100644 index d21559f0..00000000 --- a/pyrogram/client/types/voice.py +++ /dev/null @@ -1,10 +0,0 @@ -class Voice: - def __init__(self, - file_id: str, - duration: int, - mime_type: str = None, - file_size: int = None): - self.file_id = file_id - self.duration = duration - self.mime_type = mime_type - self.file_size = file_size From 74d1d99697c859272c4b3f8451efdcf9e24c36c8 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Mon, 26 Mar 2018 13:51:56 +0200 Subject: [PATCH 027/112] Update compiler --- compiler/api/compiler.py | 258 ++++++++++++++++---------------- compiler/api/template/class.txt | 3 +- 2 files changed, 130 insertions(+), 131 deletions(-) diff --git a/compiler/api/compiler.py b/compiler/api/compiler.py index e1e95328..a62e7bbe 100644 --- a/compiler/api/compiler.py +++ b/compiler/api/compiler.py @@ -22,20 +22,87 @@ import shutil HOME = "compiler/api" DESTINATION = "pyrogram/api" -notice_path = "NOTICE" +NOTICE_PATH = "NOTICE" SECTION_RE = re.compile(r"---(\w+)---") LAYER_RE = re.compile(r"//\sLAYER\s(\d+)") -COMBINATOR_RE = re.compile(r"^([\w.]+)#([0-9a-f]+)\s(?:.*)=\s([\w<>.]+);$", re.MULTILINE) +COMBINATOR_RE = re.compile(r"^([\w.]+)#([0-9a-f]+)\s(?:.*)=\s([\w<>.]+);(?: // Docs: (.+))?$", re.MULTILINE) ARGS_RE = re.compile("[^{](\w+):([\w?!.<>]+)") FLAGS_RE = re.compile(r"flags\.(\d+)\?") FLAGS_RE_2 = re.compile(r"flags\.(\d+)\?([\w<>.]+)") FLAGS_RE_3 = re.compile(r"flags:#") +INT_RE = re.compile(r"int(\d+)") core_types = ["int", "long", "int128", "int256", "double", "bytes", "string", "Bool"] +types_to_constructors = {} +types_to_functions = {} +constructors_to_functions = {} + + +def get_docstring_arg_type(t: str, is_list: bool = False): + if t in core_types: + if t == "long": + return ":obj:`int` :obj:`64-bit`" + elif "int" in t: + size = INT_RE.match(t) + return ":obj:`int` :obj:`{}-bit`".format(size.group(1)) if size else ":obj:`int` :obj:`32-bit`" + elif t == "double": + return ":obj:`float` :obj:`64-bit`" + elif t == "string": + return ":obj:`str`" + else: + return ":obj:`{}`".format(t.lower()) + elif t == "true": + return ":obj:`bool`" + elif t == "Object" or t == "X": + return "Any type from :obj:`pyrogram.api.types`" + elif t == "!X": + return "Any method from :obj:`pyrogram.api.functions`" + elif t.startswith("Vector"): + return "List of " + get_docstring_arg_type(t.split("<")[1][:-1], is_list=True) + else: + t = types_to_constructors.get(t, [t]) + n = len(t) - 1 + + t = (("e" if is_list else "E") + "ither " if n else "") + ", ".join( + ":obj:`{0} `".format(i) + for i in t + ) + + if n: + t = t.split(", ") + t = ", ".join(t[:-1]) + " or " + t[-1] + + return t + + +def get_references(t: str): + t = constructors_to_functions.get(t) + + if t: + n = len(t) - 1 + + t = ", ".join( + ":obj:`{0} `".format(i) + for i in t + ) + + if n: + t = t.split(", ") + t = ", ".join(t[:-1]) + " and " + t[-1] + + return t class Combinator: - def __init__(self, section: str, namespace: str, name: str, id: str, args: list, has_flags: bool, return_type: str): + def __init__(self, + section: str, + namespace: str, + name: str, + id: str, + args: list, + has_flags: bool, + return_type: str, + docs: str): self.section = section self.namespace = namespace self.name = name @@ -43,6 +110,7 @@ class Combinator: self.args = args self.has_flags = has_flags self.return_type = return_type + self.docs = docs def snek(s: str): @@ -72,13 +140,14 @@ def start(): with open("{}/source/auth_key.tl".format(HOME), encoding="utf-8") as auth, \ open("{}/source/sys_msgs.tl".format(HOME), encoding="utf-8") as system, \ - open("{}/source/main_api.tl".format(HOME), encoding="utf-8") as api: - schema = (auth.read() + system.read() + api.read()).splitlines() + open("{}/source/main_api.tl".format(HOME), encoding="utf-8") as api, \ + open("{}/source/pyrogram.tl".format(HOME), encoding="utf-8") as pyrogram: + schema = (auth.read() + system.read() + api.read() + pyrogram.read()).splitlines() with open("{}/template/class.txt".format(HOME), encoding="utf-8") as f: template = f.read() - with open(notice_path, encoding="utf-8") as f: + with open(NOTICE_PATH, encoding="utf-8") as f: notice = [] for line in f.readlines(): @@ -106,9 +175,9 @@ def start(): combinator = COMBINATOR_RE.match(line) if combinator: - name, id, return_type = combinator.groups() + name, id, return_type, docs = combinator.groups() namespace, name = name.split(".") if "." in name else ("", name) - args = ARGS_RE.findall(line) + args = ARGS_RE.findall(line.split(" //")[0]) # Pingu! has_flags = not not FLAGS_RE_3.findall(line) @@ -129,23 +198,37 @@ def start(): Combinator( section, namespace, - name, + capit(name), "0x{}".format(id.zfill(8)), args, has_flags, - return_type + ".".join( + return_type.split(".")[:-1] + + [capit(return_type.split(".")[-1])] + ), + docs ) ) - by_types = {} for c in combinators: - return_type = capit(c.return_type) + return_type = c.return_type - if c.section == "types": - if return_type not in by_types: - by_types[return_type] = [] + if return_type.startswith("Vector"): + return_type = return_type.split("<")[1][:-1] - by_types[return_type].append(".".join(filter(None, [c.namespace, capit(c.name)]))) + d = types_to_constructors if c.section == "types" else types_to_functions + + if return_type not in d: + d[return_type] = [] + + d[return_type].append(".".join(filter(None, [c.namespace, c.name]))) + + for k, v in types_to_constructors.items(): + for i in v: + try: + constructors_to_functions[i] = types_to_functions[k] + except KeyError: + pass total = len(combinators) current = 0 @@ -185,132 +268,45 @@ def start(): for i, arg in enumerate(sorted_args): arg_name, arg_type = arg - is_optional = arg_type.startswith("flags.") + is_optional = FLAGS_RE.match(arg_type) + flag_number = is_optional.group(1) if is_optional else -1 arg_type = arg_type.split("?")[-1] - if arg_type in core_types: - if "int" in arg_type or arg_type == "long": - arg_type = ":obj:`int`" - elif arg_type == "double": - arg_type = ":obj:`float`" - else: - arg_type = ":obj:`{}`".format(arg_type.lower()) - elif arg_type == "true": - arg_type = ":obj:`bool`" - else: - if arg_type.startswith("Vector"): - sub_type = arg_type.split("<")[1][:-1] + docs = c.docs.split("|")[1:] if c.docs else None - if sub_type in core_types: - if "int" in sub_type or sub_type == "long": - arg_type = "List of :obj:`int`" - elif sub_type == "double": - arg_type = "List of :obj:`float`" - else: - arg_type = "List of :obj:`{}`".format(sub_type.lower()) - else: - arg_type = "List of :class:`pyrogram.api.types.{}`".format( - ".".join( - sub_type.split(".")[:-1] - + [capit(sub_type.split(".")[-1])] - ) - ) - else: - arg_type = ":class:`pyrogram.api.types.{}`".format( - ".".join( - arg_type.split(".")[:-1] - + [capit(arg_type.split(".")[-1])] - ) + if docs: + docstring_args.append( + "{} ({}{}):\n {}\n".format( + arg_name, + get_docstring_arg_type(arg_type), + ", optional" if "Optional" in docs[i] else "", + re.sub("Optional\. ", "", docs[i].split(":")[1]) + ) + ) + else: + docstring_args.append( + "{}: {}{}".format( + arg_name, + "``optional`` ".format(flag_number) if is_optional else "", + get_docstring_arg_type(arg_type) ) - - docstring_args.append( - "{}: {}{}".format( - arg_name, - arg_type, - " (optional)" if is_optional else "" ) - ) if docstring_args: docstring_args = "Args:\n " + "\n ".join(docstring_args) else: docstring_args = "No parameters required." - docstring_args = "Attributes:\n ID (:obj:`int`): ``{}``\n\n ".format(c.id) + docstring_args + docstring_args = "Attributes:\n ID: ``{}``\n\n ".format(c.id) + docstring_args if c.section == "functions": - docstring_args += "\n\n Returns:\n " - if c.return_type in core_types: - if "int" in c.return_type or c.return_type == "long": - return_type = ":obj:`int`" - elif c.return_type == "double": - return_type = ":obj:`float`" - else: - return_type = ":obj:`{}`".format(c.return_type.lower()) - else: - if c.return_type.startswith("Vector"): - sub_type = c.return_type.split("<")[1][:-1] + docstring_args += "\n\n Raises:\n :obj:`pyrogram.Error`" + docstring_args += "\n\n Returns:\n " + get_docstring_arg_type(c.return_type) + else: + references = get_references(".".join(filter(None, [c.namespace, c.name]))) - if sub_type in core_types: - if "int" in sub_type or sub_type == "long": - return_type = "List of :obj:`int`" - elif sub_type == "double": - return_type = "List of :obj:`float`" - else: - return_type = "List of :obj:`{}`".format(c.return_type.lower()) - else: - if c.section == "functions": - try: - constructors = by_types[capit(sub_type)] - except KeyError: - return_type = "List of :class:`pyrogram.api.types.{}`".format( - ".".join( - sub_type.split(".")[:-1] - + [capit(sub_type.split(".")[-1])] - ) - ) - else: - constructors = ["List of :class:`pyrogram.api.types.{}`".format( - ".".join( - i.split(".")[:-1] - + [capit(i.split(".")[-1])] - ) - ) for i in constructors] - - return_type = " | ".join(constructors) - else: - return_type = "List of :class:`pyrogram.api.types.{}`".format( - ".".join( - sub_type.split(".")[:-1] - + [capit(sub_type.split(".")[-1])] - ) - ) - else: - if c.section == "functions": - try: - constructors = by_types[capit(c.return_type)] - except KeyError: - return_type = ":class:`pyrogram.api.types.{}`".format( - ".".join(filter(None, [c.namespace, capit(c.name)])) - ) - else: - constructors = [":class:`pyrogram.api.types.{}`".format( - ".".join( - i.split(".")[:-1] - + [capit(i.split(".")[-1])] - ) - ) for i in constructors] - - return_type = " | ".join(constructors) - else: - return_type = ":class:`pyrogram.api.types.{}`".format( - ".".join(filter(None, [c.namespace, capit(c.name)])) - ) - - docstring_args += return_type - - if c.section == "functions": - docstring_args += "\n\n Raises:\n :class:`pyrogram.Error`" + if references: + docstring_args += "\n\n See Also:\n This type can be returned by " + references + "." if c.has_flags: write_flags = [] @@ -397,6 +393,10 @@ def start(): read_types += "\n " read_types += "{} = Object.read(b)\n ".format(arg_name) + if c.docs: + description = c.docs.split("|")[0].split(":")[1] + docstring_args = description + "\n\n " + docstring_args + with open("{}/{}.py".format(path, snek(c.name)), "w", encoding="utf-8") as f: f.write( template.format( @@ -443,5 +443,5 @@ def start(): if "__main__" == __name__: HOME = "." DESTINATION = "../../pyrogram/api" - notice_path = "../../NOTICE" + NOTICE_PATH = "../../NOTICE" start() diff --git a/compiler/api/template/class.txt b/compiler/api/template/class.txt index d29caf05..81c99062 100644 --- a/compiler/api/template/class.txt +++ b/compiler/api/template/class.txt @@ -6,8 +6,7 @@ from pyrogram.api.core import * class {class_name}(Object): - """ - {docstring_args} + """{docstring_args} """ ID = {object_id} From 3441db17b073f56444a87d7c25affaadbb9c051f Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Mon, 26 Mar 2018 15:15:31 +0200 Subject: [PATCH 028/112] Add pyrogram tl schema --- compiler/api/source/pyrogram.tl | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 compiler/api/source/pyrogram.tl diff --git a/compiler/api/source/pyrogram.tl b/compiler/api/source/pyrogram.tl new file mode 100644 index 00000000..911d3ed5 --- /dev/null +++ b/compiler/api/source/pyrogram.tl @@ -0,0 +1,21 @@ +// Pyrogram + +---types--- + +pyrogram.update#b0700000 flags:# update_id:int message:flags.0?Message edited_message:flags.1?Message channel_post:flags.2?Message edited_channel_post:flags.3?Message inline_query:flags.4?InlineQuery chosen_inline_result:flags.5?ChosenInlineResult callback_query:flags.6?CallbackQuery shipping_query:flags.7?ShippingQuery pre_checkout_query:flags.8?PreCheckoutQuery = pyrogram.Update; // Docs: Update:This object represents an incoming update.At most one of the optional parameters can be present in any given update.|update_id:The update's unique identifier. Update identifiers start from a certain positive number and increase sequentially. This ID becomes especially handy if you're using Webhooks, since it allows you to ignore repeated updates or to restore the correct update sequence, should they get out of order. If there are no new updates for at least a week, then identifier of the next update will be chosen randomly instead of sequentially.|message:Optional. New incoming message of any kind � text, photo, sticker, etc.|edited_message:Optional. New version of a message that is known to the bot and was edited|channel_post:Optional. New incoming channel post of any kind � text, photo, sticker, etc.|edited_channel_post:Optional. New version of a channel post that is known to the bot and was edited|inline_query:Optional. New incoming inline query|chosen_inline_result:Optional. The result of an inline query that was chosen by a user and sent to their chat partner. Please see our documentation on the feedback collecting for details on how to enable these updates for your bot.|callback_query:Optional. New incoming callback query|shipping_query:Optional. New incoming shipping query. Only for invoices with flexible price|pre_checkout_query:Optional. New incoming pre-checkout query. Contains full information about checkout +pyrogram.user#b0700001 flags:# id:int is_bot:Bool first_name:string last_name:flags.0?string username:flags.1?string language_code:flags.2?string = pyrogram.User; // Docs: User:This object represents a Telegram user or bot.|id:Unique identifier for this user or bot|is_bot:True, if this user is a bot|first_name:User's or bot's first name|last_name:Optional. User's or bot's last name|username:Optional. User's or bot's username|language_code:Optional. IETF language tag of the user's language +pyrogram.chat#b0700002 flags:# id:int type:string title:flags.0?string username:flags.1?string first_name:flags.2?string last_name:flags.3?string all_members_are_administrators:flags.4?Bool photo:flags.5?ChatPhoto description:flags.6?string invite_link:flags.7?string pinned_message:flags.8?Message sticker_set_name:flags.9?string can_set_sticker_set:flags.10?Bool = pyrogram.Chat; // Docs: Chat:This object represents a chat.|id:Unique identifier for this chat. This number may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier.|type:Type of chat, can be either "private", "group", "supergroup" or "channel"|title:Optional. Title, for supergroups, channels and group chats|username:Optional. Username, for private chats, supergroups and channels if available|first_name:Optional. First name of the other party in a private chat|last_name:Optional. Last name of the other party in a private chat|all_members_are_administrators:Optional. True if a group has 'All Members Are Admins' enabled.|photo:Optional. Chat photo. Returned only in getChat.|description:Optional. Description, for supergroups and channel chats. Returned only in getChat.|invite_link:Optional. Chat invite link, for supergroups and channel chats. Returned only in getChat.|pinned_message:Optional. Pinned message, for supergroups and channel chats. Returned only in getChat.|sticker_set_name:Optional. For supergroups, name of group sticker set. Returned only in getChat.|can_set_sticker_set:Optional. True, if the bot can change the group sticker set. Returned only in getChat. +pyrogram.message#b0700003 flags:# message_id:int from_user:flags.0?User date:int chat:Chat forward_from:flags.1?User forward_from_chat:flags.2?Chat forward_from_message_id:flags.3?int forward_signature:flags.4?string forward_date:flags.5?int reply_to_message:flags.6?Message edit_date:flags.7?int media_group_id:flags.8?string author_signature:flags.9?string text:flags.10?string entities:flags.11?Vector caption_entities:flags.12?Vector audio:flags.13?Audio document:flags.14?Document game:flags.15?Game photo:flags.16?Vector sticker:flags.17?Sticker video:flags.18?Video voice:flags.19?Voice video_note:flags.20?VideoNote caption:flags.21?string contact:flags.22?Contact location:flags.23?Location venue:flags.24?Venue new_chat_members:flags.25?Vector left_chat_member:flags.26?User new_chat_title:flags.27?string new_chat_photo:flags.28?Vector delete_chat_photo:flags.29?true group_chat_created:flags.30?true supergroup_chat_created:flags.31?true channel_chat_created:flags.32?true migrate_to_chat_id:flags.33?int migrate_from_chat_id:flags.34?int pinned_message:flags.35?Message invoice:flags.36?Invoice successful_payment:flags.37?SuccessfulPayment connected_website:flags.38?string = pyrogram.Message; // Docs: Message:This object represents a message.|message_id:Unique message identifier inside this chat|from_user:Optional. Sender, empty for messages sent to channels|date:Date the message was sent in Unix time|chat:Conversation the message belongs to|forward_from:Optional. For forwarded messages, sender of the original message|forward_from_chat:Optional. For messages forwarded from channels, information about the original channel|forward_from_message_id:Optional. For messages forwarded from channels, identifier of the original message in the channel|forward_signature:Optional. For messages forwarded from channels, signature of the post author if present|forward_date:Optional. For forwarded messages, date the original message was sent in Unix time|reply_to_message:Optional. For replies, the original message. Note that the Message object in this field will not contain further reply_to_message fields even if it itself is a reply.|edit_date:Optional. Date the message was last edited in Unix time|media_group_id:Optional. The unique identifier of a media message group this message belongs to|author_signature:Optional. Signature of the post author for messages in channels|text:Optional. For text messages, the actual UTF-8 text of the message, 0-4096 characters.|entities:Optional. For text messages, special entities like usernames, URLs, bot commands, etc. that appear in the text|caption_entities:Optional. For messages with a caption, special entities like usernames, URLs, bot commands, etc. that appear in the caption|audio:Optional. Message is an audio file, information about the file|document:Optional. Message is a general file, information about the file|game:Optional. Message is a game, information about the game. More about games|photo:Optional. Message is a photo, available sizes of the photo|sticker:Optional. Message is a sticker, information about the sticker|video:Optional. Message is a video, information about the video|voice:Optional. Message is a voice message, information about the file|video_note:Optional. Message is a video note, information about the video message|caption:Optional. Caption for the audio, document, photo, video or voice, 0-200 characters|contact:Optional. Message is a shared contact, information about the contact|location:Optional. Message is a shared location, information about the location|venue:Optional. Message is a venue, information about the venue|new_chat_members:Optional. New members that were added to the group or supergroup and information about them (the bot itself may be one of these members)|left_chat_member:Optional. A member was removed from the group, information about them (this member may be the bot itself)|new_chat_title:Optional. A chat title was changed to this value|new_chat_photo:Optional. A chat photo was change to this value|delete_chat_photo:Optional. Service message: the chat photo was deleted|group_chat_created:Optional. Service message: the group has been created|supergroup_chat_created:Optional. Service message: the supergroup has been created. This field can't be received in a message coming through updates, because bot can't be a member of a supergroup when it is created. It can only be found in reply_to_message if someone replies to a very first message in a directly created supergroup.|channel_chat_created:Optional. Service message: the channel has been created. This field can't be received in a message coming through updates, because bot can't be a member of a channel when it is created. It can only be found in reply_to_message if someone replies to a very first message in a channel.|migrate_to_chat_id:Optional. The group has been migrated to a supergroup with the specified identifier. This number may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier.|migrate_from_chat_id:Optional. The supergroup has been migrated from a group with the specified identifier. This number may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier.|pinned_message:Optional. Specified message was pinned. Note that the Message object in this field will not contain further reply_to_message fields even if it is itself a reply.|invoice:Optional. Message is an invoice for a payment, information about the invoice. More about payments|successful_payment:Optional. Message is a service message about a successful payment, information about the payment. More about payments|connected_website:Optional. The domain name of the website on which the user has logged in. More about Telegram Login +pyrogram.messageEntity#b0700004 flags:# type:string offset:int length:int url:flags.0?string user:flags.1?User = pyrogram.MessageEntity; // Docs: MessageEntity:This object represents one special entity in a text message. For example, hashtags, usernames, URLs, etc.|type:Type of the entity. Can be mention (@username), hashtag, bot_command, url, email, bold (bold text), italic (italic text), code (monowidth string), pre (monowidth block), text_link (for clickable text URLs), text_mention (for users without usernames)|offset:Offset in UTF-16 code units to the start of the entity|length:Length of the entity in UTF-16 code units|url:Optional. For "text_link" only, url that will be opened after user taps on the text|user:Optional. For "text_mention" only, the mentioned user +pyrogram.photoSize#b0700005 flags:# file_id:string width:int height:int file_size:flags.0?int = pyrogram.PhotoSize; // Docs: PhotoSize:This object represents one size of a photo or a file / sticker thumbnail.|file_id:Unique identifier for this file|width:Photo width|height:Photo height|file_size:Optional. File size +pyrogram.audio#b0700006 flags:# file_id:string duration:int performer:flags.0?string title:flags.1?string mime_type:flags.2?string file_size:flags.3?int = pyrogram.Audio; // Docs: Audio:This object represents an audio file to be treated as music by the Telegram clients.|file_id:Unique identifier for this file|duration:Duration of the audio in seconds as defined by sender|performer:Optional. Performer of the audio as defined by sender or by audio tags|title:Optional. Title of the audio as defined by sender or by audio tags|mime_type:Optional. MIME type of the file as defined by sender|file_size:Optional. File size +pyrogram.document#b0700007 flags:# file_id:string thumb:flags.0?PhotoSize file_name:flags.1?string mime_type:flags.2?string file_size:flags.3?int = pyrogram.Document; // Docs: Document:This object represents a general file (as opposed to photos, voice messages and audio files).|file_id:Unique file identifier|thumb:Optional. Document thumbnail as defined by sender|file_name:Optional. Original filename as defined by sender|mime_type:Optional. MIME type of the file as defined by sender|file_size:Optional. File size +pyrogram.video#b0700008 flags:# file_id:string width:int height:int duration:int thumb:flags.0?PhotoSize mime_type:flags.1?string file_size:flags.2?int = pyrogram.Video; // Docs: Video:This object represents a video file.|file_id:Unique identifier for this file|width:Video width as defined by sender|height:Video height as defined by sender|duration:Duration of the video in seconds as defined by sender|thumb:Optional. Video thumbnail|mime_type:Optional. Mime type of a file as defined by sender|file_size:Optional. File size +pyrogram.voice#b0700009 flags:# file_id:string duration:int mime_type:flags.0?string file_size:flags.1?int = pyrogram.Voice; // Docs: Voice:This object represents a voice note.|file_id:Unique identifier for this file|duration:Duration of the audio in seconds as defined by sender|mime_type:Optional. MIME type of the file as defined by sender|file_size:Optional. File size +pyrogram.videoNote#b0700010 flags:# file_id:string length:int duration:int thumb:flags.0?PhotoSize file_size:flags.1?int = pyrogram.VideoNote; // Docs: VideoNote:This object represents a video message (available in Telegram apps as of v.4.0).|file_id:Unique identifier for this file|length:Video width and height as defined by sender|duration:Duration of the video in seconds as defined by sender|thumb:Optional. Video thumbnail|file_size:Optional. File size +pyrogram.contact#b0700011 flags:# phone_number:string first_name:string last_name:flags.0?string user_id:flags.1?int = pyrogram.Contact; // Docs: Contact:This object represents a phone contact.|phone_number:Contact's phone number|first_name:Contact's first name|last_name:Optional. Contact's last name|user_id:Optional. Contact's user identifier in Telegram +pyrogram.location#b0700012 longitude:double latitude:double = pyrogram.Location; // Docs: Location:This object represents a point on the map.|longitude:Longitude as defined by sender|latitude:Latitude as defined by sender +pyrogram.venue#b0700013 flags:# location:Location title:string address:string foursquare_id:flags.0?string = pyrogram.Venue; // Docs: Venue:This object represents a venue.|location:Venue location|title:Name of the venue|address:Address of the venue|foursquare_id:Optional. Foursquare identifier of the venue +pyrogram.userProfilePhotos#b0700014 total_count:int photos:Vector> = pyrogram.UserProfilePhotos; // Docs: UserProfilePhotos:This object represent a user's profile pictures.|total_count:Total number of profile pictures the target user has|photos:Requested profile pictures (in up to 4 sizes each) +pyrogram.chatPhoto#b0700015 small_file_id:string big_file_id:string = pyrogram.ChatPhoto; // Docs: ChatPhoto:This object represents a chat photo.|small_file_id:Unique file identifier of small (160x160) chat photo. This file_id can be used only for photo download.|big_file_id:Unique file identifier of big (640x640) chat photo. This file_id can be used only for photo download. +pyrogram.chatMember#b0700016 flags:# user:User status:string until_date:flags.0?int can_be_edited:flags.1?Bool can_change_info:flags.2?Bool can_post_messages:flags.3?Bool can_edit_messages:flags.4?Bool can_delete_messages:flags.5?Bool can_invite_users:flags.6?Bool can_restrict_members:flags.7?Bool can_pin_messages:flags.8?Bool can_promote_members:flags.9?Bool can_send_messages:flags.10?Bool can_send_media_messages:flags.11?Bool can_send_other_messages:flags.12?Bool can_add_web_page_previews:flags.13?Bool = pyrogram.ChatMember; // Docs: ChatMember:This object contains information about one member of a chat.|user:Information about the user|status:The member's status in the chat. Can be "creator", "administrator", "member", "restricted", "left" or "kicked"|until_date:Optional. Restricted and kicked only. Date when restrictions will be lifted for this user, unix time|can_be_edited:Optional. Administrators only. True, if the bot is allowed to edit administrator privileges of that user|can_change_info:Optional. Administrators only. True, if the administrator can change the chat title, photo and other settings|can_post_messages:Optional. Administrators only. True, if the administrator can post in the channel, channels only|can_edit_messages:Optional. Administrators only. True, if the administrator can edit messages of other users and can pin messages, channels only|can_delete_messages:Optional. Administrators only. True, if the administrator can delete messages of other users|can_invite_users:Optional. Administrators only. True, if the administrator can invite new users to the chat|can_restrict_members:Optional. Administrators only. True, if the administrator can restrict, ban or unban chat members|can_pin_messages:Optional. Administrators only. True, if the administrator can pin messages, supergroups only|can_promote_members:Optional. Administrators only. 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 the user)|can_send_messages:Optional. Restricted only. True, if the user can send text messages, contacts, locations and venues|can_send_media_messages:Optional. Restricted only. True, if the user can send audios, documents, photos, videos, video notes and voice notes, implies can_send_messages|can_send_other_messages:Optional. Restricted only. True, if the user can send animations, games, stickers and use inline bots, implies can_send_media_messages|can_add_web_page_previews:Optional. Restricted only. True, if user may add web page previews to his messages, implies can_send_media_messages From 676f9feba8b7a67f51360b04f535dbdb9b930e09 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Mon, 26 Mar 2018 15:16:09 +0200 Subject: [PATCH 029/112] Strip None fields away --- pyrogram/api/core/object.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/pyrogram/api/core/object.py b/pyrogram/api/core/object.py index bec9d015..a74eb876 100644 --- a/pyrogram/api/core/object.py +++ b/pyrogram/api/core/object.py @@ -47,6 +47,15 @@ class Object: pass +def remove_none(obj): + if isinstance(obj, (list, tuple, set)): + return type(obj)(remove_none(x) for x in obj if x is not None) + elif isinstance(obj, dict): + return type(obj)((remove_none(k), remove_none(v)) for k, v in obj.items() if k is not None and v is not None) + else: + return obj + + class Encoder(JSONEncoder): def default(self, o: Object): try: @@ -57,7 +66,10 @@ class Encoder(JSONEncoder): else: return repr(o) - return OrderedDict( - [("_", objects.get(getattr(o, "ID", None), None))] - + [i for i in content.items()] - ) + if "pyrogram" in objects.get(getattr(o, "ID", "")): + return remove_none(OrderedDict([i for i in content.items()])) + else: + return OrderedDict( + [("_", objects.get(getattr(o, "ID", None), None))] + + [i for i in content.items()] + ) From 26e102047eabc73cf8c5e9f6f489895080dc4da8 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Mon, 26 Mar 2018 15:16:27 +0200 Subject: [PATCH 030/112] Import pyrogram types --- pyrogram/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyrogram/__init__.py b/pyrogram/__init__.py index 469dd399..f896f99c 100644 --- a/pyrogram/__init__.py +++ b/pyrogram/__init__.py @@ -32,3 +32,4 @@ from .client import ParseMode from .client.input_media import InputMedia from .client.input_phone_contact import InputPhoneContact from .client import Emoji +from .api.types.pyrogram import * From 7447b77b2598352f3894571cc9092b365b977d3d Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Mon, 26 Mar 2018 15:17:00 +0200 Subject: [PATCH 031/112] Add initial support for pyrogram types --- pyrogram/client/client.py | 81 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 75 insertions(+), 6 deletions(-) diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py index 98cf5e65..5f51161d 100644 --- a/pyrogram/client/client.py +++ b/pyrogram/client/client.py @@ -36,6 +36,7 @@ from queue import Queue from signal import signal, SIGINT, SIGTERM, SIGABRT from threading import Event, Thread +import pyrogram from pyrogram.api import functions, types from pyrogram.api.core import Object from pyrogram.api.errors import ( @@ -699,13 +700,81 @@ class Client: break try: - if self.update_handler: - self.update_handler( - self, - update[0], - {i.id: i for i in update[1]}, - {i.id: i for i in update[2]} + users = {i.id: i for i in update[1]} + chats = {i.id: i for i in update[2]} + update = update[0] + + if isinstance(update, types.UpdateNewMessage): + message = update.message # type: types.Message + + if isinstance(message, types.Message): + from_user = users[message.from_id] # type: types.User + text = message.message or None + + if isinstance(message.to_id, types.PeerUser): + to_user_id = message.to_id.user_id + to_user = users[to_user_id] # type: types.User + + chat = pyrogram.Chat( + id=to_user_id, + type="private", + username=to_user.username, + first_name=to_user.first_name, + last_name=to_user.last_name + ) + else: + to_group_id = message.to_id.chat_id + to_group = chats[to_group_id] # type: types.Chat + + chat = pyrogram.Chat( + id=-to_group_id, + type="group", + title=to_group.title, + all_members_are_administrators=to_group.admins_enabled + ) + else: + continue + elif isinstance(update, types.UpdateNewChannelMessage): + message = update.message # type: types.Message + + if isinstance(message, types.Message): + from_user = users.get(message.from_id, None) # type: types.User or None + to_channel_id = message.to_id.channel_id + to_channel = chats[to_channel_id] # type: types.Channel + text = message.message or None + + chat = pyrogram.Chat( + id=int("-100" + str(to_channel_id)), + type="supergroup" if to_channel.megagroup else "channel", + title=to_channel.title, + username=to_channel.username + ) + else: + continue + else: + continue + + u = pyrogram.Update( + update_id=0, + message=pyrogram.Message( + message_id=message.id, + date=message.date, + chat=chat, + from_user=pyrogram.User( + id=from_user.id, + is_bot=from_user.bot, + first_name=from_user.first_name, + last_name=from_user.last_name, + username=from_user.username, + language_code=from_user.lang_code + ) if from_user else None, + text=text ) + ) + + if self.update_handler: + self.update_handler(self, u) + # self.update_handler(self, update, users, chats) except Exception as e: log.error(e, exc_info=True) From 527c5450d201e0175fa8a2a8c61bbdba4948045d Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Mon, 26 Mar 2018 20:00:25 +0200 Subject: [PATCH 032/112] Extend support for pyrogram types --- pyrogram/client/__init__.py | 1 + pyrogram/client/client.py | 81 ++++++++---------------- pyrogram/client/utils.py | 120 ++++++++++++++++++++++++++++++++++++ 3 files changed, 145 insertions(+), 57 deletions(-) create mode 100644 pyrogram/client/utils.py diff --git a/pyrogram/client/__init__.py b/pyrogram/client/__init__.py index abda4464..49214797 100644 --- a/pyrogram/client/__init__.py +++ b/pyrogram/client/__init__.py @@ -20,3 +20,4 @@ from .chat_action import ChatAction from .client import Client from .parse_mode import ParseMode from .emoji import Emoji +from . import utils diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py index 5f51161d..cb585566 100644 --- a/pyrogram/client/client.py +++ b/pyrogram/client/client.py @@ -46,6 +46,7 @@ from pyrogram.api.errors import ( PasswordHashInvalid, FloodWait, PeerIdInvalid, FilePartMissing, ChatAdminRequired, FirstnameInvalid, PhoneNumberBanned, VolumeLocNotFound, UserMigrate) +from pyrogram.client import utils from pyrogram.crypto import AES from pyrogram.session import Auth, Session from pyrogram.session.internals import MsgId @@ -704,77 +705,43 @@ class Client: chats = {i.id: i for i in update[2]} update = update[0] - if isinstance(update, types.UpdateNewMessage): - message = update.message # type: types.Message + valid_updates = (types.UpdateNewMessage, types.UpdateNewChannelMessage, + types.UpdateEditMessage, types.UpdateEditChannelMessage) + + if isinstance(update, valid_updates): + message = update.message if isinstance(message, types.Message): - from_user = users[message.from_id] # type: types.User - text = message.message or None + m = utils.parse_message(message, users, chats) - if isinstance(message.to_id, types.PeerUser): - to_user_id = message.to_id.user_id - to_user = users[to_user_id] # type: types.User + if message.reply_to_msg_id: + rm = self.get_messages(m.chat.id, [message.reply_to_msg_id]) - chat = pyrogram.Chat( - id=to_user_id, - type="private", - username=to_user.username, - first_name=to_user.first_name, - last_name=to_user.last_name - ) - else: - to_group_id = message.to_id.chat_id - to_group = chats[to_group_id] # type: types.Chat + message = rm.messages[0] - chat = pyrogram.Chat( - id=-to_group_id, - type="group", - title=to_group.title, - all_members_are_administrators=to_group.admins_enabled - ) - else: - continue - elif isinstance(update, types.UpdateNewChannelMessage): - message = update.message # type: types.Message - - if isinstance(message, types.Message): - from_user = users.get(message.from_id, None) # type: types.User or None - to_channel_id = message.to_id.channel_id - to_channel = chats[to_channel_id] # type: types.Channel - text = message.message or None - - chat = pyrogram.Chat( - id=int("-100" + str(to_channel_id)), - type="supergroup" if to_channel.megagroup else "channel", - title=to_channel.title, - username=to_channel.username - ) - else: - continue + if isinstance(message, types.Message): + m.reply_to_message = utils.parse_message( + message, + {i.id: i for i in rm.users}, + {i.id: i for i in rm.chats} + ) + else: + continue else: continue + edit = isinstance(update, (types.UpdateEditMessage, types.UpdateEditChannelMessage)) + u = pyrogram.Update( update_id=0, - message=pyrogram.Message( - message_id=message.id, - date=message.date, - chat=chat, - from_user=pyrogram.User( - id=from_user.id, - is_bot=from_user.bot, - first_name=from_user.first_name, - last_name=from_user.last_name, - username=from_user.username, - language_code=from_user.lang_code - ) if from_user else None, - text=text - ) + message=(m if m.chat.type is not "channel" else None) if not edit else None, + edited_message=(m if m.chat.type is not "channel" else None) if edit else None, + channel_post=(m if m.chat.type is "channel" else None) if not edit else None, + edited_channel_post=(m if m.chat.type is "channel" else None) if edit else None ) if self.update_handler: self.update_handler(self, u) - # self.update_handler(self, update, users, chats) except Exception as e: log.error(e, exc_info=True) diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py new file mode 100644 index 00000000..5d5b37ba --- /dev/null +++ b/pyrogram/client/utils.py @@ -0,0 +1,120 @@ +import pyrogram +from pyrogram.api import types + +ENTITIES = { + types.MessageEntityMention.ID: "mention", + types.MessageEntityHashtag.ID: "hashtag", + types.MessageEntityBotCommand.ID: "bot_command", + types.MessageEntityUrl.ID: "url", + types.MessageEntityEmail.ID: "email", + types.MessageEntityBold.ID: "bold", + types.MessageEntityItalic.ID: "italic", + types.MessageEntityCode.ID: "code", + types.MessageEntityPre.ID: "pre", + types.MessageEntityTextUrl.ID: "text_link", +} + + +def parse_entities(entities: list): + output_entities = [] + + for entity in entities: + entity_type = ENTITIES.get(entity.ID, None) + + if entity_type: + output_entities.append(pyrogram.MessageEntity( + type=entity_type, + offset=entity.offset, + length=entity.length, + url=getattr(entity, "url", None) + )) + + return output_entities + + +def parse_user(user: types.User): + return pyrogram.User( + id=user.id, + is_bot=user.bot, + first_name=user.first_name, + last_name=user.last_name, + username=user.username, + language_code=user.lang_code + ) if user else None + + +def parse_user_chat(user: types.User): + return pyrogram.Chat( + id=user.id, + type="private", + username=user.username, + first_name=user.first_name, + last_name=user.last_name + ) + + +def parse_chat_chat(chat: types.Chat): + return pyrogram.Chat( + id=-chat.id, + type="group", + title=chat.title, + all_members_are_administrators=chat.admins_enabled + ) + + +def parse_channel_chat(channel: types.Channel): + return pyrogram.Chat( + id=int("-100" + str(channel.id)), + type="supergroup" if channel.megagroup else "channel", + title=channel.title, + username=channel.username + ) + + +def parse_message(message: types.Message, users: dict, chats: dict): + from_user = users.get(message.from_id, None) # type: types.User + + if isinstance(message.to_id, types.PeerUser): + chat = parse_user_chat(users[message.to_id.user_id]) + elif isinstance(message.to_id, types.PeerChat): + chat = parse_chat_chat(chats[message.to_id.chat_id]) + else: + chat = parse_channel_chat(chats[message.to_id.channel_id]) + + entities = parse_entities(message.entities) + + forward_from = None + forward_from_chat = None + forward_from_message_id = None + forward_signature = None + forward_date = None + + forward_header = message.fwd_from # type: types.MessageFwdHeader + + if forward_header: + forward_date = forward_header.date + + if forward_header.from_id: + forward_from = parse_user(users[forward_header.from_id]) + else: + forward_from_chat = parse_channel_chat(chats[forward_header.channel_id]) + forward_from_message_id = forward_header.channel_post + forward_signature = forward_header.post_author + + return pyrogram.Message( + message_id=message.id, + date=message.date, + chat=chat, + from_user=parse_user(from_user), + text=message.message or None if message.media is None else None, + caption=message.message or None if message.media is not None else None, + entities=entities or None if message.media is None else None, + caption_entities=entities or None if message.media is not None else None, + author_signature=message.post_author, + forward_from=forward_from, + forward_from_chat=forward_from_chat, + forward_from_message_id=forward_from_message_id, + forward_signature=forward_signature, + forward_date=forward_date, + edit_date=message.edit_date + ) From 91133812a718e669ae30570652f355960c994d5e Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 27 Mar 2018 12:22:57 +0200 Subject: [PATCH 033/112] Add support for service messages --- pyrogram/client/client.py | 18 +++++++++++ pyrogram/client/utils.py | 67 ++++++++++++++++++++++++++++++++------- pyrogram/crypto/aes.py | 54 +------------------------------ 3 files changed, 74 insertions(+), 65 deletions(-) diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py index cb585566..ebb87dd3 100644 --- a/pyrogram/client/client.py +++ b/pyrogram/client/client.py @@ -727,6 +727,24 @@ class Client: ) else: continue + elif isinstance(message, types.MessageService): + m = utils.parse_message_service(message, users, chats) + + if isinstance(message.action, types.MessageActionPinMessage): + pm = self.get_messages(m.chat.id, [message.reply_to_msg_id]) + + message = pm.messages[0] + + if isinstance(message, types.Message): + m.pinned_message = utils.parse_message( + message, + {i.id: i for i in pm.users}, + {i.id: i for i in pm.chats} + ) + else: + continue + else: + continue else: continue diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py index 5d5b37ba..053b06f7 100644 --- a/pyrogram/client/utils.py +++ b/pyrogram/client/utils.py @@ -43,6 +43,15 @@ def parse_user(user: types.User): ) if user else None +def parse_chat(peer: types.PeerUser or types.PeerChat or types.PeerChannel, users: dict, chats: dict): + if isinstance(peer, types.PeerUser): + return parse_user_chat(users[peer.user_id]) + elif isinstance(peer, types.PeerChat): + return parse_chat_chat(chats[peer.chat_id]) + else: + return parse_channel_chat(chats[peer.channel_id]) + + def parse_user_chat(user: types.User): return pyrogram.Chat( id=user.id, @@ -58,7 +67,7 @@ def parse_chat_chat(chat: types.Chat): id=-chat.id, type="group", title=chat.title, - all_members_are_administrators=chat.admins_enabled + all_members_are_administrators=not chat.admins_enabled ) @@ -72,15 +81,6 @@ def parse_channel_chat(channel: types.Channel): def parse_message(message: types.Message, users: dict, chats: dict): - from_user = users.get(message.from_id, None) # type: types.User - - if isinstance(message.to_id, types.PeerUser): - chat = parse_user_chat(users[message.to_id.user_id]) - elif isinstance(message.to_id, types.PeerChat): - chat = parse_chat_chat(chats[message.to_id.chat_id]) - else: - chat = parse_channel_chat(chats[message.to_id.channel_id]) - entities = parse_entities(message.entities) forward_from = None @@ -104,8 +104,8 @@ def parse_message(message: types.Message, users: dict, chats: dict): return pyrogram.Message( message_id=message.id, date=message.date, - chat=chat, - from_user=parse_user(from_user), + chat=parse_chat(message.to_id, users, chats), + from_user=parse_user(users.get(message.from_id, None)), text=message.message or None if message.media is None else None, caption=message.message or None if message.media is not None else None, entities=entities or None if message.media is None else None, @@ -118,3 +118,46 @@ def parse_message(message: types.Message, users: dict, chats: dict): forward_date=forward_date, edit_date=message.edit_date ) + + +def parse_message_service(message: types.MessageService, users: dict, chats: dict): + action = message.action + + new_chat_members = None + left_chat_member = None + new_chat_title = None + delete_chat_photo = None + migrate_to_chat_id = None + migrate_from_chat_id = None + group_chat_created = None + + if isinstance(action, types.MessageActionChatAddUser): + new_chat_members = [parse_user(users[i]) for i in action.users] + elif isinstance(action, types.MessageActionChatJoinedByLink): + new_chat_members = [parse_user(users[action.inviter_id])] + elif isinstance(action, types.MessageActionChatDeleteUser): + left_chat_member = parse_user(users[action.user_id]) + elif isinstance(action, types.MessageActionChatEditTitle): + new_chat_title = action.title + elif isinstance(action, types.MessageActionChatDeletePhoto): + delete_chat_photo = True + elif isinstance(action, types.MessageActionChatMigrateTo): + migrate_to_chat_id = action.channel_id + elif isinstance(action, types.MessageActionChannelMigrateFrom): + migrate_from_chat_id = action.chat_id + elif isinstance(action, types.MessageActionChatCreate): + group_chat_created = True + + return pyrogram.Message( + message_id=message.id, + date=message.date, + chat=parse_chat(message.to_id, users, chats), + from_user=parse_user(users.get(message.from_id, None)), + new_chat_members=new_chat_members, + left_chat_member=left_chat_member, + new_chat_title=new_chat_title, + delete_chat_photo=delete_chat_photo, + migrate_to_chat_id=migrate_to_chat_id, + migrate_from_chat_id=migrate_from_chat_id, + group_chat_created=group_chat_created + ) diff --git a/pyrogram/crypto/aes.py b/pyrogram/crypto/aes.py index 8ca72535..a5b0c3c8 100644 --- a/pyrogram/crypto/aes.py +++ b/pyrogram/crypto/aes.py @@ -1,53 +1 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 Dan Tès -# -# This file is part of Pyrogram. -# -# Pyrogram is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published -# by the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Pyrogram is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with Pyrogram. If not, see . - -try: - import tgcrypto -except ImportError as e: - e.msg = ( - "TgCrypto is missing and Pyrogram can't run without. " - "Please install it using \"pip3 install tgcrypto\". " - "More info: https://docs.pyrogram.ml/resources/TgCrypto" - ) - - raise e - - -class AES: - @classmethod - def ige_encrypt(cls, data: bytes, key: bytes, iv: bytes) -> bytes: - return tgcrypto.ige_encrypt(data, key, iv) - - @classmethod - def ige_decrypt(cls, data: bytes, key: bytes, iv: bytes) -> bytes: - return tgcrypto.ige_decrypt(data, key, iv) - - @staticmethod - def ctr_decrypt(data: bytes, key: bytes, iv: bytes, offset: int) -> bytes: - replace = int.to_bytes(offset // 16, 4, "big") - iv = iv[:-4] + replace - - return tgcrypto.ctr_decrypt(data, key, iv) - - @staticmethod - def xor(a: bytes, b: bytes) -> bytes: - return int.to_bytes( - int.from_bytes(a, "big") ^ int.from_bytes(b, "big"), - len(a), - "big", - ) +# Pyrogram - Telegram MTProto API Client Library for Python # Copyright (C) 2017-2018 Dan Tès # # This file is part of Pyrogram. # # Pyrogram is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Pyrogram is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . try: import tgcrypto except ImportError as e: e.msg = ( "TgCrypto is missing and Pyrogram can't run without. " "Please install it using \"pip3 install tgcrypto\". " "More info: https://docs.pyrogram.ml/resources/TgCrypto" ) raise e class AES: @classmethod def ige_encrypt(cls, data: bytes, key: bytes, iv: bytes) -> bytes: return tgcrypto.ige_encrypt(data, key, iv) @classmethod def ige_decrypt(cls, data: bytes, key: bytes, iv: bytes) -> bytes: return tgcrypto.ige_decrypt(data, key, iv) @staticmethod def ctr_decrypt(data: bytes, key: bytes, iv: bytes, offset: int) -> bytes: replace = int.to_bytes(offset // 16, 4, "big") iv = iv[:-4] + replace return tgcrypto.ctr_decrypt(data, key, iv) @staticmethod def xor(a: bytes, b: bytes) -> bytes: return int.to_bytes( int.from_bytes(a, "big") ^ int.from_bytes(b, "big"), len(a), "big", ) \ No newline at end of file From f1e1480c5a0c8c98b9c37f636c2320e003ac5f0a Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 27 Mar 2018 15:58:04 +0200 Subject: [PATCH 034/112] Only parse selected messages --- pyrogram/client/client.py | 3 +++ pyrogram/client/utils.py | 16 +++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py index ebb87dd3..290a8ba9 100644 --- a/pyrogram/client/client.py +++ b/pyrogram/client/client.py @@ -730,6 +730,9 @@ class Client: elif isinstance(message, types.MessageService): m = utils.parse_message_service(message, users, chats) + if m is None: + continue + if isinstance(message.action, types.MessageActionPinMessage): pm = self.get_messages(m.chat.id, [message.reply_to_msg_id]) diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py index 053b06f7..db755822 100644 --- a/pyrogram/client/utils.py +++ b/pyrogram/client/utils.py @@ -43,13 +43,13 @@ def parse_user(user: types.User): ) if user else None -def parse_chat(peer: types.PeerUser or types.PeerChat or types.PeerChannel, users: dict, chats: dict): - if isinstance(peer, types.PeerUser): - return parse_user_chat(users[peer.user_id]) - elif isinstance(peer, types.PeerChat): - return parse_chat_chat(chats[peer.chat_id]) +def parse_chat(message: types.Message, users: dict, chats: dict): + if isinstance(message.to_id, types.PeerUser): + return parse_user_chat(users[message.from_id]) + elif isinstance(message.to_id, types.PeerChat): + return parse_chat_chat(chats[message.to_id.chat_id]) else: - return parse_channel_chat(chats[peer.channel_id]) + return parse_channel_chat(chats[message.to_id.channel_id]) def parse_user_chat(user: types.User): @@ -104,7 +104,7 @@ def parse_message(message: types.Message, users: dict, chats: dict): return pyrogram.Message( message_id=message.id, date=message.date, - chat=parse_chat(message.to_id, users, chats), + chat=parse_chat(message, users, chats), from_user=parse_user(users.get(message.from_id, None)), text=message.message or None if message.media is None else None, caption=message.message or None if message.media is not None else None, @@ -147,6 +147,8 @@ def parse_message_service(message: types.MessageService, users: dict, chats: dic migrate_from_chat_id = action.chat_id elif isinstance(action, types.MessageActionChatCreate): group_chat_created = True + else: + return None return pyrogram.Message( message_id=message.id, From 692539f6a20e6408ffd6d7839cb4d7f5d6b9eda7 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Wed, 28 Mar 2018 18:23:44 +0200 Subject: [PATCH 035/112] Add file_id codec --- pyrogram/client/utils.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py index db755822..fa5ff41a 100644 --- a/pyrogram/client/utils.py +++ b/pyrogram/client/utils.py @@ -1,3 +1,5 @@ +from base64 import b64encode, b64decode + import pyrogram from pyrogram.api import types @@ -163,3 +165,39 @@ def parse_message_service(message: types.MessageService, users: dict, chats: dic migrate_from_chat_id=migrate_from_chat_id, group_chat_created=group_chat_created ) + + +def decode(s): + s = b64decode(s + "=" * (-len(s) % 4), "-_") + r = b"" + + assert s[-1] == 2 + + i = 0 + while i < len(s) - 1: + if s[i] != 0: + r += bytes([s[i]]) + else: + r += b"\x00" * s[i + 1] + i += 1 + + i += 1 + + return r + + +def encode(s): + r = b"" + n = 0 + + for i in s + bytes([2]): + if i == 0: + n += 1 + else: + if n: + r += b"\x00" + bytes([n]) + n = 0 + + r += bytes([i]) + + return b64encode(r, b"-_").decode().rstrip("=") From 846fb0b637120c523c057f6764158fee9bed68ff Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sat, 31 Mar 2018 12:20:22 +0200 Subject: [PATCH 036/112] Add photo field --- pyrogram/client/utils.py | 43 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py index fa5ff41a..b47f81b5 100644 --- a/pyrogram/client/utils.py +++ b/pyrogram/client/utils.py @@ -1,4 +1,5 @@ from base64 import b64encode, b64decode +from struct import pack import pyrogram from pyrogram.api import types @@ -103,6 +104,45 @@ def parse_message(message: types.Message, users: dict, chats: dict): forward_from_message_id = forward_header.channel_post forward_signature = forward_header.post_author + photo = None + + media = message.media + + if media: + if isinstance(media, types.MessageMediaPhoto): + photo = media.photo + + if isinstance(photo, types.Photo): + sizes = photo.sizes + photo_sizes = [] + + for size in sizes: + if isinstance(size, (types.PhotoSize, types.PhotoCachedSize)): + location = size.location + + if isinstance(location, types.FileLocation): + photo_size = pyrogram.PhotoSize( + file_id=encode( + pack( + " Date: Sun, 1 Apr 2018 12:17:38 +0200 Subject: [PATCH 037/112] Fix chat parsing --- pyrogram/client/utils.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py index b47f81b5..b353250b 100644 --- a/pyrogram/client/utils.py +++ b/pyrogram/client/utils.py @@ -48,7 +48,7 @@ def parse_user(user: types.User): def parse_chat(message: types.Message, users: dict, chats: dict): if isinstance(message.to_id, types.PeerUser): - return parse_user_chat(users[message.from_id]) + return parse_user_chat(users[message.to_id.user_id if message.out else message.from_id]) elif isinstance(message.to_id, types.PeerChat): return parse_chat_chat(chats[message.to_id.chat_id]) else: @@ -120,6 +120,11 @@ def parse_message(message: types.Message, users: dict, chats: dict): if isinstance(size, (types.PhotoSize, types.PhotoCachedSize)): location = size.location + if isinstance(size, types.PhotoSize): + file_size = size.size + else: + file_size = len(size.bytes) + if isinstance(location, types.FileLocation): photo_size = pyrogram.PhotoSize( file_id=encode( @@ -136,7 +141,7 @@ def parse_message(message: types.Message, users: dict, chats: dict): ), width=size.w, height=size.h, - file_size=size.size + file_size=file_size ) photo_sizes.append(photo_size) @@ -196,7 +201,7 @@ def parse_message_service(message: types.MessageService, users: dict, chats: dic return pyrogram.Message( message_id=message.id, date=message.date, - chat=parse_chat(message.to_id, users, chats), + chat=parse_chat(message, users, chats), from_user=parse_user(users.get(message.from_id, None)), new_chat_members=new_chat_members, left_chat_member=left_chat_member, From 44a3a4d69bc81804dde015627d8dc72e28b6f3b3 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sun, 1 Apr 2018 13:08:37 +0200 Subject: [PATCH 038/112] Add pyrogram.txt template --- compiler/api/template/{class.txt => mtproto.txt} | 0 compiler/api/template/pyrogram.txt | 10 ++++++++++ 2 files changed, 10 insertions(+) rename compiler/api/template/{class.txt => mtproto.txt} (100%) create mode 100644 compiler/api/template/pyrogram.txt diff --git a/compiler/api/template/class.txt b/compiler/api/template/mtproto.txt similarity index 100% rename from compiler/api/template/class.txt rename to compiler/api/template/mtproto.txt diff --git a/compiler/api/template/pyrogram.txt b/compiler/api/template/pyrogram.txt new file mode 100644 index 00000000..f5f7a96a --- /dev/null +++ b/compiler/api/template/pyrogram.txt @@ -0,0 +1,10 @@ +{notice} + + +class {class_name}: + """{docstring_args} + """ + ID = {object_id} + + def __init__(self{arguments}): + {fields} From c54730b8ab77f319c8acf4f68811ce459423b370 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sun, 1 Apr 2018 13:09:32 +0200 Subject: [PATCH 039/112] Don't inherit from Object --- compiler/api/compiler.py | 47 ++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/compiler/api/compiler.py b/compiler/api/compiler.py index a62e7bbe..7ee707a9 100644 --- a/compiler/api/compiler.py +++ b/compiler/api/compiler.py @@ -144,8 +144,11 @@ def start(): open("{}/source/pyrogram.tl".format(HOME), encoding="utf-8") as pyrogram: schema = (auth.read() + system.read() + api.read() + pyrogram.read()).splitlines() - with open("{}/template/class.txt".format(HOME), encoding="utf-8") as f: - template = f.read() + with open("{}/template/mtproto.txt".format(HOME), encoding="utf-8") as f: + mtproto_template = f.read() + + with open("{}/template/pyrogram.txt".format(HOME), encoding="utf-8") as f: + pyrogram_template = f.read() with open(NOTICE_PATH, encoding="utf-8") as f: notice = [] @@ -398,21 +401,33 @@ def start(): docstring_args = description + "\n\n " + docstring_args with open("{}/{}.py".format(path, snek(c.name)), "w", encoding="utf-8") as f: - f.write( - template.format( - notice=notice, - class_name=capit(c.name), - docstring_args=docstring_args, - object_id=c.id, - arguments=arguments, - fields=fields, - read_flags=read_flags, - read_types=read_types, - write_flags=write_flags, - write_types=write_types, - return_arguments=", ".join([i[0] for i in sorted_args]) + if c.docs: + f.write( + pyrogram_template.format( + notice=notice, + class_name=capit(c.name), + docstring_args=docstring_args, + object_id=c.id, + arguments=arguments, + fields=fields + ) + ) + else: + f.write( + mtproto_template.format( + notice=notice, + class_name=capit(c.name), + docstring_args=docstring_args, + object_id=c.id, + arguments=arguments, + fields=fields, + read_flags=read_flags, + read_types=read_types, + write_flags=write_flags, + write_types=write_types, + return_arguments=", ".join([i[0] for i in sorted_args]) + ) ) - ) with open("{}/all.py".format(DESTINATION), "w", encoding="utf-8") as f: f.write(notice + "\n\n") From 1bbb282dab0f2aa184e498ed93b542d8f77add5e Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sun, 1 Apr 2018 13:27:05 +0200 Subject: [PATCH 040/112] Change type/desc separator --- compiler/api/compiler.py | 7 +++---- compiler/api/source/pyrogram.tl | 34 ++++++++++++++++----------------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/compiler/api/compiler.py b/compiler/api/compiler.py index 7ee707a9..3e15a2bd 100644 --- a/compiler/api/compiler.py +++ b/compiler/api/compiler.py @@ -268,6 +268,7 @@ def start(): ) if c.args else "pass" docstring_args = [] + docs = c.docs.split("|")[1:] if c.docs else None for i, arg in enumerate(sorted_args): arg_name, arg_type = arg @@ -275,15 +276,13 @@ def start(): flag_number = is_optional.group(1) if is_optional else -1 arg_type = arg_type.split("?")[-1] - docs = c.docs.split("|")[1:] if c.docs else None - if docs: docstring_args.append( "{} ({}{}):\n {}\n".format( arg_name, get_docstring_arg_type(arg_type), ", optional" if "Optional" in docs[i] else "", - re.sub("Optional\. ", "", docs[i].split(":")[1]) + re.sub("Optional\. ", "", docs[i].split("§")[1].rstrip(".") + ".") ) ) else: @@ -397,7 +396,7 @@ def start(): read_types += "{} = Object.read(b)\n ".format(arg_name) if c.docs: - description = c.docs.split("|")[0].split(":")[1] + description = c.docs.split("|")[0].split("§")[1] docstring_args = description + "\n\n " + docstring_args with open("{}/{}.py".format(path, snek(c.name)), "w", encoding="utf-8") as f: diff --git a/compiler/api/source/pyrogram.tl b/compiler/api/source/pyrogram.tl index 911d3ed5..491e890c 100644 --- a/compiler/api/source/pyrogram.tl +++ b/compiler/api/source/pyrogram.tl @@ -2,20 +2,20 @@ ---types--- -pyrogram.update#b0700000 flags:# update_id:int message:flags.0?Message edited_message:flags.1?Message channel_post:flags.2?Message edited_channel_post:flags.3?Message inline_query:flags.4?InlineQuery chosen_inline_result:flags.5?ChosenInlineResult callback_query:flags.6?CallbackQuery shipping_query:flags.7?ShippingQuery pre_checkout_query:flags.8?PreCheckoutQuery = pyrogram.Update; // Docs: Update:This object represents an incoming update.At most one of the optional parameters can be present in any given update.|update_id:The update's unique identifier. Update identifiers start from a certain positive number and increase sequentially. This ID becomes especially handy if you're using Webhooks, since it allows you to ignore repeated updates or to restore the correct update sequence, should they get out of order. If there are no new updates for at least a week, then identifier of the next update will be chosen randomly instead of sequentially.|message:Optional. New incoming message of any kind � text, photo, sticker, etc.|edited_message:Optional. New version of a message that is known to the bot and was edited|channel_post:Optional. New incoming channel post of any kind � text, photo, sticker, etc.|edited_channel_post:Optional. New version of a channel post that is known to the bot and was edited|inline_query:Optional. New incoming inline query|chosen_inline_result:Optional. The result of an inline query that was chosen by a user and sent to their chat partner. Please see our documentation on the feedback collecting for details on how to enable these updates for your bot.|callback_query:Optional. New incoming callback query|shipping_query:Optional. New incoming shipping query. Only for invoices with flexible price|pre_checkout_query:Optional. New incoming pre-checkout query. Contains full information about checkout -pyrogram.user#b0700001 flags:# id:int is_bot:Bool first_name:string last_name:flags.0?string username:flags.1?string language_code:flags.2?string = pyrogram.User; // Docs: User:This object represents a Telegram user or bot.|id:Unique identifier for this user or bot|is_bot:True, if this user is a bot|first_name:User's or bot's first name|last_name:Optional. User's or bot's last name|username:Optional. User's or bot's username|language_code:Optional. IETF language tag of the user's language -pyrogram.chat#b0700002 flags:# id:int type:string title:flags.0?string username:flags.1?string first_name:flags.2?string last_name:flags.3?string all_members_are_administrators:flags.4?Bool photo:flags.5?ChatPhoto description:flags.6?string invite_link:flags.7?string pinned_message:flags.8?Message sticker_set_name:flags.9?string can_set_sticker_set:flags.10?Bool = pyrogram.Chat; // Docs: Chat:This object represents a chat.|id:Unique identifier for this chat. This number may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier.|type:Type of chat, can be either "private", "group", "supergroup" or "channel"|title:Optional. Title, for supergroups, channels and group chats|username:Optional. Username, for private chats, supergroups and channels if available|first_name:Optional. First name of the other party in a private chat|last_name:Optional. Last name of the other party in a private chat|all_members_are_administrators:Optional. True if a group has 'All Members Are Admins' enabled.|photo:Optional. Chat photo. Returned only in getChat.|description:Optional. Description, for supergroups and channel chats. Returned only in getChat.|invite_link:Optional. Chat invite link, for supergroups and channel chats. Returned only in getChat.|pinned_message:Optional. Pinned message, for supergroups and channel chats. Returned only in getChat.|sticker_set_name:Optional. For supergroups, name of group sticker set. Returned only in getChat.|can_set_sticker_set:Optional. True, if the bot can change the group sticker set. Returned only in getChat. -pyrogram.message#b0700003 flags:# message_id:int from_user:flags.0?User date:int chat:Chat forward_from:flags.1?User forward_from_chat:flags.2?Chat forward_from_message_id:flags.3?int forward_signature:flags.4?string forward_date:flags.5?int reply_to_message:flags.6?Message edit_date:flags.7?int media_group_id:flags.8?string author_signature:flags.9?string text:flags.10?string entities:flags.11?Vector caption_entities:flags.12?Vector audio:flags.13?Audio document:flags.14?Document game:flags.15?Game photo:flags.16?Vector sticker:flags.17?Sticker video:flags.18?Video voice:flags.19?Voice video_note:flags.20?VideoNote caption:flags.21?string contact:flags.22?Contact location:flags.23?Location venue:flags.24?Venue new_chat_members:flags.25?Vector left_chat_member:flags.26?User new_chat_title:flags.27?string new_chat_photo:flags.28?Vector delete_chat_photo:flags.29?true group_chat_created:flags.30?true supergroup_chat_created:flags.31?true channel_chat_created:flags.32?true migrate_to_chat_id:flags.33?int migrate_from_chat_id:flags.34?int pinned_message:flags.35?Message invoice:flags.36?Invoice successful_payment:flags.37?SuccessfulPayment connected_website:flags.38?string = pyrogram.Message; // Docs: Message:This object represents a message.|message_id:Unique message identifier inside this chat|from_user:Optional. Sender, empty for messages sent to channels|date:Date the message was sent in Unix time|chat:Conversation the message belongs to|forward_from:Optional. For forwarded messages, sender of the original message|forward_from_chat:Optional. For messages forwarded from channels, information about the original channel|forward_from_message_id:Optional. For messages forwarded from channels, identifier of the original message in the channel|forward_signature:Optional. For messages forwarded from channels, signature of the post author if present|forward_date:Optional. For forwarded messages, date the original message was sent in Unix time|reply_to_message:Optional. For replies, the original message. Note that the Message object in this field will not contain further reply_to_message fields even if it itself is a reply.|edit_date:Optional. Date the message was last edited in Unix time|media_group_id:Optional. The unique identifier of a media message group this message belongs to|author_signature:Optional. Signature of the post author for messages in channels|text:Optional. For text messages, the actual UTF-8 text of the message, 0-4096 characters.|entities:Optional. For text messages, special entities like usernames, URLs, bot commands, etc. that appear in the text|caption_entities:Optional. For messages with a caption, special entities like usernames, URLs, bot commands, etc. that appear in the caption|audio:Optional. Message is an audio file, information about the file|document:Optional. Message is a general file, information about the file|game:Optional. Message is a game, information about the game. More about games|photo:Optional. Message is a photo, available sizes of the photo|sticker:Optional. Message is a sticker, information about the sticker|video:Optional. Message is a video, information about the video|voice:Optional. Message is a voice message, information about the file|video_note:Optional. Message is a video note, information about the video message|caption:Optional. Caption for the audio, document, photo, video or voice, 0-200 characters|contact:Optional. Message is a shared contact, information about the contact|location:Optional. Message is a shared location, information about the location|venue:Optional. Message is a venue, information about the venue|new_chat_members:Optional. New members that were added to the group or supergroup and information about them (the bot itself may be one of these members)|left_chat_member:Optional. A member was removed from the group, information about them (this member may be the bot itself)|new_chat_title:Optional. A chat title was changed to this value|new_chat_photo:Optional. A chat photo was change to this value|delete_chat_photo:Optional. Service message: the chat photo was deleted|group_chat_created:Optional. Service message: the group has been created|supergroup_chat_created:Optional. Service message: the supergroup has been created. This field can't be received in a message coming through updates, because bot can't be a member of a supergroup when it is created. It can only be found in reply_to_message if someone replies to a very first message in a directly created supergroup.|channel_chat_created:Optional. Service message: the channel has been created. This field can't be received in a message coming through updates, because bot can't be a member of a channel when it is created. It can only be found in reply_to_message if someone replies to a very first message in a channel.|migrate_to_chat_id:Optional. The group has been migrated to a supergroup with the specified identifier. This number may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier.|migrate_from_chat_id:Optional. The supergroup has been migrated from a group with the specified identifier. This number may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier.|pinned_message:Optional. Specified message was pinned. Note that the Message object in this field will not contain further reply_to_message fields even if it is itself a reply.|invoice:Optional. Message is an invoice for a payment, information about the invoice. More about payments|successful_payment:Optional. Message is a service message about a successful payment, information about the payment. More about payments|connected_website:Optional. The domain name of the website on which the user has logged in. More about Telegram Login -pyrogram.messageEntity#b0700004 flags:# type:string offset:int length:int url:flags.0?string user:flags.1?User = pyrogram.MessageEntity; // Docs: MessageEntity:This object represents one special entity in a text message. For example, hashtags, usernames, URLs, etc.|type:Type of the entity. Can be mention (@username), hashtag, bot_command, url, email, bold (bold text), italic (italic text), code (monowidth string), pre (monowidth block), text_link (for clickable text URLs), text_mention (for users without usernames)|offset:Offset in UTF-16 code units to the start of the entity|length:Length of the entity in UTF-16 code units|url:Optional. For "text_link" only, url that will be opened after user taps on the text|user:Optional. For "text_mention" only, the mentioned user -pyrogram.photoSize#b0700005 flags:# file_id:string width:int height:int file_size:flags.0?int = pyrogram.PhotoSize; // Docs: PhotoSize:This object represents one size of a photo or a file / sticker thumbnail.|file_id:Unique identifier for this file|width:Photo width|height:Photo height|file_size:Optional. File size -pyrogram.audio#b0700006 flags:# file_id:string duration:int performer:flags.0?string title:flags.1?string mime_type:flags.2?string file_size:flags.3?int = pyrogram.Audio; // Docs: Audio:This object represents an audio file to be treated as music by the Telegram clients.|file_id:Unique identifier for this file|duration:Duration of the audio in seconds as defined by sender|performer:Optional. Performer of the audio as defined by sender or by audio tags|title:Optional. Title of the audio as defined by sender or by audio tags|mime_type:Optional. MIME type of the file as defined by sender|file_size:Optional. File size -pyrogram.document#b0700007 flags:# file_id:string thumb:flags.0?PhotoSize file_name:flags.1?string mime_type:flags.2?string file_size:flags.3?int = pyrogram.Document; // Docs: Document:This object represents a general file (as opposed to photos, voice messages and audio files).|file_id:Unique file identifier|thumb:Optional. Document thumbnail as defined by sender|file_name:Optional. Original filename as defined by sender|mime_type:Optional. MIME type of the file as defined by sender|file_size:Optional. File size -pyrogram.video#b0700008 flags:# file_id:string width:int height:int duration:int thumb:flags.0?PhotoSize mime_type:flags.1?string file_size:flags.2?int = pyrogram.Video; // Docs: Video:This object represents a video file.|file_id:Unique identifier for this file|width:Video width as defined by sender|height:Video height as defined by sender|duration:Duration of the video in seconds as defined by sender|thumb:Optional. Video thumbnail|mime_type:Optional. Mime type of a file as defined by sender|file_size:Optional. File size -pyrogram.voice#b0700009 flags:# file_id:string duration:int mime_type:flags.0?string file_size:flags.1?int = pyrogram.Voice; // Docs: Voice:This object represents a voice note.|file_id:Unique identifier for this file|duration:Duration of the audio in seconds as defined by sender|mime_type:Optional. MIME type of the file as defined by sender|file_size:Optional. File size -pyrogram.videoNote#b0700010 flags:# file_id:string length:int duration:int thumb:flags.0?PhotoSize file_size:flags.1?int = pyrogram.VideoNote; // Docs: VideoNote:This object represents a video message (available in Telegram apps as of v.4.0).|file_id:Unique identifier for this file|length:Video width and height as defined by sender|duration:Duration of the video in seconds as defined by sender|thumb:Optional. Video thumbnail|file_size:Optional. File size -pyrogram.contact#b0700011 flags:# phone_number:string first_name:string last_name:flags.0?string user_id:flags.1?int = pyrogram.Contact; // Docs: Contact:This object represents a phone contact.|phone_number:Contact's phone number|first_name:Contact's first name|last_name:Optional. Contact's last name|user_id:Optional. Contact's user identifier in Telegram -pyrogram.location#b0700012 longitude:double latitude:double = pyrogram.Location; // Docs: Location:This object represents a point on the map.|longitude:Longitude as defined by sender|latitude:Latitude as defined by sender -pyrogram.venue#b0700013 flags:# location:Location title:string address:string foursquare_id:flags.0?string = pyrogram.Venue; // Docs: Venue:This object represents a venue.|location:Venue location|title:Name of the venue|address:Address of the venue|foursquare_id:Optional. Foursquare identifier of the venue -pyrogram.userProfilePhotos#b0700014 total_count:int photos:Vector> = pyrogram.UserProfilePhotos; // Docs: UserProfilePhotos:This object represent a user's profile pictures.|total_count:Total number of profile pictures the target user has|photos:Requested profile pictures (in up to 4 sizes each) -pyrogram.chatPhoto#b0700015 small_file_id:string big_file_id:string = pyrogram.ChatPhoto; // Docs: ChatPhoto:This object represents a chat photo.|small_file_id:Unique file identifier of small (160x160) chat photo. This file_id can be used only for photo download.|big_file_id:Unique file identifier of big (640x640) chat photo. This file_id can be used only for photo download. -pyrogram.chatMember#b0700016 flags:# user:User status:string until_date:flags.0?int can_be_edited:flags.1?Bool can_change_info:flags.2?Bool can_post_messages:flags.3?Bool can_edit_messages:flags.4?Bool can_delete_messages:flags.5?Bool can_invite_users:flags.6?Bool can_restrict_members:flags.7?Bool can_pin_messages:flags.8?Bool can_promote_members:flags.9?Bool can_send_messages:flags.10?Bool can_send_media_messages:flags.11?Bool can_send_other_messages:flags.12?Bool can_add_web_page_previews:flags.13?Bool = pyrogram.ChatMember; // Docs: ChatMember:This object contains information about one member of a chat.|user:Information about the user|status:The member's status in the chat. Can be "creator", "administrator", "member", "restricted", "left" or "kicked"|until_date:Optional. Restricted and kicked only. Date when restrictions will be lifted for this user, unix time|can_be_edited:Optional. Administrators only. True, if the bot is allowed to edit administrator privileges of that user|can_change_info:Optional. Administrators only. True, if the administrator can change the chat title, photo and other settings|can_post_messages:Optional. Administrators only. True, if the administrator can post in the channel, channels only|can_edit_messages:Optional. Administrators only. True, if the administrator can edit messages of other users and can pin messages, channels only|can_delete_messages:Optional. Administrators only. True, if the administrator can delete messages of other users|can_invite_users:Optional. Administrators only. True, if the administrator can invite new users to the chat|can_restrict_members:Optional. Administrators only. True, if the administrator can restrict, ban or unban chat members|can_pin_messages:Optional. Administrators only. True, if the administrator can pin messages, supergroups only|can_promote_members:Optional. Administrators only. 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 the user)|can_send_messages:Optional. Restricted only. True, if the user can send text messages, contacts, locations and venues|can_send_media_messages:Optional. Restricted only. True, if the user can send audios, documents, photos, videos, video notes and voice notes, implies can_send_messages|can_send_other_messages:Optional. Restricted only. True, if the user can send animations, games, stickers and use inline bots, implies can_send_media_messages|can_add_web_page_previews:Optional. Restricted only. True, if user may add web page previews to his messages, implies can_send_media_messages +pyrogram.update#b0700000 flags:# update_id:int message:flags.0?Message edited_message:flags.1?Message channel_post:flags.2?Message edited_channel_post:flags.3?Message inline_query:flags.4?InlineQuery chosen_inline_result:flags.5?ChosenInlineResult callback_query:flags.6?CallbackQuery shipping_query:flags.7?ShippingQuery pre_checkout_query:flags.8?PreCheckoutQuery = pyrogram.Update; // Docs: Update§This object represents an incoming update.At most one of the optional parameters can be present in any given update.|update_id§The update's unique identifier. Update identifiers start from a certain positive number and increase sequentially. This ID becomes especially handy if you're using Webhooks, since it allows you to ignore repeated updates or to restore the correct update sequence, should they get out of order. If there are no new updates for at least a week, then identifier of the next update will be chosen randomly instead of sequentially.|message§Optional. New incoming message of any kind — text, photo, sticker, etc.|edited_message§Optional. New version of a message that is known to the bot and was edited|channel_post§Optional. New incoming channel post of any kind — text, photo, sticker, etc.|edited_channel_post§Optional. New version of a channel post that is known to the bot and was edited|inline_query§Optional. New incoming inline query|chosen_inline_result§Optional. The result of an inline query that was chosen by a user and sent to their chat partner. Please see our documentation on the feedback collecting for details on how to enable these updates for your bot.|callback_query§Optional. New incoming callback query|shipping_query§Optional. New incoming shipping query. Only for invoices with flexible price|pre_checkout_query§Optional. New incoming pre-checkout query. Contains full information about checkout +pyrogram.user#b0700001 flags:# id:int is_bot:Bool first_name:string last_name:flags.0?string username:flags.1?string language_code:flags.2?string = pyrogram.User; // Docs: User§This object represents a Telegram user or bot.|id§Unique identifier for this user or bot|is_bot§True, if this user is a bot|first_name§User's or bot's first name|last_name§Optional. User's or bot's last name|username§Optional. User's or bot's username|language_code§Optional. IETF language tag of the user's language +pyrogram.chat#b0700002 flags:# id:int type:string title:flags.0?string username:flags.1?string first_name:flags.2?string last_name:flags.3?string all_members_are_administrators:flags.4?Bool photo:flags.5?ChatPhoto description:flags.6?string invite_link:flags.7?string pinned_message:flags.8?Message sticker_set_name:flags.9?string can_set_sticker_set:flags.10?Bool = pyrogram.Chat; // Docs: Chat§This object represents a chat.|id§Unique identifier for this chat. This number may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier.|type§Type of chat, can be either "private", "group", "supergroup" or "channel"|title§Optional. Title, for supergroups, channels and group chats|username§Optional. Username, for private chats, supergroups and channels if available|first_name§Optional. First name of the other party in a private chat|last_name§Optional. Last name of the other party in a private chat|all_members_are_administrators§Optional. True if a group has 'All Members Are Admins' enabled.|photo§Optional. Chat photo. Returned only in getChat.|description§Optional. Description, for supergroups and channel chats. Returned only in getChat.|invite_link§Optional. Chat invite link, for supergroups and channel chats. Returned only in getChat.|pinned_message§Optional. Pinned message, for supergroups and channel chats. Returned only in getChat.|sticker_set_name§Optional. For supergroups, name of group sticker set. Returned only in getChat.|can_set_sticker_set§Optional. True, if the bot can change the group sticker set. Returned only in getChat. +pyrogram.message#b0700003 flags:# message_id:int from_user:flags.0?User date:int chat:Chat forward_from:flags.1?User forward_from_chat:flags.2?Chat forward_from_message_id:flags.3?int forward_signature:flags.4?string forward_date:flags.5?int reply_to_message:flags.6?Message edit_date:flags.7?int media_group_id:flags.8?string author_signature:flags.9?string text:flags.10?string entities:flags.11?Vector caption_entities:flags.12?Vector audio:flags.13?Audio document:flags.14?Document game:flags.15?Game photo:flags.16?Vector sticker:flags.17?Sticker video:flags.18?Video voice:flags.19?Voice video_note:flags.20?VideoNote caption:flags.21?string contact:flags.22?Contact location:flags.23?Location venue:flags.24?Venue new_chat_members:flags.25?Vector left_chat_member:flags.26?User new_chat_title:flags.27?string new_chat_photo:flags.28?Vector delete_chat_photo:flags.29?true group_chat_created:flags.30?true supergroup_chat_created:flags.31?true channel_chat_created:flags.32?true migrate_to_chat_id:flags.33?int migrate_from_chat_id:flags.34?int pinned_message:flags.35?Message invoice:flags.36?Invoice successful_payment:flags.37?SuccessfulPayment connected_website:flags.38?string = pyrogram.Message; // Docs: Message§This object represents a message.|message_id§Unique message identifier inside this chat|from_user§Optional. Sender, empty for messages sent to channels|date§Date the message was sent in Unix time|chat§Conversation the message belongs to|forward_from§Optional. For forwarded messages, sender of the original message|forward_from_chat§Optional. For messages forwarded from channels, information about the original channel|forward_from_message_id§Optional. For messages forwarded from channels, identifier of the original message in the channel|forward_signature§Optional. For messages forwarded from channels, signature of the post author if present|forward_date§Optional. For forwarded messages, date the original message was sent in Unix time|reply_to_message§Optional. For replies, the original message. Note that the Message object in this field will not contain further reply_to_message fields even if it itself is a reply.|edit_date§Optional. Date the message was last edited in Unix time|media_group_id§Optional. The unique identifier of a media message group this message belongs to|author_signature§Optional. Signature of the post author for messages in channels|text§Optional. For text messages, the actual UTF-8 text of the message, 0-4096 characters.|entities§Optional. For text messages, special entities like usernames, URLs, bot commands, etc. that appear in the text|caption_entities§Optional. For messages with a caption, special entities like usernames, URLs, bot commands, etc. that appear in the caption|audio§Optional. Message is an audio file, information about the file|document§Optional. Message is a general file, information about the file|game§Optional. Message is a game, information about the game. More about games|photo§Optional. Message is a photo, available sizes of the photo|sticker§Optional. Message is a sticker, information about the sticker|video§Optional. Message is a video, information about the video|voice§Optional. Message is a voice message, information about the file|video_note§Optional. Message is a video note, information about the video message|caption§Optional. Caption for the audio, document, photo, video or voice, 0-200 characters|contact§Optional. Message is a shared contact, information about the contact|location§Optional. Message is a shared location, information about the location|venue§Optional. Message is a venue, information about the venue|new_chat_members§Optional. New members that were added to the group or supergroup and information about them (the bot itself may be one of these members)|left_chat_member§Optional. A member was removed from the group, information about them (this member may be the bot itself)|new_chat_title§Optional. A chat title was changed to this value|new_chat_photo§Optional. A chat photo was change to this value|delete_chat_photo§Optional. Service message: the chat photo was deleted|group_chat_created§Optional. Service message: the group has been created|supergroup_chat_created§Optional. Service message: the supergroup has been created. This field can't be received in a message coming through updates, because bot can't be a member of a supergroup when it is created. It can only be found in reply_to_message if someone replies to a very first message in a directly created supergroup.|channel_chat_created§Optional. Service message: the channel has been created. This field can't be received in a message coming through updates, because bot can't be a member of a channel when it is created. It can only be found in reply_to_message if someone replies to a very first message in a channel.|migrate_to_chat_id§Optional. The group has been migrated to a supergroup with the specified identifier. This number may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier.|migrate_from_chat_id§Optional. The supergroup has been migrated from a group with the specified identifier. This number may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier.|pinned_message§Optional. Specified message was pinned. Note that the Message object in this field will not contain further reply_to_message fields even if it is itself a reply.|invoice§Optional. Message is an invoice for a payment, information about the invoice. More about payments|successful_payment§Optional. Message is a service message about a successful payment, information about the payment. More about payments|connected_website§Optional. The domain name of the website on which the user has logged in. More about Telegram Login +pyrogram.messageEntity#b0700004 flags:# type:string offset:int length:int url:flags.0?string user:flags.1?User = pyrogram.MessageEntity; // Docs: MessageEntity§This object represents one special entity in a text message. For example, hashtags, usernames, URLs, etc.|type§Type of the entity. Can be mention (@username), hashtag, bot_command, url, email, bold (bold text), italic (italic text), code (monowidth string), pre (monowidth block), text_link (for clickable text URLs), text_mention (for users without usernames)|offset§Offset in UTF-16 code units to the start of the entity|length§Length of the entity in UTF-16 code units|url§Optional. For "text_link" only, url that will be opened after user taps on the text|user§Optional. For "text_mention" only, the mentioned user +pyrogram.photoSize#b0700005 flags:# file_id:string width:int height:int file_size:flags.0?int = pyrogram.PhotoSize; // Docs: PhotoSize§This object represents one size of a photo or a file / sticker thumbnail.|file_id§Unique identifier for this file|width§Photo width|height§Photo height|file_size§Optional. File size +pyrogram.audio#b0700006 flags:# file_id:string duration:int performer:flags.0?string title:flags.1?string mime_type:flags.2?string file_size:flags.3?int = pyrogram.Audio; // Docs: Audio§This object represents an audio file to be treated as music by the Telegram clients.|file_id§Unique identifier for this file|duration§Duration of the audio in seconds as defined by sender|performer§Optional. Performer of the audio as defined by sender or by audio tags|title§Optional. Title of the audio as defined by sender or by audio tags|mime_type§Optional. MIME type of the file as defined by sender|file_size§Optional. File size +pyrogram.document#b0700007 flags:# file_id:string thumb:flags.0?PhotoSize file_name:flags.1?string mime_type:flags.2?string file_size:flags.3?int = pyrogram.Document; // Docs: Document§This object represents a general file (as opposed to photos, voice messages and audio files).|file_id§Unique file identifier|thumb§Optional. Document thumbnail as defined by sender|file_name§Optional. Original filename as defined by sender|mime_type§Optional. MIME type of the file as defined by sender|file_size§Optional. File size +pyrogram.video#b0700008 flags:# file_id:string width:int height:int duration:int thumb:flags.0?PhotoSize mime_type:flags.1?string file_size:flags.2?int = pyrogram.Video; // Docs: Video§This object represents a video file.|file_id§Unique identifier for this file|width§Video width as defined by sender|height§Video height as defined by sender|duration§Duration of the video in seconds as defined by sender|thumb§Optional. Video thumbnail|mime_type§Optional. Mime type of a file as defined by sender|file_size§Optional. File size +pyrogram.voice#b0700009 flags:# file_id:string duration:int mime_type:flags.0?string file_size:flags.1?int = pyrogram.Voice; // Docs: Voice§This object represents a voice note.|file_id§Unique identifier for this file|duration§Duration of the audio in seconds as defined by sender|mime_type§Optional. MIME type of the file as defined by sender|file_size§Optional. File size +pyrogram.videoNote#b0700010 flags:# file_id:string length:int duration:int thumb:flags.0?PhotoSize file_size:flags.1?int = pyrogram.VideoNote; // Docs: VideoNote§This object represents a video message (available in Telegram apps as of v.4.0).|file_id§Unique identifier for this file|length§Video width and height as defined by sender|duration§Duration of the video in seconds as defined by sender|thumb§Optional. Video thumbnail|file_size§Optional. File size +pyrogram.contact#b0700011 flags:# phone_number:string first_name:string last_name:flags.0?string user_id:flags.1?int = pyrogram.Contact; // Docs: Contact§This object represents a phone contact.|phone_number§Contact's phone number|first_name§Contact's first name|last_name§Optional. Contact's last name|user_id§Optional. Contact's user identifier in Telegram +pyrogram.location#b0700012 longitude:double latitude:double = pyrogram.Location; // Docs: Location§This object represents a point on the map.|longitude§Longitude as defined by sender|latitude§Latitude as defined by sender +pyrogram.venue#b0700013 flags:# location:Location title:string address:string foursquare_id:flags.0?string = pyrogram.Venue; // Docs: Venue§This object represents a venue.|location§Venue location|title§Name of the venue|address§Address of the venue|foursquare_id§Optional. Foursquare identifier of the venue +pyrogram.userProfilePhotos#b0700014 total_count:int photos:Vector> = pyrogram.UserProfilePhotos; // Docs: UserProfilePhotos§This object represent a user's profile pictures.|total_count§Total number of profile pictures the target user has|photos§Requested profile pictures (in up to 4 sizes each) +pyrogram.chatPhoto#b0700015 small_file_id:string big_file_id:string = pyrogram.ChatPhoto; // Docs: ChatPhoto§This object represents a chat photo.|small_file_id§Unique file identifier of small (160x160) chat photo. This file_id can be used only for photo download.|big_file_id§Unique file identifier of big (640x640) chat photo. This file_id can be used only for photo download. +pyrogram.chatMember#b0700016 flags:# user:User status:string until_date:flags.0?int can_be_edited:flags.1?Bool can_change_info:flags.2?Bool can_post_messages:flags.3?Bool can_edit_messages:flags.4?Bool can_delete_messages:flags.5?Bool can_invite_users:flags.6?Bool can_restrict_members:flags.7?Bool can_pin_messages:flags.8?Bool can_promote_members:flags.9?Bool can_send_messages:flags.10?Bool can_send_media_messages:flags.11?Bool can_send_other_messages:flags.12?Bool can_add_web_page_previews:flags.13?Bool = pyrogram.ChatMember; // Docs: ChatMember§This object contains information about one member of a chat.|user§Information about the user|status§The member's status in the chat. Can be "creator", "administrator", "member", "restricted", "left" or "kicked"|until_date§Optional. Restricted and kicked only. Date when restrictions will be lifted for this user, unix time|can_be_edited§Optional. Administrators only. True, if the bot is allowed to edit administrator privileges of that user|can_change_info§Optional. Administrators only. True, if the administrator can change the chat title, photo and other settings|can_post_messages§Optional. Administrators only. True, if the administrator can post in the channel, channels only|can_edit_messages§Optional. Administrators only. True, if the administrator can edit messages of other users and can pin messages, channels only|can_delete_messages§Optional. Administrators only. True, if the administrator can delete messages of other users|can_invite_users§Optional. Administrators only. True, if the administrator can invite new users to the chat|can_restrict_members§Optional. Administrators only. True, if the administrator can restrict, ban or unban chat members|can_pin_messages§Optional. Administrators only. True, if the administrator can pin messages, supergroups only|can_promote_members§Optional. Administrators only. 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 the user)|can_send_messages§Optional. Restricted only. True, if the user can send text messages, contacts, locations and venues|can_send_media_messages§Optional. Restricted only. True, if the user can send audios, documents, photos, videos, video notes and voice notes, implies can_send_messages|can_send_other_messages§Optional. Restricted only. True, if the user can send animations, games, stickers and use inline bots, implies can_send_media_messages|can_add_web_page_previews§Optional. Restricted only. True, if user may add web page previews to his messages, implies can_send_media_messages From 22c51fdd70b1e501b9707b7eef324c6b06c92ede Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sun, 1 Apr 2018 14:34:29 +0200 Subject: [PATCH 041/112] Fix docstrings generation for Pyrogram types --- compiler/api/compiler.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/compiler/api/compiler.py b/compiler/api/compiler.py index 3e15a2bd..3d5af589 100644 --- a/compiler/api/compiler.py +++ b/compiler/api/compiler.py @@ -38,7 +38,7 @@ types_to_functions = {} constructors_to_functions = {} -def get_docstring_arg_type(t: str, is_list: bool = False): +def get_docstring_arg_type(t: str, is_list: bool = False, is_pyrogram_type: bool = False): if t in core_types: if t == "long": return ":obj:`int` :obj:`64-bit`" @@ -58,13 +58,20 @@ def get_docstring_arg_type(t: str, is_list: bool = False): elif t == "!X": return "Any method from :obj:`pyrogram.api.functions`" elif t.startswith("Vector"): - return "List of " + get_docstring_arg_type(t.split("<")[1][:-1], is_list=True) + return "List of " + get_docstring_arg_type(t.split("<", 1)[1][:-1], True, is_pyrogram_type) else: + if is_pyrogram_type: + t = "pyrogram." + t + t = types_to_constructors.get(t, [t]) + n = len(t) - 1 t = (("e" if is_list else "E") + "ither " if n else "") + ", ".join( - ":obj:`{0} `".format(i) + ":obj:`{1} `".format( + "pyrogram." if is_pyrogram_type else "", + i.lstrip("pyrogram.") + ) for i in t ) @@ -280,7 +287,7 @@ def start(): docstring_args.append( "{} ({}{}):\n {}\n".format( arg_name, - get_docstring_arg_type(arg_type), + get_docstring_arg_type(arg_type, is_pyrogram_type=True), ", optional" if "Optional" in docs[i] else "", re.sub("Optional\. ", "", docs[i].split("§")[1].rstrip(".") + ".") ) From 124ec403007fb53cf7f080bc0fa5f80d2857f522 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 3 Apr 2018 10:54:29 +0200 Subject: [PATCH 042/112] Inherit again from Object --- compiler/api/template/pyrogram.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/api/template/pyrogram.txt b/compiler/api/template/pyrogram.txt index f5f7a96a..adbe4151 100644 --- a/compiler/api/template/pyrogram.txt +++ b/compiler/api/template/pyrogram.txt @@ -1,7 +1,8 @@ {notice} +from pyrogram.api.core import Object -class {class_name}: +class {class_name}(Object): """{docstring_args} """ ID = {object_id} From 88292cf7d6231ccfe3177a698f6d5a01d78f01dc Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 3 Apr 2018 14:44:24 +0200 Subject: [PATCH 043/112] Implement __bool__ --- pyrogram/api/core/object.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyrogram/api/core/object.py b/pyrogram/api/core/object.py index a74eb876..24c1dcf1 100644 --- a/pyrogram/api/core/object.py +++ b/pyrogram/api/core/object.py @@ -37,6 +37,9 @@ class Object: def __str__(self) -> str: return dumps(self, cls=Encoder, indent=4) + def __bool__(self) -> bool: + return True + def __eq__(self, other) -> bool: return self.__dict__ == other.__dict__ From 6201f6b1f71c8e8b12d352d464248b152465bd4a Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Wed, 4 Apr 2018 20:15:59 +0200 Subject: [PATCH 044/112] Add a bunch of TODOs --- pyrogram/client/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py index b353250b..73247722 100644 --- a/pyrogram/client/utils.py +++ b/pyrogram/client/utils.py @@ -15,6 +15,7 @@ ENTITIES = { types.MessageEntityCode.ID: "code", types.MessageEntityPre.ID: "pre", types.MessageEntityTextUrl.ID: "text_link", + # TODO: text_mention } @@ -83,6 +84,7 @@ def parse_channel_chat(channel: types.Channel): ) +# TODO: Reorganize code, maybe split parts as well def parse_message(message: types.Message, users: dict, chats: dict): entities = parse_entities(message.entities) From 7ba523600e244da0c7840e84553180d5d5b0284a Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Wed, 4 Apr 2018 20:21:07 +0200 Subject: [PATCH 045/112] Handle Location type --- pyrogram/client/utils.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py index 73247722..45d15489 100644 --- a/pyrogram/client/utils.py +++ b/pyrogram/client/utils.py @@ -107,6 +107,7 @@ def parse_message(message: types.Message, users: dict, chats: dict): forward_signature = forward_header.post_author photo = None + location = None media = message.media @@ -149,6 +150,14 @@ def parse_message(message: types.Message, users: dict, chats: dict): photo_sizes.append(photo_size) photo = photo_sizes + elif isinstance(media, types.MessageMediaGeo): + geo_point = media.geo + + if isinstance(geo_point, types.GeoPoint): + location = pyrogram.Location( + longitude=geo_point.long, + latitude=geo_point.lat + ) return pyrogram.Message( message_id=message.id, @@ -166,7 +175,8 @@ def parse_message(message: types.Message, users: dict, chats: dict): forward_signature=forward_signature, forward_date=forward_date, edit_date=message.edit_date, - photo=photo + photo=photo, + location=location ) From 156afd980554ff179ac03372401076ee729aa270 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Wed, 4 Apr 2018 20:28:05 +0200 Subject: [PATCH 046/112] Handle Contact type --- pyrogram/client/utils.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py index 45d15489..98f605a5 100644 --- a/pyrogram/client/utils.py +++ b/pyrogram/client/utils.py @@ -108,6 +108,7 @@ def parse_message(message: types.Message, users: dict, chats: dict): photo = None location = None + contact = None media = message.media @@ -158,6 +159,13 @@ def parse_message(message: types.Message, users: dict, chats: dict): longitude=geo_point.long, latitude=geo_point.lat ) + elif isinstance(media, types.MessageMediaContact): + contact = pyrogram.Contact( + phone_number=media.phone_number, + first_name=media.first_name, + last_name=media.last_name, + user_id=media.user_id + ) return pyrogram.Message( message_id=message.id, @@ -176,7 +184,8 @@ def parse_message(message: types.Message, users: dict, chats: dict): forward_date=forward_date, edit_date=message.edit_date, photo=photo, - location=location + location=location, + contact=contact ) From c49c8c0ce665b2f5e66f99e009490547451ca4e8 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Wed, 4 Apr 2018 22:23:20 +0200 Subject: [PATCH 047/112] Handle Audio type --- pyrogram/client/utils.py | 70 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 7 deletions(-) diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py index 98f605a5..196e795b 100644 --- a/pyrogram/client/utils.py +++ b/pyrogram/client/utils.py @@ -84,6 +84,35 @@ def parse_channel_chat(channel: types.Channel): ) +def parse_thumb(thumb: types.PhotoSize or types.PhotoCachedSize): + if isinstance(thumb, (types.PhotoSize, types.PhotoCachedSize)): + loc = thumb.location + + if isinstance(thumb, types.PhotoSize): + file_size = thumb.size + else: + file_size = len(thumb.bytes) + + if isinstance(loc, types.FileLocation): + return pyrogram.PhotoSize( + file_id=encode( + pack( + " Date: Wed, 4 Apr 2018 22:24:09 +0200 Subject: [PATCH 048/112] Handle Video and VideoNote type --- pyrogram/client/utils.py | 41 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py index 196e795b..1f24373d 100644 --- a/pyrogram/client/utils.py +++ b/pyrogram/client/utils.py @@ -139,6 +139,8 @@ def parse_message(message: types.Message, users: dict, chats: dict): location = None contact = None audio = None + video = None + video_note = None media = message.media @@ -221,6 +223,43 @@ def parse_message(message: types.Message, users: dict, chats: dict): mime_type=doc.mime_type, file_size=doc.size ) + elif types.DocumentAttributeVideo in attributes: + video_attributes = attributes[types.DocumentAttributeVideo] + + if video_attributes.round_message: + video_note = pyrogram.VideoNote( + file_id=encode( + pack( + " Date: Wed, 4 Apr 2018 22:27:05 +0200 Subject: [PATCH 049/112] Handle Voice type --- pyrogram/client/utils.py | 50 +++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py index 1f24373d..93f8b4ee 100644 --- a/pyrogram/client/utils.py +++ b/pyrogram/client/utils.py @@ -139,6 +139,7 @@ def parse_message(message: types.Message, users: dict, chats: dict): location = None contact = None audio = None + voice = None video = None video_note = None @@ -207,22 +208,38 @@ def parse_message(message: types.Message, users: dict, chats: dict): if types.DocumentAttributeAudio in attributes: audio_attributes = attributes[types.DocumentAttributeAudio] - audio = pyrogram.Audio( - file_id=encode( - pack( - " Date: Wed, 4 Apr 2018 22:37:22 +0200 Subject: [PATCH 050/112] Add Sticker type --- compiler/api/source/pyrogram.tl | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/api/source/pyrogram.tl b/compiler/api/source/pyrogram.tl index 491e890c..0f580808 100644 --- a/compiler/api/source/pyrogram.tl +++ b/compiler/api/source/pyrogram.tl @@ -19,3 +19,4 @@ pyrogram.venue#b0700013 flags:# location:Location title:string address:string fo pyrogram.userProfilePhotos#b0700014 total_count:int photos:Vector> = pyrogram.UserProfilePhotos; // Docs: UserProfilePhotos§This object represent a user's profile pictures.|total_count§Total number of profile pictures the target user has|photos§Requested profile pictures (in up to 4 sizes each) pyrogram.chatPhoto#b0700015 small_file_id:string big_file_id:string = pyrogram.ChatPhoto; // Docs: ChatPhoto§This object represents a chat photo.|small_file_id§Unique file identifier of small (160x160) chat photo. This file_id can be used only for photo download.|big_file_id§Unique file identifier of big (640x640) chat photo. This file_id can be used only for photo download. pyrogram.chatMember#b0700016 flags:# user:User status:string until_date:flags.0?int can_be_edited:flags.1?Bool can_change_info:flags.2?Bool can_post_messages:flags.3?Bool can_edit_messages:flags.4?Bool can_delete_messages:flags.5?Bool can_invite_users:flags.6?Bool can_restrict_members:flags.7?Bool can_pin_messages:flags.8?Bool can_promote_members:flags.9?Bool can_send_messages:flags.10?Bool can_send_media_messages:flags.11?Bool can_send_other_messages:flags.12?Bool can_add_web_page_previews:flags.13?Bool = pyrogram.ChatMember; // Docs: ChatMember§This object contains information about one member of a chat.|user§Information about the user|status§The member's status in the chat. Can be "creator", "administrator", "member", "restricted", "left" or "kicked"|until_date§Optional. Restricted and kicked only. Date when restrictions will be lifted for this user, unix time|can_be_edited§Optional. Administrators only. True, if the bot is allowed to edit administrator privileges of that user|can_change_info§Optional. Administrators only. True, if the administrator can change the chat title, photo and other settings|can_post_messages§Optional. Administrators only. True, if the administrator can post in the channel, channels only|can_edit_messages§Optional. Administrators only. True, if the administrator can edit messages of other users and can pin messages, channels only|can_delete_messages§Optional. Administrators only. True, if the administrator can delete messages of other users|can_invite_users§Optional. Administrators only. True, if the administrator can invite new users to the chat|can_restrict_members§Optional. Administrators only. True, if the administrator can restrict, ban or unban chat members|can_pin_messages§Optional. Administrators only. True, if the administrator can pin messages, supergroups only|can_promote_members§Optional. Administrators only. 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 the user)|can_send_messages§Optional. Restricted only. True, if the user can send text messages, contacts, locations and venues|can_send_media_messages§Optional. Restricted only. True, if the user can send audios, documents, photos, videos, video notes and voice notes, implies can_send_messages|can_send_other_messages§Optional. Restricted only. True, if the user can send animations, games, stickers and use inline bots, implies can_send_media_messages|can_add_web_page_previews§Optional. Restricted only. True, if user may add web page previews to his messages, implies can_send_media_messages +pyrogram.sticker#b0700017 flags:# file_id:string width:int height:int thumb:flags.0?PhotoSize emoji:flags.1?string set_name:flags.2?string mask_position:flags.3?MaskPosition file_size:flags.4?int = pyrogram.Sticker; // Docs: Sticker§This object represents a sticker.|file_id§Unique identifier for this file|width§Sticker width|height§Sticker height|thumb§Optional. Sticker thumbnail in the .webp or .jpg format|emoji§Optional. Emoji associated with the sticker|set_name§Optional. Name of the sticker set to which the sticker belongs|mask_position§Optional. For mask stickers, the position where the mask should be placed|file_size§Optional. File size From d5fe82687cb60815eb4fe1b56aeafa934bd4b91a Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Wed, 4 Apr 2018 22:37:40 +0200 Subject: [PATCH 051/112] Handle Sticker type --- pyrogram/client/utils.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py index 93f8b4ee..6a5042a8 100644 --- a/pyrogram/client/utils.py +++ b/pyrogram/client/utils.py @@ -142,6 +142,7 @@ def parse_message(message: types.Message, users: dict, chats: dict): voice = None video = None video_note = None + sticker = None media = message.media @@ -277,6 +278,25 @@ def parse_message(message: types.Message, users: dict, chats: dict): mime_type=doc.mime_type, file_size=doc.size ) + elif types.DocumentAttributeSticker in attributes: + image_size_attribute = attributes[types.DocumentAttributeImageSize] + + sticker = pyrogram.Sticker( + file_id=encode( + pack( + " Date: Wed, 4 Apr 2018 22:42:30 +0200 Subject: [PATCH 052/112] Handle media_group_id --- pyrogram/client/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py index 6a5042a8..6b98ad93 100644 --- a/pyrogram/client/utils.py +++ b/pyrogram/client/utils.py @@ -314,6 +314,7 @@ def parse_message(message: types.Message, users: dict, chats: dict): forward_signature=forward_signature, forward_date=forward_date, edit_date=message.edit_date, + media_group_id=message.grouped_id, photo=photo, location=location, contact=contact, From 2acb38649d84f2231239f769f1117a9acc0e8d78 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Wed, 4 Apr 2018 22:47:52 +0200 Subject: [PATCH 053/112] Add some TODOs --- pyrogram/client/utils.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py index 6b98ad93..80c9bd11 100644 --- a/pyrogram/client/utils.py +++ b/pyrogram/client/utils.py @@ -364,10 +364,14 @@ def parse_message_service(message: types.MessageService, users: dict, chats: dic new_chat_members=new_chat_members, left_chat_member=left_chat_member, new_chat_title=new_chat_title, + # TODO: new_chat_photo delete_chat_photo=delete_chat_photo, migrate_to_chat_id=migrate_to_chat_id, migrate_from_chat_id=migrate_from_chat_id, group_chat_created=group_chat_created + # TODO: supergroup_chat_created + # TODO: channel_chat_created + # TODO: pinned_message ) From f1a8cd1038c2fcec885501df17f9132e5bade805 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Wed, 4 Apr 2018 22:48:14 +0200 Subject: [PATCH 054/112] Rename to image_size_attributes --- pyrogram/client/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py index 80c9bd11..283dc48f 100644 --- a/pyrogram/client/utils.py +++ b/pyrogram/client/utils.py @@ -279,7 +279,7 @@ def parse_message(message: types.Message, users: dict, chats: dict): file_size=doc.size ) elif types.DocumentAttributeSticker in attributes: - image_size_attribute = attributes[types.DocumentAttributeImageSize] + image_size_attributes = attributes[types.DocumentAttributeImageSize] sticker = pyrogram.Sticker( file_id=encode( @@ -291,8 +291,8 @@ def parse_message(message: types.Message, users: dict, chats: dict): doc.access_hash ) ), - width=image_size_attribute.w, - height=image_size_attribute.h, + width=image_size_attributes.w, + height=image_size_attributes.h, thumb=parse_thumb(doc.thumb), # TODO: Emoji, set_name and mask_position file_size=doc.size, From 2fcd8ea54e8e72ef3ac25f259e2964c1ccc00c9c Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Wed, 4 Apr 2018 23:59:30 +0200 Subject: [PATCH 055/112] Handle GIF and Document type --- pyrogram/client/utils.py | 46 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py index 283dc48f..29b7e038 100644 --- a/pyrogram/client/utils.py +++ b/pyrogram/client/utils.py @@ -143,6 +143,7 @@ def parse_message(message: types.Message, users: dict, chats: dict): video = None video_note = None sticker = None + document = None media = message.media @@ -241,7 +242,7 @@ def parse_message(message: types.Message, users: dict, chats: dict): mime_type=doc.mime_type, file_size=doc.size ) - elif types.DocumentAttributeVideo in attributes: + elif types.DocumentAttributeVideo in attributes and types.DocumentAttributeAnimated not in attributes: video_attributes = attributes[types.DocumentAttributeVideo] if video_attributes.round_message: @@ -297,6 +298,46 @@ def parse_message(message: types.Message, users: dict, chats: dict): # TODO: Emoji, set_name and mask_position file_size=doc.size, ) + elif types.DocumentAttributeAnimated in attributes: + document = pyrogram.Document( + file_id=encode( + pack( + " Date: Thu, 5 Apr 2018 00:19:13 +0200 Subject: [PATCH 056/112] Move pyrogram types import on the top --- pyrogram/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyrogram/__init__.py b/pyrogram/__init__.py index 53686f94..41c743e3 100644 --- a/pyrogram/__init__.py +++ b/pyrogram/__init__.py @@ -26,10 +26,10 @@ __license__ = "GNU Lesser General Public License v3 or later (LGPLv3+)" __version__ = "0.6.5" from .api.errors import Error +from .api.types.pyrogram import * from .client import ChatAction from .client import Client from .client import ParseMode from .client.input_media import InputMedia from .client.input_phone_contact import InputPhoneContact from .client import Emoji -from .api.types.pyrogram import * From feece7e633f3010c22c85b73847628d8c15b735a Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Thu, 5 Apr 2018 00:23:38 +0200 Subject: [PATCH 057/112] Add return types (function annotations) --- pyrogram/client/utils.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py index 29b7e038..da86a104 100644 --- a/pyrogram/client/utils.py +++ b/pyrogram/client/utils.py @@ -19,7 +19,7 @@ ENTITIES = { } -def parse_entities(entities: list): +def parse_entities(entities: list) -> list: output_entities = [] for entity in entities: @@ -36,7 +36,7 @@ def parse_entities(entities: list): return output_entities -def parse_user(user: types.User): +def parse_user(user: types.User) -> pyrogram.User or None: return pyrogram.User( id=user.id, is_bot=user.bot, @@ -47,7 +47,7 @@ def parse_user(user: types.User): ) if user else None -def parse_chat(message: types.Message, users: dict, chats: dict): +def parse_chat(message: types.Message, users: dict, chats: dict) -> pyrogram.Chat: if isinstance(message.to_id, types.PeerUser): return parse_user_chat(users[message.to_id.user_id if message.out else message.from_id]) elif isinstance(message.to_id, types.PeerChat): @@ -56,7 +56,7 @@ def parse_chat(message: types.Message, users: dict, chats: dict): return parse_channel_chat(chats[message.to_id.channel_id]) -def parse_user_chat(user: types.User): +def parse_user_chat(user: types.User) -> pyrogram.Chat: return pyrogram.Chat( id=user.id, type="private", @@ -66,7 +66,7 @@ def parse_user_chat(user: types.User): ) -def parse_chat_chat(chat: types.Chat): +def parse_chat_chat(chat: types.Chat) -> pyrogram.Chat: return pyrogram.Chat( id=-chat.id, type="group", @@ -75,7 +75,7 @@ def parse_chat_chat(chat: types.Chat): ) -def parse_channel_chat(channel: types.Channel): +def parse_channel_chat(channel: types.Channel) -> pyrogram.Chat: return pyrogram.Chat( id=int("-100" + str(channel.id)), type="supergroup" if channel.megagroup else "channel", @@ -84,7 +84,7 @@ def parse_channel_chat(channel: types.Channel): ) -def parse_thumb(thumb: types.PhotoSize or types.PhotoCachedSize): +def parse_thumb(thumb: types.PhotoSize or types.PhotoCachedSize) -> pyrogram.PhotoSize or None: if isinstance(thumb, (types.PhotoSize, types.PhotoCachedSize)): loc = thumb.location @@ -114,7 +114,7 @@ def parse_thumb(thumb: types.PhotoSize or types.PhotoCachedSize): # TODO: Reorganize code, maybe split parts as well -def parse_message(message: types.Message, users: dict, chats: dict): +def parse_message(message: types.Message, users: dict, chats: dict) -> pyrogram.Message: entities = parse_entities(message.entities) forward_from = None @@ -368,7 +368,7 @@ def parse_message(message: types.Message, users: dict, chats: dict): ) -def parse_message_service(message: types.MessageService, users: dict, chats: dict): +def parse_message_service(message: types.MessageService, users: dict, chats: dict) -> pyrogram.Message or None: action = message.action new_chat_members = None @@ -417,7 +417,7 @@ def parse_message_service(message: types.MessageService, users: dict, chats: dic ) -def decode(s): +def decode(s: str) -> bytes: s = b64decode(s + "=" * (-len(s) % 4), "-_") r = b"" @@ -436,7 +436,7 @@ def decode(s): return r -def encode(s): +def encode(s: bytes) -> str: r = b"" n = 0 From 4adc55a0704df7e1a9feea4ebba90047aa63d56b Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Thu, 5 Apr 2018 08:22:27 +0200 Subject: [PATCH 058/112] Move GIF handling before Video to avoid an extra check --- pyrogram/client/utils.py | 42 ++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py index da86a104..43dba6d4 100644 --- a/pyrogram/client/utils.py +++ b/pyrogram/client/utils.py @@ -242,7 +242,27 @@ def parse_message(message: types.Message, users: dict, chats: dict) -> pyrogram. mime_type=doc.mime_type, file_size=doc.size ) - elif types.DocumentAttributeVideo in attributes and types.DocumentAttributeAnimated not in attributes: + elif types.DocumentAttributeAnimated in attributes: + document = pyrogram.Document( + file_id=encode( + pack( + " pyrogram. # TODO: Emoji, set_name and mask_position file_size=doc.size, ) - elif types.DocumentAttributeAnimated in attributes: - document = pyrogram.Document( - file_id=encode( - pack( - " Date: Thu, 5 Apr 2018 08:26:50 +0200 Subject: [PATCH 059/112] Don't return None This will break Message Pin handling --- pyrogram/client/client.py | 3 --- pyrogram/client/utils.py | 5 +---- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py index 290a8ba9..ebb87dd3 100644 --- a/pyrogram/client/client.py +++ b/pyrogram/client/client.py @@ -730,9 +730,6 @@ class Client: elif isinstance(message, types.MessageService): m = utils.parse_message_service(message, users, chats) - if m is None: - continue - if isinstance(message.action, types.MessageActionPinMessage): pm = self.get_messages(m.chat.id, [message.reply_to_msg_id]) diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py index 43dba6d4..68af6b84 100644 --- a/pyrogram/client/utils.py +++ b/pyrogram/client/utils.py @@ -368,7 +368,7 @@ def parse_message(message: types.Message, users: dict, chats: dict) -> pyrogram. ) -def parse_message_service(message: types.MessageService, users: dict, chats: dict) -> pyrogram.Message or None: +def parse_message_service(message: types.MessageService, users: dict, chats: dict) -> pyrogram.Message: action = message.action new_chat_members = None @@ -395,8 +395,6 @@ def parse_message_service(message: types.MessageService, users: dict, chats: dic migrate_from_chat_id = action.chat_id elif isinstance(action, types.MessageActionChatCreate): group_chat_created = True - else: - return None return pyrogram.Message( message_id=message.id, @@ -413,7 +411,6 @@ def parse_message_service(message: types.MessageService, users: dict, chats: dic group_chat_created=group_chat_created # TODO: supergroup_chat_created # TODO: channel_chat_created - # TODO: pinned_message ) From 195cd22ebb0360c70f58b913b11ec94ed779a4b3 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Thu, 5 Apr 2018 10:49:20 +0200 Subject: [PATCH 060/112] Better handling of reply and pin messages --- pyrogram/client/client.py | 32 ++------------------------ pyrogram/client/utils.py | 48 +++++++++++++++++++++++++++++++++++---- 2 files changed, 46 insertions(+), 34 deletions(-) diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py index ebb87dd3..9afd7b53 100644 --- a/pyrogram/client/client.py +++ b/pyrogram/client/client.py @@ -712,37 +712,9 @@ class Client: message = update.message if isinstance(message, types.Message): - m = utils.parse_message(message, users, chats) - - if message.reply_to_msg_id: - rm = self.get_messages(m.chat.id, [message.reply_to_msg_id]) - - message = rm.messages[0] - - if isinstance(message, types.Message): - m.reply_to_message = utils.parse_message( - message, - {i.id: i for i in rm.users}, - {i.id: i for i in rm.chats} - ) - else: - continue + m = utils.parse_message(self, message, users, chats) elif isinstance(message, types.MessageService): - m = utils.parse_message_service(message, users, chats) - - if isinstance(message.action, types.MessageActionPinMessage): - pm = self.get_messages(m.chat.id, [message.reply_to_msg_id]) - - message = pm.messages[0] - - if isinstance(message, types.Message): - m.pinned_message = utils.parse_message( - message, - {i.id: i for i in pm.users}, - {i.id: i for i in pm.chats} - ) - else: - continue + m = utils.parse_message_service(self, message, users, chats) else: continue else: diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py index 68af6b84..5fe04ee5 100644 --- a/pyrogram/client/utils.py +++ b/pyrogram/client/utils.py @@ -114,7 +114,13 @@ def parse_thumb(thumb: types.PhotoSize or types.PhotoCachedSize) -> pyrogram.Pho # TODO: Reorganize code, maybe split parts as well -def parse_message(message: types.Message, users: dict, chats: dict) -> pyrogram.Message: +def parse_message( + client, + message: types.Message, + users: dict, + chats: dict, + replies: int = 1 +) -> pyrogram.Message: entities = parse_entities(message.entities) forward_from = None @@ -339,7 +345,7 @@ def parse_message(message: types.Message, users: dict, chats: dict) -> pyrogram. file_size=doc.size ) - return pyrogram.Message( + m = pyrogram.Message( message_id=message.id, date=message.date, chat=parse_chat(message, users, chats), @@ -367,8 +373,27 @@ def parse_message(message: types.Message, users: dict, chats: dict) -> pyrogram. document=document ) + if message.reply_to_msg_id and replies: + reply_to_message = client.get_messages(m.chat.id, [message.reply_to_msg_id]) -def parse_message_service(message: types.MessageService, users: dict, chats: dict) -> pyrogram.Message: + message = reply_to_message.messages[0] + users = {i.id: i for i in reply_to_message.users} + chats = {i.id: i for i in reply_to_message.chats} + + if isinstance(message, types.Message): + m.reply_to_message = parse_message(client, message, users, chats, replies - 1) + elif isinstance(message, types.MessageService): + m.reply_to_message = parse_message_service(client, message, users, chats) + + return m + + +def parse_message_service( + client, + message: types.MessageService, + users: dict, + chats: dict +) -> pyrogram.Message: action = message.action new_chat_members = None @@ -396,7 +421,7 @@ def parse_message_service(message: types.MessageService, users: dict, chats: dic elif isinstance(action, types.MessageActionChatCreate): group_chat_created = True - return pyrogram.Message( + m = pyrogram.Message( message_id=message.id, date=message.date, chat=parse_chat(message, users, chats), @@ -413,6 +438,21 @@ def parse_message_service(message: types.MessageService, users: dict, chats: dic # TODO: channel_chat_created ) + if isinstance(action, types.MessageActionPinMessage): + pin_message = client.get_messages(m.chat.id, [message.reply_to_msg_id]) + + message = pin_message.messages[0] + users = {i.id: i for i in pin_message.users} + chats = {i.id: i for i in pin_message.chats} + + if isinstance(message, types.Message): + m.pinned_message = parse_message(client, message, users, chats) + elif isinstance(message, types.MessageService): + # TODO: We can't pin a service message, can we? + m.pinned_message = parse_message_service(client, message, users, chats) + + return m + def decode(s: str) -> bytes: s = b64decode(s + "=" * (-len(s) % 4), "-_") From 7483d3df37d4916de64dec4a50d1223b44c5ba1c Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Thu, 5 Apr 2018 10:59:12 +0200 Subject: [PATCH 061/112] Use correct chat ids --- pyrogram/client/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py index 5fe04ee5..25511426 100644 --- a/pyrogram/client/utils.py +++ b/pyrogram/client/utils.py @@ -431,8 +431,8 @@ def parse_message_service( new_chat_title=new_chat_title, # TODO: new_chat_photo delete_chat_photo=delete_chat_photo, - migrate_to_chat_id=migrate_to_chat_id, - migrate_from_chat_id=migrate_from_chat_id, + migrate_to_chat_id=int("-100" + str(migrate_to_chat_id)) if migrate_to_chat_id else None, + migrate_from_chat_id=-migrate_from_chat_id if migrate_from_chat_id else None, group_chat_created=group_chat_created # TODO: supergroup_chat_created # TODO: channel_chat_created From 65e3852706b73fe352093da3a8f5dd7a92f87002 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Thu, 5 Apr 2018 10:59:44 +0200 Subject: [PATCH 062/112] Add channel_chat_created field --- pyrogram/client/utils.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py index 25511426..f6a3e5c2 100644 --- a/pyrogram/client/utils.py +++ b/pyrogram/client/utils.py @@ -403,6 +403,7 @@ def parse_message_service( migrate_to_chat_id = None migrate_from_chat_id = None group_chat_created = None + channel_chat_created = None if isinstance(action, types.MessageActionChatAddUser): new_chat_members = [parse_user(users[i]) for i in action.users] @@ -420,6 +421,8 @@ def parse_message_service( migrate_from_chat_id = action.chat_id elif isinstance(action, types.MessageActionChatCreate): group_chat_created = True + elif isinstance(action, types.MessageActionChannelCreate): + channel_chat_created = True m = pyrogram.Message( message_id=message.id, @@ -433,9 +436,9 @@ def parse_message_service( delete_chat_photo=delete_chat_photo, migrate_to_chat_id=int("-100" + str(migrate_to_chat_id)) if migrate_to_chat_id else None, migrate_from_chat_id=-migrate_from_chat_id if migrate_from_chat_id else None, - group_chat_created=group_chat_created + group_chat_created=group_chat_created, + channel_chat_created=channel_chat_created # TODO: supergroup_chat_created - # TODO: channel_chat_created ) if isinstance(action, types.MessageActionPinMessage): From fad0e7a26d4084ec252cbe2bafd65fd092a6cbbd Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Thu, 5 Apr 2018 11:17:40 +0200 Subject: [PATCH 063/112] Add new_chat_photo field --- pyrogram/client/utils.py | 41 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py index f6a3e5c2..60c11978 100644 --- a/pyrogram/client/utils.py +++ b/pyrogram/client/utils.py @@ -404,6 +404,7 @@ def parse_message_service( migrate_from_chat_id = None group_chat_created = None channel_chat_created = None + new_chat_photo = None if isinstance(action, types.MessageActionChatAddUser): new_chat_members = [parse_user(users[i]) for i in action.users] @@ -423,6 +424,44 @@ def parse_message_service( group_chat_created = True elif isinstance(action, types.MessageActionChannelCreate): channel_chat_created = True + elif isinstance(action, types.MessageActionChatEditPhoto): + photo = action.photo + + if isinstance(photo, types.Photo): + sizes = photo.sizes + photo_sizes = [] + + for size in sizes: + if isinstance(size, (types.PhotoSize, types.PhotoCachedSize)): + loc = size.location + + if isinstance(size, types.PhotoSize): + file_size = size.size + else: + file_size = len(size.bytes) + + if isinstance(loc, types.FileLocation): + photo_size = pyrogram.PhotoSize( + file_id=encode( + pack( + " Date: Thu, 5 Apr 2018 20:18:04 +0200 Subject: [PATCH 064/112] Keep InputMediaPhoto separated --- pyrogram/client/input_media_photo.py | 44 ++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 pyrogram/client/input_media_photo.py diff --git a/pyrogram/client/input_media_photo.py b/pyrogram/client/input_media_photo.py new file mode 100644 index 00000000..f1fe6acb --- /dev/null +++ b/pyrogram/client/input_media_photo.py @@ -0,0 +1,44 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2018 Dan Tès +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + + +class InputMediaPhoto: + """This object represents a photo to be sent inside an album. + It is intended to be used with :obj:`send_media_group `. + + Args: + media (:obj:`str`): + Photo file to send. + Pass a file path as string to send a photo that exists on your local machine. + + caption (:obj:`str`): + Caption of the photo to be sent, 0-200 characters + + parse_mode (:obj:`str`): + Use :obj:`MARKDOWN ` or :obj:`HTML ` + if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your caption. + Defaults to Markdown. + """ + + def __init__(self, + media: str, + caption: str = "", + parse_mode: str = ""): + self.media = media + self.caption = caption + self.parse_mode = parse_mode From 35938869171d183878619e40bb00162faeb3c4b4 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Thu, 5 Apr 2018 20:19:37 +0200 Subject: [PATCH 065/112] Keep InputMediaVideo separated as well --- pyrogram/client/input_media_video.py | 64 ++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 pyrogram/client/input_media_video.py diff --git a/pyrogram/client/input_media_video.py b/pyrogram/client/input_media_video.py new file mode 100644 index 00000000..c14767e5 --- /dev/null +++ b/pyrogram/client/input_media_video.py @@ -0,0 +1,64 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2018 Dan Tès +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + + +class InputMediaVideo: + """This object represents a video to be sent inside an album. + It is intended to be used with :obj:`send_media_group `. + + Args: + media (:obj:`str`): + Video file to send. + Pass a file path as string to send a video that exists on your local machine. + + caption (:obj:`str`, optional): + Caption of the video to be sent, 0-200 characters + + parse_mode (:obj:`str`, optional): + Use :obj:`MARKDOWN ` or :obj:`HTML ` + if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your caption. + Defaults to Markdown. + + width (:obj:`int`, optional): + Video width. + + height (:obj:`int`, optional): + Video height. + + duration (:obj:`int`, optional): + Video duration. + + supports_streaming (:obj:`bool`, optional): + Pass True, if the uploaded video is suitable for streaming. + """ + + def __init__(self, + media: str, + caption: str = "", + parse_mode: str = "", + width: int = 0, + height: int = 0, + duration: int = 0, + supports_streaming: bool = True): + self.media = media + self.caption = caption + self.parse_mode = parse_mode + self.width = width + self.height = height + self.duration = duration + self.supports_streaming = supports_streaming From 1fe2b11a0a8856575c05d73c99919e1c4bbbd7dc Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Thu, 5 Apr 2018 20:21:02 +0200 Subject: [PATCH 066/112] Update imports --- pyrogram/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyrogram/__init__.py b/pyrogram/__init__.py index 41c743e3..0d6595fa 100644 --- a/pyrogram/__init__.py +++ b/pyrogram/__init__.py @@ -30,6 +30,7 @@ from .api.types.pyrogram import * from .client import ChatAction from .client import Client from .client import ParseMode -from .client.input_media import InputMedia +from .client.input_media_photo import InputMediaPhoto +from .client.input_media_video import InputMediaVideo from .client.input_phone_contact import InputPhoneContact from .client import Emoji From 23c0d2b9ceb3890e0ce7fa2e853647f1929159ed Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Thu, 5 Apr 2018 20:35:25 +0200 Subject: [PATCH 067/112] Handle Venue type --- pyrogram/client/utils.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py index 60c11978..cf81c68d 100644 --- a/pyrogram/client/utils.py +++ b/pyrogram/client/utils.py @@ -144,6 +144,7 @@ def parse_message( photo = None location = None contact = None + venue = None audio = None voice = None video = None @@ -207,6 +208,16 @@ def parse_message( last_name=media.last_name, user_id=media.user_id ) + elif isinstance(media, types.MessageMediaVenue): + venue = pyrogram.Venue( + location=pyrogram.Location( + longitude=media.geo.long, + latitude=media.geo.lat + ), + title=media.title, + address=media.address, + foursquare_id=media.venue_id + ) elif isinstance(media, types.MessageMediaDocument): doc = media.document @@ -365,6 +376,7 @@ def parse_message( photo=photo, location=location, contact=contact, + venue=venue, audio=audio, voice=voice, video=video, From 3229d36556f0bf6e33edbd6221bde55f65cdb46d Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Thu, 5 Apr 2018 20:43:21 +0200 Subject: [PATCH 068/112] Remove docstrings for now --- compiler/api/source/pyrogram.tl | 36 ++++++++++++++++----------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/compiler/api/source/pyrogram.tl b/compiler/api/source/pyrogram.tl index 0f580808..e28b7c27 100644 --- a/compiler/api/source/pyrogram.tl +++ b/compiler/api/source/pyrogram.tl @@ -2,21 +2,21 @@ ---types--- -pyrogram.update#b0700000 flags:# update_id:int message:flags.0?Message edited_message:flags.1?Message channel_post:flags.2?Message edited_channel_post:flags.3?Message inline_query:flags.4?InlineQuery chosen_inline_result:flags.5?ChosenInlineResult callback_query:flags.6?CallbackQuery shipping_query:flags.7?ShippingQuery pre_checkout_query:flags.8?PreCheckoutQuery = pyrogram.Update; // Docs: Update§This object represents an incoming update.At most one of the optional parameters can be present in any given update.|update_id§The update's unique identifier. Update identifiers start from a certain positive number and increase sequentially. This ID becomes especially handy if you're using Webhooks, since it allows you to ignore repeated updates or to restore the correct update sequence, should they get out of order. If there are no new updates for at least a week, then identifier of the next update will be chosen randomly instead of sequentially.|message§Optional. New incoming message of any kind — text, photo, sticker, etc.|edited_message§Optional. New version of a message that is known to the bot and was edited|channel_post§Optional. New incoming channel post of any kind — text, photo, sticker, etc.|edited_channel_post§Optional. New version of a channel post that is known to the bot and was edited|inline_query§Optional. New incoming inline query|chosen_inline_result§Optional. The result of an inline query that was chosen by a user and sent to their chat partner. Please see our documentation on the feedback collecting for details on how to enable these updates for your bot.|callback_query§Optional. New incoming callback query|shipping_query§Optional. New incoming shipping query. Only for invoices with flexible price|pre_checkout_query§Optional. New incoming pre-checkout query. Contains full information about checkout -pyrogram.user#b0700001 flags:# id:int is_bot:Bool first_name:string last_name:flags.0?string username:flags.1?string language_code:flags.2?string = pyrogram.User; // Docs: User§This object represents a Telegram user or bot.|id§Unique identifier for this user or bot|is_bot§True, if this user is a bot|first_name§User's or bot's first name|last_name§Optional. User's or bot's last name|username§Optional. User's or bot's username|language_code§Optional. IETF language tag of the user's language -pyrogram.chat#b0700002 flags:# id:int type:string title:flags.0?string username:flags.1?string first_name:flags.2?string last_name:flags.3?string all_members_are_administrators:flags.4?Bool photo:flags.5?ChatPhoto description:flags.6?string invite_link:flags.7?string pinned_message:flags.8?Message sticker_set_name:flags.9?string can_set_sticker_set:flags.10?Bool = pyrogram.Chat; // Docs: Chat§This object represents a chat.|id§Unique identifier for this chat. This number may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier.|type§Type of chat, can be either "private", "group", "supergroup" or "channel"|title§Optional. Title, for supergroups, channels and group chats|username§Optional. Username, for private chats, supergroups and channels if available|first_name§Optional. First name of the other party in a private chat|last_name§Optional. Last name of the other party in a private chat|all_members_are_administrators§Optional. True if a group has 'All Members Are Admins' enabled.|photo§Optional. Chat photo. Returned only in getChat.|description§Optional. Description, for supergroups and channel chats. Returned only in getChat.|invite_link§Optional. Chat invite link, for supergroups and channel chats. Returned only in getChat.|pinned_message§Optional. Pinned message, for supergroups and channel chats. Returned only in getChat.|sticker_set_name§Optional. For supergroups, name of group sticker set. Returned only in getChat.|can_set_sticker_set§Optional. True, if the bot can change the group sticker set. Returned only in getChat. -pyrogram.message#b0700003 flags:# message_id:int from_user:flags.0?User date:int chat:Chat forward_from:flags.1?User forward_from_chat:flags.2?Chat forward_from_message_id:flags.3?int forward_signature:flags.4?string forward_date:flags.5?int reply_to_message:flags.6?Message edit_date:flags.7?int media_group_id:flags.8?string author_signature:flags.9?string text:flags.10?string entities:flags.11?Vector caption_entities:flags.12?Vector audio:flags.13?Audio document:flags.14?Document game:flags.15?Game photo:flags.16?Vector sticker:flags.17?Sticker video:flags.18?Video voice:flags.19?Voice video_note:flags.20?VideoNote caption:flags.21?string contact:flags.22?Contact location:flags.23?Location venue:flags.24?Venue new_chat_members:flags.25?Vector left_chat_member:flags.26?User new_chat_title:flags.27?string new_chat_photo:flags.28?Vector delete_chat_photo:flags.29?true group_chat_created:flags.30?true supergroup_chat_created:flags.31?true channel_chat_created:flags.32?true migrate_to_chat_id:flags.33?int migrate_from_chat_id:flags.34?int pinned_message:flags.35?Message invoice:flags.36?Invoice successful_payment:flags.37?SuccessfulPayment connected_website:flags.38?string = pyrogram.Message; // Docs: Message§This object represents a message.|message_id§Unique message identifier inside this chat|from_user§Optional. Sender, empty for messages sent to channels|date§Date the message was sent in Unix time|chat§Conversation the message belongs to|forward_from§Optional. For forwarded messages, sender of the original message|forward_from_chat§Optional. For messages forwarded from channels, information about the original channel|forward_from_message_id§Optional. For messages forwarded from channels, identifier of the original message in the channel|forward_signature§Optional. For messages forwarded from channels, signature of the post author if present|forward_date§Optional. For forwarded messages, date the original message was sent in Unix time|reply_to_message§Optional. For replies, the original message. Note that the Message object in this field will not contain further reply_to_message fields even if it itself is a reply.|edit_date§Optional. Date the message was last edited in Unix time|media_group_id§Optional. The unique identifier of a media message group this message belongs to|author_signature§Optional. Signature of the post author for messages in channels|text§Optional. For text messages, the actual UTF-8 text of the message, 0-4096 characters.|entities§Optional. For text messages, special entities like usernames, URLs, bot commands, etc. that appear in the text|caption_entities§Optional. For messages with a caption, special entities like usernames, URLs, bot commands, etc. that appear in the caption|audio§Optional. Message is an audio file, information about the file|document§Optional. Message is a general file, information about the file|game§Optional. Message is a game, information about the game. More about games|photo§Optional. Message is a photo, available sizes of the photo|sticker§Optional. Message is a sticker, information about the sticker|video§Optional. Message is a video, information about the video|voice§Optional. Message is a voice message, information about the file|video_note§Optional. Message is a video note, information about the video message|caption§Optional. Caption for the audio, document, photo, video or voice, 0-200 characters|contact§Optional. Message is a shared contact, information about the contact|location§Optional. Message is a shared location, information about the location|venue§Optional. Message is a venue, information about the venue|new_chat_members§Optional. New members that were added to the group or supergroup and information about them (the bot itself may be one of these members)|left_chat_member§Optional. A member was removed from the group, information about them (this member may be the bot itself)|new_chat_title§Optional. A chat title was changed to this value|new_chat_photo§Optional. A chat photo was change to this value|delete_chat_photo§Optional. Service message: the chat photo was deleted|group_chat_created§Optional. Service message: the group has been created|supergroup_chat_created§Optional. Service message: the supergroup has been created. This field can't be received in a message coming through updates, because bot can't be a member of a supergroup when it is created. It can only be found in reply_to_message if someone replies to a very first message in a directly created supergroup.|channel_chat_created§Optional. Service message: the channel has been created. This field can't be received in a message coming through updates, because bot can't be a member of a channel when it is created. It can only be found in reply_to_message if someone replies to a very first message in a channel.|migrate_to_chat_id§Optional. The group has been migrated to a supergroup with the specified identifier. This number may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier.|migrate_from_chat_id§Optional. The supergroup has been migrated from a group with the specified identifier. This number may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier.|pinned_message§Optional. Specified message was pinned. Note that the Message object in this field will not contain further reply_to_message fields even if it is itself a reply.|invoice§Optional. Message is an invoice for a payment, information about the invoice. More about payments|successful_payment§Optional. Message is a service message about a successful payment, information about the payment. More about payments|connected_website§Optional. The domain name of the website on which the user has logged in. More about Telegram Login -pyrogram.messageEntity#b0700004 flags:# type:string offset:int length:int url:flags.0?string user:flags.1?User = pyrogram.MessageEntity; // Docs: MessageEntity§This object represents one special entity in a text message. For example, hashtags, usernames, URLs, etc.|type§Type of the entity. Can be mention (@username), hashtag, bot_command, url, email, bold (bold text), italic (italic text), code (monowidth string), pre (monowidth block), text_link (for clickable text URLs), text_mention (for users without usernames)|offset§Offset in UTF-16 code units to the start of the entity|length§Length of the entity in UTF-16 code units|url§Optional. For "text_link" only, url that will be opened after user taps on the text|user§Optional. For "text_mention" only, the mentioned user -pyrogram.photoSize#b0700005 flags:# file_id:string width:int height:int file_size:flags.0?int = pyrogram.PhotoSize; // Docs: PhotoSize§This object represents one size of a photo or a file / sticker thumbnail.|file_id§Unique identifier for this file|width§Photo width|height§Photo height|file_size§Optional. File size -pyrogram.audio#b0700006 flags:# file_id:string duration:int performer:flags.0?string title:flags.1?string mime_type:flags.2?string file_size:flags.3?int = pyrogram.Audio; // Docs: Audio§This object represents an audio file to be treated as music by the Telegram clients.|file_id§Unique identifier for this file|duration§Duration of the audio in seconds as defined by sender|performer§Optional. Performer of the audio as defined by sender or by audio tags|title§Optional. Title of the audio as defined by sender or by audio tags|mime_type§Optional. MIME type of the file as defined by sender|file_size§Optional. File size -pyrogram.document#b0700007 flags:# file_id:string thumb:flags.0?PhotoSize file_name:flags.1?string mime_type:flags.2?string file_size:flags.3?int = pyrogram.Document; // Docs: Document§This object represents a general file (as opposed to photos, voice messages and audio files).|file_id§Unique file identifier|thumb§Optional. Document thumbnail as defined by sender|file_name§Optional. Original filename as defined by sender|mime_type§Optional. MIME type of the file as defined by sender|file_size§Optional. File size -pyrogram.video#b0700008 flags:# file_id:string width:int height:int duration:int thumb:flags.0?PhotoSize mime_type:flags.1?string file_size:flags.2?int = pyrogram.Video; // Docs: Video§This object represents a video file.|file_id§Unique identifier for this file|width§Video width as defined by sender|height§Video height as defined by sender|duration§Duration of the video in seconds as defined by sender|thumb§Optional. Video thumbnail|mime_type§Optional. Mime type of a file as defined by sender|file_size§Optional. File size -pyrogram.voice#b0700009 flags:# file_id:string duration:int mime_type:flags.0?string file_size:flags.1?int = pyrogram.Voice; // Docs: Voice§This object represents a voice note.|file_id§Unique identifier for this file|duration§Duration of the audio in seconds as defined by sender|mime_type§Optional. MIME type of the file as defined by sender|file_size§Optional. File size -pyrogram.videoNote#b0700010 flags:# file_id:string length:int duration:int thumb:flags.0?PhotoSize file_size:flags.1?int = pyrogram.VideoNote; // Docs: VideoNote§This object represents a video message (available in Telegram apps as of v.4.0).|file_id§Unique identifier for this file|length§Video width and height as defined by sender|duration§Duration of the video in seconds as defined by sender|thumb§Optional. Video thumbnail|file_size§Optional. File size -pyrogram.contact#b0700011 flags:# phone_number:string first_name:string last_name:flags.0?string user_id:flags.1?int = pyrogram.Contact; // Docs: Contact§This object represents a phone contact.|phone_number§Contact's phone number|first_name§Contact's first name|last_name§Optional. Contact's last name|user_id§Optional. Contact's user identifier in Telegram -pyrogram.location#b0700012 longitude:double latitude:double = pyrogram.Location; // Docs: Location§This object represents a point on the map.|longitude§Longitude as defined by sender|latitude§Latitude as defined by sender -pyrogram.venue#b0700013 flags:# location:Location title:string address:string foursquare_id:flags.0?string = pyrogram.Venue; // Docs: Venue§This object represents a venue.|location§Venue location|title§Name of the venue|address§Address of the venue|foursquare_id§Optional. Foursquare identifier of the venue -pyrogram.userProfilePhotos#b0700014 total_count:int photos:Vector> = pyrogram.UserProfilePhotos; // Docs: UserProfilePhotos§This object represent a user's profile pictures.|total_count§Total number of profile pictures the target user has|photos§Requested profile pictures (in up to 4 sizes each) -pyrogram.chatPhoto#b0700015 small_file_id:string big_file_id:string = pyrogram.ChatPhoto; // Docs: ChatPhoto§This object represents a chat photo.|small_file_id§Unique file identifier of small (160x160) chat photo. This file_id can be used only for photo download.|big_file_id§Unique file identifier of big (640x640) chat photo. This file_id can be used only for photo download. -pyrogram.chatMember#b0700016 flags:# user:User status:string until_date:flags.0?int can_be_edited:flags.1?Bool can_change_info:flags.2?Bool can_post_messages:flags.3?Bool can_edit_messages:flags.4?Bool can_delete_messages:flags.5?Bool can_invite_users:flags.6?Bool can_restrict_members:flags.7?Bool can_pin_messages:flags.8?Bool can_promote_members:flags.9?Bool can_send_messages:flags.10?Bool can_send_media_messages:flags.11?Bool can_send_other_messages:flags.12?Bool can_add_web_page_previews:flags.13?Bool = pyrogram.ChatMember; // Docs: ChatMember§This object contains information about one member of a chat.|user§Information about the user|status§The member's status in the chat. Can be "creator", "administrator", "member", "restricted", "left" or "kicked"|until_date§Optional. Restricted and kicked only. Date when restrictions will be lifted for this user, unix time|can_be_edited§Optional. Administrators only. True, if the bot is allowed to edit administrator privileges of that user|can_change_info§Optional. Administrators only. True, if the administrator can change the chat title, photo and other settings|can_post_messages§Optional. Administrators only. True, if the administrator can post in the channel, channels only|can_edit_messages§Optional. Administrators only. True, if the administrator can edit messages of other users and can pin messages, channels only|can_delete_messages§Optional. Administrators only. True, if the administrator can delete messages of other users|can_invite_users§Optional. Administrators only. True, if the administrator can invite new users to the chat|can_restrict_members§Optional. Administrators only. True, if the administrator can restrict, ban or unban chat members|can_pin_messages§Optional. Administrators only. True, if the administrator can pin messages, supergroups only|can_promote_members§Optional. Administrators only. 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 the user)|can_send_messages§Optional. Restricted only. True, if the user can send text messages, contacts, locations and venues|can_send_media_messages§Optional. Restricted only. True, if the user can send audios, documents, photos, videos, video notes and voice notes, implies can_send_messages|can_send_other_messages§Optional. Restricted only. True, if the user can send animations, games, stickers and use inline bots, implies can_send_media_messages|can_add_web_page_previews§Optional. Restricted only. True, if user may add web page previews to his messages, implies can_send_media_messages -pyrogram.sticker#b0700017 flags:# file_id:string width:int height:int thumb:flags.0?PhotoSize emoji:flags.1?string set_name:flags.2?string mask_position:flags.3?MaskPosition file_size:flags.4?int = pyrogram.Sticker; // Docs: Sticker§This object represents a sticker.|file_id§Unique identifier for this file|width§Sticker width|height§Sticker height|thumb§Optional. Sticker thumbnail in the .webp or .jpg format|emoji§Optional. Emoji associated with the sticker|set_name§Optional. Name of the sticker set to which the sticker belongs|mask_position§Optional. For mask stickers, the position where the mask should be placed|file_size§Optional. File size +pyrogram.update#b0700000 flags:# update_id:int message:flags.0?Message edited_message:flags.1?Message channel_post:flags.2?Message edited_channel_post:flags.3?Message inline_query:flags.4?InlineQuery chosen_inline_result:flags.5?ChosenInlineResult callback_query:flags.6?CallbackQuery shipping_query:flags.7?ShippingQuery pre_checkout_query:flags.8?PreCheckoutQuery = pyrogram.Update; +pyrogram.user#b0700001 flags:# id:int is_bot:Bool first_name:string last_name:flags.0?string username:flags.1?string language_code:flags.2?string = pyrogram.User; +pyrogram.chat#b0700002 flags:# id:int type:string title:flags.0?string username:flags.1?string first_name:flags.2?string last_name:flags.3?string all_members_are_administrators:flags.4?Bool photo:flags.5?ChatPhoto description:flags.6?string invite_link:flags.7?string pinned_message:flags.8?Message sticker_set_name:flags.9?string can_set_sticker_set:flags.10?Bool = pyrogram.Chat; +pyrogram.message#b0700003 flags:# message_id:int from_user:flags.0?User date:int chat:Chat forward_from:flags.1?User forward_from_chat:flags.2?Chat forward_from_message_id:flags.3?int forward_signature:flags.4?string forward_date:flags.5?int reply_to_message:flags.6?Message edit_date:flags.7?int media_group_id:flags.8?string author_signature:flags.9?string text:flags.10?string entities:flags.11?Vector caption_entities:flags.12?Vector audio:flags.13?Audio document:flags.14?Document game:flags.15?Game photo:flags.16?Vector sticker:flags.17?Sticker video:flags.18?Video voice:flags.19?Voice video_note:flags.20?VideoNote caption:flags.21?string contact:flags.22?Contact location:flags.23?Location venue:flags.24?Venue new_chat_members:flags.25?Vector left_chat_member:flags.26?User new_chat_title:flags.27?string new_chat_photo:flags.28?Vector delete_chat_photo:flags.29?true group_chat_created:flags.30?true supergroup_chat_created:flags.31?true channel_chat_created:flags.32?true migrate_to_chat_id:flags.33?int migrate_from_chat_id:flags.34?int pinned_message:flags.35?Message invoice:flags.36?Invoice successful_payment:flags.37?SuccessfulPayment connected_website:flags.38?string = pyrogram.Message; +pyrogram.messageEntity#b0700004 flags:# type:string offset:int length:int url:flags.0?string user:flags.1?User = pyrogram.MessageEntity; +pyrogram.photoSize#b0700005 flags:# file_id:string width:int height:int file_size:flags.0?int = pyrogram.PhotoSize; +pyrogram.audio#b0700006 flags:# file_id:string duration:int performer:flags.0?string title:flags.1?string mime_type:flags.2?string file_size:flags.3?int = pyrogram.Audio; +pyrogram.document#b0700007 flags:# file_id:string thumb:flags.0?PhotoSize file_name:flags.1?string mime_type:flags.2?string file_size:flags.3?int = pyrogram.Document; +pyrogram.video#b0700008 flags:# file_id:string width:int height:int duration:int thumb:flags.0?PhotoSize mime_type:flags.1?string file_size:flags.2?int = pyrogram.Video; +pyrogram.voice#b0700009 flags:# file_id:string duration:int mime_type:flags.0?string file_size:flags.1?int = pyrogram.Voice; +pyrogram.videoNote#b0700010 flags:# file_id:string length:int duration:int thumb:flags.0?PhotoSize file_size:flags.1?int = pyrogram.VideoNote; +pyrogram.contact#b0700011 flags:# phone_number:string first_name:string last_name:flags.0?string user_id:flags.1?int = pyrogram.Contact; +pyrogram.location#b0700012 longitude:double latitude:double = pyrogram.Location; +pyrogram.venue#b0700013 flags:# location:Location title:string address:string foursquare_id:flags.0?string = pyrogram.Venue; +pyrogram.userProfilePhotos#b0700014 total_count:int photos:Vector> = pyrogram.UserProfilePhotos; +pyrogram.chatPhoto#b0700015 small_file_id:string big_file_id:string = pyrogram.ChatPhoto; +pyrogram.chatMember#b0700016 flags:# user:User status:string until_date:flags.0?int can_be_edited:flags.1?Bool can_change_info:flags.2?Bool can_post_messages:flags.3?Bool can_edit_messages:flags.4?Bool can_delete_messages:flags.5?Bool can_invite_users:flags.6?Bool can_restrict_members:flags.7?Bool can_pin_messages:flags.8?Bool can_promote_members:flags.9?Bool can_send_messages:flags.10?Bool can_send_media_messages:flags.11?Bool can_send_other_messages:flags.12?Bool can_add_web_page_previews:flags.13?Bool = pyrogram.ChatMember; +pyrogram.sticker#b0700017 flags:# file_id:string width:int height:int thumb:flags.0?PhotoSize emoji:flags.1?string set_name:flags.2?string mask_position:flags.3?MaskPosition file_size:flags.4?int = pyrogram.Sticker; From f3fcfb17df784579997125d34828634987463f37 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Thu, 5 Apr 2018 20:44:11 +0200 Subject: [PATCH 069/112] Add fallback for any unsupported media --- pyrogram/client/utils.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py index cf81c68d..2358db3c 100644 --- a/pyrogram/client/utils.py +++ b/pyrogram/client/utils.py @@ -355,16 +355,18 @@ def parse_message( mime_type=doc.mime_type, file_size=doc.size ) + else: + media = None m = pyrogram.Message( message_id=message.id, date=message.date, chat=parse_chat(message, users, chats), from_user=parse_user(users.get(message.from_id, None)), - text=message.message or None if message.media is None else None, - caption=message.message or None if message.media is not None else None, - entities=entities or None if message.media is None else None, - caption_entities=entities or None if message.media is not None else None, + text=message.message or None if media is None else None, + caption=message.message or None if media is not None else None, + entities=entities or None if media is None else None, + caption_entities=entities or None if media is not None else None, author_signature=message.post_author, forward_from=forward_from, forward_from_chat=forward_from_chat, From 055d1c80eaeea829a71fbb5c3b5038ddccc369d2 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Thu, 5 Apr 2018 20:59:41 +0200 Subject: [PATCH 070/112] Handle text_mentions --- pyrogram/client/utils.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py index 2358db3c..8890ee1a 100644 --- a/pyrogram/client/utils.py +++ b/pyrogram/client/utils.py @@ -15,11 +15,11 @@ ENTITIES = { types.MessageEntityCode.ID: "code", types.MessageEntityPre.ID: "pre", types.MessageEntityTextUrl.ID: "text_link", - # TODO: text_mention + types.MessageEntityMentionName.ID: "text_mention" } -def parse_entities(entities: list) -> list: +def parse_entities(entities: list, users: dict) -> list: output_entities = [] for entity in entities: @@ -30,7 +30,13 @@ def parse_entities(entities: list) -> list: type=entity_type, offset=entity.offset, length=entity.length, - url=getattr(entity, "url", None) + url=getattr(entity, "url", None), + user=parse_user( + users.get( + getattr(entity, "user_id", None), + None + ) + ) )) return output_entities @@ -121,7 +127,7 @@ def parse_message( chats: dict, replies: int = 1 ) -> pyrogram.Message: - entities = parse_entities(message.entities) + entities = parse_entities(message.entities, users) forward_from = None forward_from_chat = None From 73246c26f990e9ca885d7415224c53554f878707 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 6 Apr 2018 14:47:00 +0200 Subject: [PATCH 071/112] Rename utils to message_parser --- pyrogram/client/__init__.py | 2 +- pyrogram/client/client.py | 6 +++--- pyrogram/client/{utils.py => message_parser.py} | 0 3 files changed, 4 insertions(+), 4 deletions(-) rename pyrogram/client/{utils.py => message_parser.py} (100%) diff --git a/pyrogram/client/__init__.py b/pyrogram/client/__init__.py index 49214797..1faa8dc6 100644 --- a/pyrogram/client/__init__.py +++ b/pyrogram/client/__init__.py @@ -20,4 +20,4 @@ from .chat_action import ChatAction from .client import Client from .parse_mode import ParseMode from .emoji import Emoji -from . import utils +from . import message_parser diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py index c14ab380..c85ff8d7 100644 --- a/pyrogram/client/client.py +++ b/pyrogram/client/client.py @@ -46,7 +46,7 @@ from pyrogram.api.errors import ( PasswordHashInvalid, FloodWait, PeerIdInvalid, FilePartMissing, ChatAdminRequired, FirstnameInvalid, PhoneNumberBanned, VolumeLocNotFound, UserMigrate) -from pyrogram.client import utils +from pyrogram.client import message_parser from pyrogram.crypto import AES from pyrogram.session import Auth, Session from pyrogram.session.internals import MsgId @@ -732,9 +732,9 @@ class Client: message = update.message if isinstance(message, types.Message): - m = utils.parse_message(self, message, users, chats) + m = message_parser.parse_message(self, message, users, chats) elif isinstance(message, types.MessageService): - m = utils.parse_message_service(self, message, users, chats) + m = message_parser.parse_message_service(self, message, users, chats) else: continue else: diff --git a/pyrogram/client/utils.py b/pyrogram/client/message_parser.py similarity index 100% rename from pyrogram/client/utils.py rename to pyrogram/client/message_parser.py From 6fd4c5c016348901c17769998a9644aefa3529f2 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 6 Apr 2018 14:47:47 +0200 Subject: [PATCH 072/112] Add TODO --- pyrogram/client/message_parser.py | 553 +----------------------------- 1 file changed, 1 insertion(+), 552 deletions(-) diff --git a/pyrogram/client/message_parser.py b/pyrogram/client/message_parser.py index 8890ee1a..39512414 100644 --- a/pyrogram/client/message_parser.py +++ b/pyrogram/client/message_parser.py @@ -1,552 +1 @@ -from base64 import b64encode, b64decode -from struct import pack - -import pyrogram -from pyrogram.api import types - -ENTITIES = { - types.MessageEntityMention.ID: "mention", - types.MessageEntityHashtag.ID: "hashtag", - types.MessageEntityBotCommand.ID: "bot_command", - types.MessageEntityUrl.ID: "url", - types.MessageEntityEmail.ID: "email", - types.MessageEntityBold.ID: "bold", - types.MessageEntityItalic.ID: "italic", - types.MessageEntityCode.ID: "code", - types.MessageEntityPre.ID: "pre", - types.MessageEntityTextUrl.ID: "text_link", - types.MessageEntityMentionName.ID: "text_mention" -} - - -def parse_entities(entities: list, users: dict) -> list: - output_entities = [] - - for entity in entities: - entity_type = ENTITIES.get(entity.ID, None) - - if entity_type: - output_entities.append(pyrogram.MessageEntity( - type=entity_type, - offset=entity.offset, - length=entity.length, - url=getattr(entity, "url", None), - user=parse_user( - users.get( - getattr(entity, "user_id", None), - None - ) - ) - )) - - return output_entities - - -def parse_user(user: types.User) -> pyrogram.User or None: - return pyrogram.User( - id=user.id, - is_bot=user.bot, - first_name=user.first_name, - last_name=user.last_name, - username=user.username, - language_code=user.lang_code - ) if user else None - - -def parse_chat(message: types.Message, users: dict, chats: dict) -> pyrogram.Chat: - if isinstance(message.to_id, types.PeerUser): - return parse_user_chat(users[message.to_id.user_id if message.out else message.from_id]) - elif isinstance(message.to_id, types.PeerChat): - return parse_chat_chat(chats[message.to_id.chat_id]) - else: - return parse_channel_chat(chats[message.to_id.channel_id]) - - -def parse_user_chat(user: types.User) -> pyrogram.Chat: - return pyrogram.Chat( - id=user.id, - type="private", - username=user.username, - first_name=user.first_name, - last_name=user.last_name - ) - - -def parse_chat_chat(chat: types.Chat) -> pyrogram.Chat: - return pyrogram.Chat( - id=-chat.id, - type="group", - title=chat.title, - all_members_are_administrators=not chat.admins_enabled - ) - - -def parse_channel_chat(channel: types.Channel) -> pyrogram.Chat: - return pyrogram.Chat( - id=int("-100" + str(channel.id)), - type="supergroup" if channel.megagroup else "channel", - title=channel.title, - username=channel.username - ) - - -def parse_thumb(thumb: types.PhotoSize or types.PhotoCachedSize) -> pyrogram.PhotoSize or None: - if isinstance(thumb, (types.PhotoSize, types.PhotoCachedSize)): - loc = thumb.location - - if isinstance(thumb, types.PhotoSize): - file_size = thumb.size - else: - file_size = len(thumb.bytes) - - if isinstance(loc, types.FileLocation): - return pyrogram.PhotoSize( - file_id=encode( - pack( - " pyrogram.Message: - entities = parse_entities(message.entities, users) - - forward_from = None - forward_from_chat = None - forward_from_message_id = None - forward_signature = None - forward_date = None - - forward_header = message.fwd_from # type: types.MessageFwdHeader - - if forward_header: - forward_date = forward_header.date - - if forward_header.from_id: - forward_from = parse_user(users[forward_header.from_id]) - else: - forward_from_chat = parse_channel_chat(chats[forward_header.channel_id]) - forward_from_message_id = forward_header.channel_post - forward_signature = forward_header.post_author - - photo = None - location = None - contact = None - venue = None - audio = None - voice = None - video = None - video_note = None - sticker = None - document = None - - media = message.media - - if media: - if isinstance(media, types.MessageMediaPhoto): - photo = media.photo - - if isinstance(photo, types.Photo): - sizes = photo.sizes - photo_sizes = [] - - for size in sizes: - if isinstance(size, (types.PhotoSize, types.PhotoCachedSize)): - loc = size.location - - if isinstance(size, types.PhotoSize): - file_size = size.size - else: - file_size = len(size.bytes) - - if isinstance(loc, types.FileLocation): - photo_size = pyrogram.PhotoSize( - file_id=encode( - pack( - " pyrogram.Message: - action = message.action - - new_chat_members = None - left_chat_member = None - new_chat_title = None - delete_chat_photo = None - migrate_to_chat_id = None - migrate_from_chat_id = None - group_chat_created = None - channel_chat_created = None - new_chat_photo = None - - if isinstance(action, types.MessageActionChatAddUser): - new_chat_members = [parse_user(users[i]) for i in action.users] - elif isinstance(action, types.MessageActionChatJoinedByLink): - new_chat_members = [parse_user(users[action.inviter_id])] - elif isinstance(action, types.MessageActionChatDeleteUser): - left_chat_member = parse_user(users[action.user_id]) - elif isinstance(action, types.MessageActionChatEditTitle): - new_chat_title = action.title - elif isinstance(action, types.MessageActionChatDeletePhoto): - delete_chat_photo = True - elif isinstance(action, types.MessageActionChatMigrateTo): - migrate_to_chat_id = action.channel_id - elif isinstance(action, types.MessageActionChannelMigrateFrom): - migrate_from_chat_id = action.chat_id - elif isinstance(action, types.MessageActionChatCreate): - group_chat_created = True - elif isinstance(action, types.MessageActionChannelCreate): - channel_chat_created = True - elif isinstance(action, types.MessageActionChatEditPhoto): - photo = action.photo - - if isinstance(photo, types.Photo): - sizes = photo.sizes - photo_sizes = [] - - for size in sizes: - if isinstance(size, (types.PhotoSize, types.PhotoCachedSize)): - loc = size.location - - if isinstance(size, types.PhotoSize): - file_size = size.size - else: - file_size = len(size.bytes) - - if isinstance(loc, types.FileLocation): - photo_size = pyrogram.PhotoSize( - file_id=encode( - pack( - " bytes: - s = b64decode(s + "=" * (-len(s) % 4), "-_") - r = b"" - - assert s[-1] == 2 - - i = 0 - while i < len(s) - 1: - if s[i] != 0: - r += bytes([s[i]]) - else: - r += b"\x00" * s[i + 1] - i += 1 - - i += 1 - - return r - - -def encode(s: bytes) -> str: - r = b"" - n = 0 - - for i in s + bytes([2]): - if i == 0: - n += 1 - else: - if n: - r += b"\x00" + bytes([n]) - n = 0 - - r += bytes([i]) - - return b64encode(r, b"-_").decode().rstrip("=") +from base64 import b64encode, b64decode from struct import pack import pyrogram from pyrogram.api import types # TODO: Organize the code better? ENTITIES = { types.MessageEntityMention.ID: "mention", types.MessageEntityHashtag.ID: "hashtag", types.MessageEntityBotCommand.ID: "bot_command", types.MessageEntityUrl.ID: "url", types.MessageEntityEmail.ID: "email", types.MessageEntityBold.ID: "bold", types.MessageEntityItalic.ID: "italic", types.MessageEntityCode.ID: "code", types.MessageEntityPre.ID: "pre", types.MessageEntityTextUrl.ID: "text_link", types.MessageEntityMentionName.ID: "text_mention" } def parse_entities(entities: list, users: dict) -> list: output_entities = [] for entity in entities: entity_type = ENTITIES.get(entity.ID, None) if entity_type: output_entities.append(pyrogram.MessageEntity( type=entity_type, offset=entity.offset, length=entity.length, url=getattr(entity, "url", None), user=parse_user( users.get( getattr(entity, "user_id", None), None ) ) )) return output_entities def parse_user(user: types.User) -> pyrogram.User or None: return pyrogram.User( id=user.id, is_bot=user.bot, first_name=user.first_name, last_name=user.last_name, username=user.username, language_code=user.lang_code ) if user else None def parse_chat(message: types.Message, users: dict, chats: dict) -> pyrogram.Chat: if isinstance(message.to_id, types.PeerUser): return parse_user_chat(users[message.to_id.user_id if message.out else message.from_id]) elif isinstance(message.to_id, types.PeerChat): return parse_chat_chat(chats[message.to_id.chat_id]) else: return parse_channel_chat(chats[message.to_id.channel_id]) def parse_user_chat(user: types.User) -> pyrogram.Chat: return pyrogram.Chat( id=user.id, type="private", username=user.username, first_name=user.first_name, last_name=user.last_name ) def parse_chat_chat(chat: types.Chat) -> pyrogram.Chat: return pyrogram.Chat( id=-chat.id, type="group", title=chat.title, all_members_are_administrators=not chat.admins_enabled ) def parse_channel_chat(channel: types.Channel) -> pyrogram.Chat: return pyrogram.Chat( id=int("-100" + str(channel.id)), type="supergroup" if channel.megagroup else "channel", title=channel.title, username=channel.username ) def parse_thumb(thumb: types.PhotoSize or types.PhotoCachedSize) -> pyrogram.PhotoSize or None: if isinstance(thumb, (types.PhotoSize, types.PhotoCachedSize)): loc = thumb.location if isinstance(thumb, types.PhotoSize): file_size = thumb.size else: file_size = len(thumb.bytes) if isinstance(loc, types.FileLocation): return pyrogram.PhotoSize( file_id=encode( pack( " pyrogram.Message: entities = parse_entities(message.entities, users) forward_from = None forward_from_chat = None forward_from_message_id = None forward_signature = None forward_date = None forward_header = message.fwd_from # type: types.MessageFwdHeader if forward_header: forward_date = forward_header.date if forward_header.from_id: forward_from = parse_user(users[forward_header.from_id]) else: forward_from_chat = parse_channel_chat(chats[forward_header.channel_id]) forward_from_message_id = forward_header.channel_post forward_signature = forward_header.post_author photo = None location = None contact = None venue = None audio = None voice = None video = None video_note = None sticker = None document = None media = message.media if media: if isinstance(media, types.MessageMediaPhoto): photo = media.photo if isinstance(photo, types.Photo): sizes = photo.sizes photo_sizes = [] for size in sizes: if isinstance(size, (types.PhotoSize, types.PhotoCachedSize)): loc = size.location if isinstance(size, types.PhotoSize): file_size = size.size else: file_size = len(size.bytes) if isinstance(loc, types.FileLocation): photo_size = pyrogram.PhotoSize( file_id=encode( pack( " pyrogram.Message: action = message.action new_chat_members = None left_chat_member = None new_chat_title = None delete_chat_photo = None migrate_to_chat_id = None migrate_from_chat_id = None group_chat_created = None channel_chat_created = None new_chat_photo = None if isinstance(action, types.MessageActionChatAddUser): new_chat_members = [parse_user(users[i]) for i in action.users] elif isinstance(action, types.MessageActionChatJoinedByLink): new_chat_members = [parse_user(users[action.inviter_id])] elif isinstance(action, types.MessageActionChatDeleteUser): left_chat_member = parse_user(users[action.user_id]) elif isinstance(action, types.MessageActionChatEditTitle): new_chat_title = action.title elif isinstance(action, types.MessageActionChatDeletePhoto): delete_chat_photo = True elif isinstance(action, types.MessageActionChatMigrateTo): migrate_to_chat_id = action.channel_id elif isinstance(action, types.MessageActionChannelMigrateFrom): migrate_from_chat_id = action.chat_id elif isinstance(action, types.MessageActionChatCreate): group_chat_created = True elif isinstance(action, types.MessageActionChannelCreate): channel_chat_created = True elif isinstance(action, types.MessageActionChatEditPhoto): photo = action.photo if isinstance(photo, types.Photo): sizes = photo.sizes photo_sizes = [] for size in sizes: if isinstance(size, (types.PhotoSize, types.PhotoCachedSize)): loc = size.location if isinstance(size, types.PhotoSize): file_size = size.size else: file_size = len(size.bytes) if isinstance(loc, types.FileLocation): photo_size = pyrogram.PhotoSize( file_id=encode( pack( " bytes: s = b64decode(s + "=" * (-len(s) % 4), "-_") r = b"" assert s[-1] == 2 i = 0 while i < len(s) - 1: if s[i] != 0: r += bytes([s[i]]) else: r += b"\x00" * s[i + 1] i += 1 i += 1 return r def encode(s: bytes) -> str: r = b"" n = 0 for i in s + bytes([2]): if i == 0: n += 1 else: if n: r += b"\x00" + bytes([n]) n = 0 r += bytes([i]) return b64encode(r, b"-_").decode().rstrip("=") \ No newline at end of file From 6bc52fd03b88598ce5f4051be20a61c6e60c17ad Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 6 Apr 2018 17:44:12 +0200 Subject: [PATCH 073/112] Add base Handler class --- pyrogram/client/handler/handler.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 pyrogram/client/handler/handler.py diff --git a/pyrogram/client/handler/handler.py b/pyrogram/client/handler/handler.py new file mode 100644 index 00000000..5bcde2f9 --- /dev/null +++ b/pyrogram/client/handler/handler.py @@ -0,0 +1,22 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2018 Dan Tès +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + + +class Handler: + def __init__(self, callback: callable): + self.callback = callback From 1e8cb843cb67e7e706abd37cc7e483ac3c013a1a Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 6 Apr 2018 17:44:50 +0200 Subject: [PATCH 074/112] Add MessageHandler --- pyrogram/client/handler/message_handler.py | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 pyrogram/client/handler/message_handler.py diff --git a/pyrogram/client/handler/message_handler.py b/pyrogram/client/handler/message_handler.py new file mode 100644 index 00000000..3b751fd7 --- /dev/null +++ b/pyrogram/client/handler/message_handler.py @@ -0,0 +1,24 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2018 Dan Tès +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from .handler import Handler + + +class MessageHandler(Handler): + def __init__(self, callback: callable): + super().__init__(callback) From f5a906452c93a40b0acc806115c2107003fa0253 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 6 Apr 2018 17:45:03 +0200 Subject: [PATCH 075/112] Add EditedMessageHandler --- .../client/handler/edited_message_handler.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 pyrogram/client/handler/edited_message_handler.py diff --git a/pyrogram/client/handler/edited_message_handler.py b/pyrogram/client/handler/edited_message_handler.py new file mode 100644 index 00000000..05fb0690 --- /dev/null +++ b/pyrogram/client/handler/edited_message_handler.py @@ -0,0 +1,24 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2018 Dan Tès +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from .handler import Handler + + +class EditedMessageHandler(Handler): + def __init__(self, callback: callable): + super().__init__(callback) From e638cc68c66090d840dec9deb4b5255b98806fb1 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 6 Apr 2018 17:45:37 +0200 Subject: [PATCH 076/112] Add ChannelPostHandler and EditedChannelPostHandler --- .../client/handler/channel_post_handler.py | 24 +++++++++++++++++++ .../handler/edited_channel_post_handler.py | 24 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 pyrogram/client/handler/channel_post_handler.py create mode 100644 pyrogram/client/handler/edited_channel_post_handler.py diff --git a/pyrogram/client/handler/channel_post_handler.py b/pyrogram/client/handler/channel_post_handler.py new file mode 100644 index 00000000..35310d46 --- /dev/null +++ b/pyrogram/client/handler/channel_post_handler.py @@ -0,0 +1,24 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2018 Dan Tès +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from .handler import Handler + + +class ChannelPostHandler(Handler): + def __init__(self, callback: callable): + super().__init__(callback) diff --git a/pyrogram/client/handler/edited_channel_post_handler.py b/pyrogram/client/handler/edited_channel_post_handler.py new file mode 100644 index 00000000..fb5abd85 --- /dev/null +++ b/pyrogram/client/handler/edited_channel_post_handler.py @@ -0,0 +1,24 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2018 Dan Tès +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from .handler import Handler + + +class EditedChannelPostHandler(Handler): + def __init__(self, callback: callable): + super().__init__(callback) From 9df7fc774fb4f31fb28ae9388191136e4d909c1f Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 6 Apr 2018 17:46:34 +0200 Subject: [PATCH 077/112] Add handlers to init file --- pyrogram/client/handler/__init__.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 pyrogram/client/handler/__init__.py diff --git a/pyrogram/client/handler/__init__.py b/pyrogram/client/handler/__init__.py new file mode 100644 index 00000000..f9366fd0 --- /dev/null +++ b/pyrogram/client/handler/__init__.py @@ -0,0 +1,23 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2018 Dan Tès +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from .channel_post_handler import ChannelPostHandler +from .edited_channel_post_handler import EditedChannelPostHandler +from .edited_message_handler import EditedMessageHandler +from .handler import Handler +from .message_handler import MessageHandler From 2dc57002d6026e622cf092f4eb981dc918d77cb3 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 6 Apr 2018 18:36:29 +0200 Subject: [PATCH 078/112] Add Dispatcher --- pyrogram/client/dispatcher/__init__.py | 19 + pyrogram/client/dispatcher/dispatcher.py | 129 +++++ pyrogram/client/dispatcher/message_parser.py | 572 +++++++++++++++++++ pyrogram/client/message_parser.py | 1 - 4 files changed, 720 insertions(+), 1 deletion(-) create mode 100644 pyrogram/client/dispatcher/__init__.py create mode 100644 pyrogram/client/dispatcher/dispatcher.py create mode 100644 pyrogram/client/dispatcher/message_parser.py delete mode 100644 pyrogram/client/message_parser.py diff --git a/pyrogram/client/dispatcher/__init__.py b/pyrogram/client/dispatcher/__init__.py new file mode 100644 index 00000000..c0cb368a --- /dev/null +++ b/pyrogram/client/dispatcher/__init__.py @@ -0,0 +1,19 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2018 Dan Tès +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from .dispatcher import Dispatcher diff --git a/pyrogram/client/dispatcher/dispatcher.py b/pyrogram/client/dispatcher/dispatcher.py new file mode 100644 index 00000000..035e2aff --- /dev/null +++ b/pyrogram/client/dispatcher/dispatcher.py @@ -0,0 +1,129 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2018 Dan Tès +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +import logging +import threading +from collections import OrderedDict +from queue import Queue +from threading import Thread + +import pyrogram +from pyrogram.api import types +from . import message_parser +from ..handler import ( + Handler, MessageHandler, EditedMessageHandler, + ChannelPostHandler, EditedChannelPostHandler +) + +log = logging.getLogger(__name__) + + +class Dispatcher: + def __init__(self, client, workers): + self.client = client + self.workers = workers + self.updates = Queue() + self.handlers = OrderedDict() + + def start(self): + for i in range(self.workers): + Thread(target=self.update_worker, name="UpdateWorker#{}".format(i + 1)).start() + + def stop(self): + for _ in range(self.workers): + self.updates.put(None) + + def add_handler(self, handler: Handler, group: int): + if group not in self.handlers: + self.handlers[group] = {} + self.handlers = OrderedDict(sorted(self.handlers.items())) + + if type(handler) not in self.handlers[group]: + self.handlers[group][type(handler)] = handler + else: + raise ValueError( + "'{0}' is already registered in Group #{1}. " + "You can register a different handler in this group or another '{0}' in a different group".format( + type(handler).__name__, + group + ) + ) + + def dispatch(self, update): + if update.message: + key = MessageHandler + elif update.edited_message: + key = EditedMessageHandler + elif update.channel_post: + key = ChannelPostHandler + elif update.edited_channel_post: + key = EditedChannelPostHandler + else: + return + + for group in self.handlers.values(): + handler = group.get(key, None) + + if handler is not None: + handler.callback(self.client, update) + + def update_worker(self): + name = threading.current_thread().name + log.debug("{} started".format(name)) + + while True: + update = self.updates.get() + + if update is None: + break + + try: + users = {i.id: i for i in update[1]} + chats = {i.id: i for i in update[2]} + update = update[0] + + valid_updates = (types.UpdateNewMessage, types.UpdateNewChannelMessage, + types.UpdateEditMessage, types.UpdateEditChannelMessage) + + if isinstance(update, valid_updates): + message = update.message + + if isinstance(message, types.Message): + m = message_parser.parse_message(self.client, message, users, chats) + elif isinstance(message, types.MessageService): + m = message_parser.parse_message_service(self.client, message, users, chats) + else: + continue + else: + continue + + edit = isinstance(update, (types.UpdateEditMessage, types.UpdateEditChannelMessage)) + + self.dispatch( + pyrogram.Update( + update_id=0, + message=(m if m.chat.type is not "channel" else None) if not edit else None, + edited_message=(m if m.chat.type is not "channel" else None) if edit else None, + channel_post=(m if m.chat.type is "channel" else None) if not edit else None, + edited_channel_post=(m if m.chat.type is "channel" else None) if edit else None + ) + ) + except Exception as e: + log.error(e, exc_info=True) + + log.debug("{} stopped".format(name)) diff --git a/pyrogram/client/dispatcher/message_parser.py b/pyrogram/client/dispatcher/message_parser.py new file mode 100644 index 00000000..a101b78e --- /dev/null +++ b/pyrogram/client/dispatcher/message_parser.py @@ -0,0 +1,572 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2018 Dan Tès +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from base64 import b64encode, b64decode +from struct import pack + +import pyrogram +from pyrogram.api import types + +# TODO: Organize the code better? + +ENTITIES = { + types.MessageEntityMention.ID: "mention", + types.MessageEntityHashtag.ID: "hashtag", + types.MessageEntityBotCommand.ID: "bot_command", + types.MessageEntityUrl.ID: "url", + types.MessageEntityEmail.ID: "email", + types.MessageEntityBold.ID: "bold", + types.MessageEntityItalic.ID: "italic", + types.MessageEntityCode.ID: "code", + types.MessageEntityPre.ID: "pre", + types.MessageEntityTextUrl.ID: "text_link", + types.MessageEntityMentionName.ID: "text_mention" +} + + +def parse_entities(entities: list, users: dict) -> list: + output_entities = [] + + for entity in entities: + entity_type = ENTITIES.get(entity.ID, None) + + if entity_type: + output_entities.append(pyrogram.MessageEntity( + type=entity_type, + offset=entity.offset, + length=entity.length, + url=getattr(entity, "url", None), + user=parse_user( + users.get( + getattr(entity, "user_id", None), + None + ) + ) + )) + + return output_entities + + +def parse_user(user: types.User) -> pyrogram.User or None: + return pyrogram.User( + id=user.id, + is_bot=user.bot, + first_name=user.first_name, + last_name=user.last_name, + username=user.username, + language_code=user.lang_code + ) if user else None + + +def parse_chat(message: types.Message, users: dict, chats: dict) -> pyrogram.Chat: + if isinstance(message.to_id, types.PeerUser): + return parse_user_chat(users[message.to_id.user_id if message.out else message.from_id]) + elif isinstance(message.to_id, types.PeerChat): + return parse_chat_chat(chats[message.to_id.chat_id]) + else: + return parse_channel_chat(chats[message.to_id.channel_id]) + + +def parse_user_chat(user: types.User) -> pyrogram.Chat: + return pyrogram.Chat( + id=user.id, + type="private", + username=user.username, + first_name=user.first_name, + last_name=user.last_name + ) + + +def parse_chat_chat(chat: types.Chat) -> pyrogram.Chat: + return pyrogram.Chat( + id=-chat.id, + type="group", + title=chat.title, + all_members_are_administrators=not chat.admins_enabled + ) + + +def parse_channel_chat(channel: types.Channel) -> pyrogram.Chat: + return pyrogram.Chat( + id=int("-100" + str(channel.id)), + type="supergroup" if channel.megagroup else "channel", + title=channel.title, + username=channel.username + ) + + +def parse_thumb(thumb: types.PhotoSize or types.PhotoCachedSize) -> pyrogram.PhotoSize or None: + if isinstance(thumb, (types.PhotoSize, types.PhotoCachedSize)): + loc = thumb.location + + if isinstance(thumb, types.PhotoSize): + file_size = thumb.size + else: + file_size = len(thumb.bytes) + + if isinstance(loc, types.FileLocation): + return pyrogram.PhotoSize( + file_id=encode( + pack( + " pyrogram.Message: + entities = parse_entities(message.entities, users) + + forward_from = None + forward_from_chat = None + forward_from_message_id = None + forward_signature = None + forward_date = None + + forward_header = message.fwd_from # type: types.MessageFwdHeader + + if forward_header: + forward_date = forward_header.date + + if forward_header.from_id: + forward_from = parse_user(users[forward_header.from_id]) + else: + forward_from_chat = parse_channel_chat(chats[forward_header.channel_id]) + forward_from_message_id = forward_header.channel_post + forward_signature = forward_header.post_author + + photo = None + location = None + contact = None + venue = None + audio = None + voice = None + video = None + video_note = None + sticker = None + document = None + + media = message.media + + if media: + if isinstance(media, types.MessageMediaPhoto): + photo = media.photo + + if isinstance(photo, types.Photo): + sizes = photo.sizes + photo_sizes = [] + + for size in sizes: + if isinstance(size, (types.PhotoSize, types.PhotoCachedSize)): + loc = size.location + + if isinstance(size, types.PhotoSize): + file_size = size.size + else: + file_size = len(size.bytes) + + if isinstance(loc, types.FileLocation): + photo_size = pyrogram.PhotoSize( + file_id=encode( + pack( + " pyrogram.Message: + action = message.action + + new_chat_members = None + left_chat_member = None + new_chat_title = None + delete_chat_photo = None + migrate_to_chat_id = None + migrate_from_chat_id = None + group_chat_created = None + channel_chat_created = None + new_chat_photo = None + + if isinstance(action, types.MessageActionChatAddUser): + new_chat_members = [parse_user(users[i]) for i in action.users] + elif isinstance(action, types.MessageActionChatJoinedByLink): + new_chat_members = [parse_user(users[action.inviter_id])] + elif isinstance(action, types.MessageActionChatDeleteUser): + left_chat_member = parse_user(users[action.user_id]) + elif isinstance(action, types.MessageActionChatEditTitle): + new_chat_title = action.title + elif isinstance(action, types.MessageActionChatDeletePhoto): + delete_chat_photo = True + elif isinstance(action, types.MessageActionChatMigrateTo): + migrate_to_chat_id = action.channel_id + elif isinstance(action, types.MessageActionChannelMigrateFrom): + migrate_from_chat_id = action.chat_id + elif isinstance(action, types.MessageActionChatCreate): + group_chat_created = True + elif isinstance(action, types.MessageActionChannelCreate): + channel_chat_created = True + elif isinstance(action, types.MessageActionChatEditPhoto): + photo = action.photo + + if isinstance(photo, types.Photo): + sizes = photo.sizes + photo_sizes = [] + + for size in sizes: + if isinstance(size, (types.PhotoSize, types.PhotoCachedSize)): + loc = size.location + + if isinstance(size, types.PhotoSize): + file_size = size.size + else: + file_size = len(size.bytes) + + if isinstance(loc, types.FileLocation): + photo_size = pyrogram.PhotoSize( + file_id=encode( + pack( + " bytes: + s = b64decode(s + "=" * (-len(s) % 4), "-_") + r = b"" + + assert s[-1] == 2 + + i = 0 + while i < len(s) - 1: + if s[i] != 0: + r += bytes([s[i]]) + else: + r += b"\x00" * s[i + 1] + i += 1 + + i += 1 + + return r + + +def encode(s: bytes) -> str: + r = b"" + n = 0 + + for i in s + bytes([2]): + if i == 0: + n += 1 + else: + if n: + r += b"\x00" + bytes([n]) + n = 0 + + r += bytes([i]) + + return b64encode(r, b"-_").decode().rstrip("=") diff --git a/pyrogram/client/message_parser.py b/pyrogram/client/message_parser.py deleted file mode 100644 index 39512414..00000000 --- a/pyrogram/client/message_parser.py +++ /dev/null @@ -1 +0,0 @@ -from base64 import b64encode, b64decode from struct import pack import pyrogram from pyrogram.api import types # TODO: Organize the code better? ENTITIES = { types.MessageEntityMention.ID: "mention", types.MessageEntityHashtag.ID: "hashtag", types.MessageEntityBotCommand.ID: "bot_command", types.MessageEntityUrl.ID: "url", types.MessageEntityEmail.ID: "email", types.MessageEntityBold.ID: "bold", types.MessageEntityItalic.ID: "italic", types.MessageEntityCode.ID: "code", types.MessageEntityPre.ID: "pre", types.MessageEntityTextUrl.ID: "text_link", types.MessageEntityMentionName.ID: "text_mention" } def parse_entities(entities: list, users: dict) -> list: output_entities = [] for entity in entities: entity_type = ENTITIES.get(entity.ID, None) if entity_type: output_entities.append(pyrogram.MessageEntity( type=entity_type, offset=entity.offset, length=entity.length, url=getattr(entity, "url", None), user=parse_user( users.get( getattr(entity, "user_id", None), None ) ) )) return output_entities def parse_user(user: types.User) -> pyrogram.User or None: return pyrogram.User( id=user.id, is_bot=user.bot, first_name=user.first_name, last_name=user.last_name, username=user.username, language_code=user.lang_code ) if user else None def parse_chat(message: types.Message, users: dict, chats: dict) -> pyrogram.Chat: if isinstance(message.to_id, types.PeerUser): return parse_user_chat(users[message.to_id.user_id if message.out else message.from_id]) elif isinstance(message.to_id, types.PeerChat): return parse_chat_chat(chats[message.to_id.chat_id]) else: return parse_channel_chat(chats[message.to_id.channel_id]) def parse_user_chat(user: types.User) -> pyrogram.Chat: return pyrogram.Chat( id=user.id, type="private", username=user.username, first_name=user.first_name, last_name=user.last_name ) def parse_chat_chat(chat: types.Chat) -> pyrogram.Chat: return pyrogram.Chat( id=-chat.id, type="group", title=chat.title, all_members_are_administrators=not chat.admins_enabled ) def parse_channel_chat(channel: types.Channel) -> pyrogram.Chat: return pyrogram.Chat( id=int("-100" + str(channel.id)), type="supergroup" if channel.megagroup else "channel", title=channel.title, username=channel.username ) def parse_thumb(thumb: types.PhotoSize or types.PhotoCachedSize) -> pyrogram.PhotoSize or None: if isinstance(thumb, (types.PhotoSize, types.PhotoCachedSize)): loc = thumb.location if isinstance(thumb, types.PhotoSize): file_size = thumb.size else: file_size = len(thumb.bytes) if isinstance(loc, types.FileLocation): return pyrogram.PhotoSize( file_id=encode( pack( " pyrogram.Message: entities = parse_entities(message.entities, users) forward_from = None forward_from_chat = None forward_from_message_id = None forward_signature = None forward_date = None forward_header = message.fwd_from # type: types.MessageFwdHeader if forward_header: forward_date = forward_header.date if forward_header.from_id: forward_from = parse_user(users[forward_header.from_id]) else: forward_from_chat = parse_channel_chat(chats[forward_header.channel_id]) forward_from_message_id = forward_header.channel_post forward_signature = forward_header.post_author photo = None location = None contact = None venue = None audio = None voice = None video = None video_note = None sticker = None document = None media = message.media if media: if isinstance(media, types.MessageMediaPhoto): photo = media.photo if isinstance(photo, types.Photo): sizes = photo.sizes photo_sizes = [] for size in sizes: if isinstance(size, (types.PhotoSize, types.PhotoCachedSize)): loc = size.location if isinstance(size, types.PhotoSize): file_size = size.size else: file_size = len(size.bytes) if isinstance(loc, types.FileLocation): photo_size = pyrogram.PhotoSize( file_id=encode( pack( " pyrogram.Message: action = message.action new_chat_members = None left_chat_member = None new_chat_title = None delete_chat_photo = None migrate_to_chat_id = None migrate_from_chat_id = None group_chat_created = None channel_chat_created = None new_chat_photo = None if isinstance(action, types.MessageActionChatAddUser): new_chat_members = [parse_user(users[i]) for i in action.users] elif isinstance(action, types.MessageActionChatJoinedByLink): new_chat_members = [parse_user(users[action.inviter_id])] elif isinstance(action, types.MessageActionChatDeleteUser): left_chat_member = parse_user(users[action.user_id]) elif isinstance(action, types.MessageActionChatEditTitle): new_chat_title = action.title elif isinstance(action, types.MessageActionChatDeletePhoto): delete_chat_photo = True elif isinstance(action, types.MessageActionChatMigrateTo): migrate_to_chat_id = action.channel_id elif isinstance(action, types.MessageActionChannelMigrateFrom): migrate_from_chat_id = action.chat_id elif isinstance(action, types.MessageActionChatCreate): group_chat_created = True elif isinstance(action, types.MessageActionChannelCreate): channel_chat_created = True elif isinstance(action, types.MessageActionChatEditPhoto): photo = action.photo if isinstance(photo, types.Photo): sizes = photo.sizes photo_sizes = [] for size in sizes: if isinstance(size, (types.PhotoSize, types.PhotoCachedSize)): loc = size.location if isinstance(size, types.PhotoSize): file_size = size.size else: file_size = len(size.bytes) if isinstance(loc, types.FileLocation): photo_size = pyrogram.PhotoSize( file_id=encode( pack( " bytes: s = b64decode(s + "=" * (-len(s) % 4), "-_") r = b"" assert s[-1] == 2 i = 0 while i < len(s) - 1: if s[i] != 0: r += bytes([s[i]]) else: r += b"\x00" * s[i + 1] i += 1 i += 1 return r def encode(s: bytes) -> str: r = b"" n = 0 for i in s + bytes([2]): if i == 0: n += 1 else: if n: r += b"\x00" + bytes([n]) n = 0 r += bytes([i]) return b64encode(r, b"-_").decode().rstrip("=") \ No newline at end of file From 331eb624554db202699d5584a7603745bfe01eec Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 6 Apr 2018 18:37:11 +0200 Subject: [PATCH 079/112] Clean up __init__ file --- pyrogram/client/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pyrogram/client/__init__.py b/pyrogram/client/__init__.py index 1faa8dc6..b2935dad 100644 --- a/pyrogram/client/__init__.py +++ b/pyrogram/client/__init__.py @@ -18,6 +18,5 @@ from .chat_action import ChatAction from .client import Client -from .parse_mode import ParseMode from .emoji import Emoji -from . import message_parser +from .parse_mode import ParseMode From 7bd52c3718a78dc76efd8c77cb7ac681266b98db Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 6 Apr 2018 18:37:54 +0200 Subject: [PATCH 080/112] Add handlers to __init__ file --- pyrogram/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyrogram/__init__.py b/pyrogram/__init__.py index 0d6595fa..e0dfe473 100644 --- a/pyrogram/__init__.py +++ b/pyrogram/__init__.py @@ -34,3 +34,7 @@ from .client.input_media_photo import InputMediaPhoto from .client.input_media_video import InputMediaVideo from .client.input_phone_contact import InputPhoneContact from .client import Emoji +from .client.handler import ( + MessageHandler, EditedMessageHandler, ChannelPostHandler, + EditedChannelPostHandler +) From e98b209526953d77daed9f2f0d3b3eecbcc1ba07 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 6 Apr 2018 18:48:41 +0200 Subject: [PATCH 081/112] Accommodate the new Dispatcher --- pyrogram/client/client.py | 115 +++++--------------------------------- 1 file changed, 14 insertions(+), 101 deletions(-) diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py index c85ff8d7..74c2d01c 100644 --- a/pyrogram/client/client.py +++ b/pyrogram/client/client.py @@ -36,7 +36,6 @@ from queue import Queue from signal import signal, SIGINT, SIGTERM, SIGABRT from threading import Event, Thread -import pyrogram from pyrogram.api import functions, types from pyrogram.api.core import Object from pyrogram.api.errors import ( @@ -46,10 +45,11 @@ from pyrogram.api.errors import ( PasswordHashInvalid, FloodWait, PeerIdInvalid, FilePartMissing, ChatAdminRequired, FirstnameInvalid, PhoneNumberBanned, VolumeLocNotFound, UserMigrate) -from pyrogram.client import message_parser from pyrogram.crypto import AES from pyrogram.session import Auth, Session from pyrogram.session.internals import MsgId +from .dispatcher import Dispatcher +from .handler import Handler from .input_media import InputMedia from .style import Markdown, HTML @@ -183,10 +183,13 @@ class Client: self.is_idle = None self.updates_queue = Queue() - self.update_queue = Queue() + self.download_queue = Queue() + + self.dispatcher = Dispatcher(self, workers) self.update_handler = None - self.download_queue = Queue() + def add_handler(self, handler: Handler, group: int = 0): + self.dispatcher.add_handler(handler, group) def start(self): """Use this method to start the Client after creating it. @@ -234,12 +237,11 @@ class Client: for i in range(self.UPDATES_WORKERS): Thread(target=self.updates_worker, name="UpdatesWorker#{}".format(i + 1)).start() - for i in range(self.workers): - Thread(target=self.update_worker, name="UpdateWorker#{}".format(i + 1)).start() - for i in range(self.DOWNLOAD_WORKERS): Thread(target=self.download_worker, name="DownloadWorker#{}".format(i + 1)).start() + self.dispatcher.start() + mimetypes.init() def stop(self): @@ -255,12 +257,11 @@ class Client: for _ in range(self.UPDATES_WORKERS): self.updates_queue.put(None) - for _ in range(self.workers): - self.update_queue.put(None) - for _ in range(self.DOWNLOAD_WORKERS): self.download_queue.put(None) + self.dispatcher.stop() + def authorize_bot(self): try: r = self.send( @@ -684,7 +685,7 @@ class Client: if len(self.channels_pts[channel_id]) > 50: self.channels_pts[channel_id] = self.channels_pts[channel_id][25:] - self.update_queue.put((update, updates.users, updates.chats)) + self.dispatcher.updates.put((update, updates.users, updates.chats)) elif isinstance(updates, (types.UpdateShortMessage, types.UpdateShortChatMessage)): diff = self.send( functions.updates.GetDifference( @@ -694,7 +695,7 @@ class Client: ) ) - self.update_queue.put(( + self.dispatcher.updates.put(( types.UpdateNewMessage( message=diff.new_messages[0], pts=updates.pts, @@ -704,54 +705,7 @@ class Client: diff.chats )) elif isinstance(updates, types.UpdateShort): - self.update_queue.put((updates.update, [], [])) - except Exception as e: - log.error(e, exc_info=True) - - log.debug("{} stopped".format(name)) - - def update_worker(self): - name = threading.current_thread().name - log.debug("{} started".format(name)) - - while True: - update = self.update_queue.get() - - if update is None: - break - - try: - users = {i.id: i for i in update[1]} - chats = {i.id: i for i in update[2]} - update = update[0] - - valid_updates = (types.UpdateNewMessage, types.UpdateNewChannelMessage, - types.UpdateEditMessage, types.UpdateEditChannelMessage) - - if isinstance(update, valid_updates): - message = update.message - - if isinstance(message, types.Message): - m = message_parser.parse_message(self, message, users, chats) - elif isinstance(message, types.MessageService): - m = message_parser.parse_message_service(self, message, users, chats) - else: - continue - else: - continue - - edit = isinstance(update, (types.UpdateEditMessage, types.UpdateEditChannelMessage)) - - u = pyrogram.Update( - update_id=0, - message=(m if m.chat.type is not "channel" else None) if not edit else None, - edited_message=(m if m.chat.type is not "channel" else None) if edit else None, - channel_post=(m if m.chat.type is "channel" else None) if not edit else None, - edited_channel_post=(m if m.chat.type is "channel" else None) if edit else None - ) - - if self.update_handler: - self.update_handler(self, u) + self.dispatcher.updates.put((updates.update, [], [])) except Exception as e: log.error(e, exc_info=True) @@ -778,47 +732,6 @@ class Client: while self.is_idle: time.sleep(1) - def set_update_handler(self, callback: callable): - """Use this method to set the update handler. - - You must call this method *before* you *start()* the Client. - - Args: - callback (``callable``): - A function that will be called when a new update is received from the server. It takes - *(client, update, users, chats)* as positional arguments (Look at the section below for - a detailed description). - - Other Parameters: - client (:class:`Client `): - The Client itself, useful when you want to call other API methods inside the update handler. - - update (``Update``): - The received update, which can be one of the many single Updates listed in the *updates* - field you see in the :obj:`Update ` type. - - users (``dict``): - Dictionary of all :obj:`User ` mentioned in the update. - You can access extra info about the user (such as *first_name*, *last_name*, etc...) by using - the IDs you find in the *update* argument (e.g.: *users[1768841572]*). - - chats (``dict``): - Dictionary of all :obj:`Chat ` and - :obj:`Channel ` mentioned in the update. - You can access extra info about the chat (such as *title*, *participants_count*, etc...) - by using the IDs you find in the *update* argument (e.g.: *chats[1701277281]*). - - Note: - The following Empty or Forbidden types may exist inside the *users* and *chats* dictionaries. - They mean you have been blocked by the user or banned from the group/channel. - - - :obj:`UserEmpty ` - - :obj:`ChatEmpty ` - - :obj:`ChatForbidden ` - - :obj:`ChannelForbidden ` - """ - self.update_handler = callback - def send(self, data: Object): """Use this method to send Raw Function queries. From ede627de52536aa961473bf32928bca13b86dca8 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 6 Apr 2018 20:35:38 +0200 Subject: [PATCH 082/112] Directly pass the message instead of the update --- pyrogram/client/dispatcher/dispatcher.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pyrogram/client/dispatcher/dispatcher.py b/pyrogram/client/dispatcher/dispatcher.py index 035e2aff..1584740d 100644 --- a/pyrogram/client/dispatcher/dispatcher.py +++ b/pyrogram/client/dispatcher/dispatcher.py @@ -67,12 +67,16 @@ class Dispatcher: def dispatch(self, update): if update.message: key = MessageHandler + value = update.message elif update.edited_message: key = EditedMessageHandler + value = update.edited_message elif update.channel_post: key = ChannelPostHandler + value = update.channel_post elif update.edited_channel_post: key = EditedChannelPostHandler + value = update.edited_channel_post else: return @@ -80,7 +84,7 @@ class Dispatcher: handler = group.get(key, None) if handler is not None: - handler.callback(self.client, update) + handler.callback(self.client, value) def update_worker(self): name = threading.current_thread().name From 90a4e4c41131bae5025ccfe010da9228edd1bc28 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Fri, 6 Apr 2018 20:38:34 +0200 Subject: [PATCH 083/112] Allow registering handlers using decorators --- pyrogram/client/client.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py index 74c2d01c..cd7fe030 100644 --- a/pyrogram/client/client.py +++ b/pyrogram/client/client.py @@ -36,6 +36,7 @@ from queue import Queue from signal import signal, SIGINT, SIGTERM, SIGABRT from threading import Event, Thread +import pyrogram from pyrogram.api import functions, types from pyrogram.api.core import Object from pyrogram.api.errors import ( @@ -188,6 +189,25 @@ class Client: self.dispatcher = Dispatcher(self, workers) self.update_handler = None + def on(self, handler, group: int): + def decorator(f): + self.add_handler(handler(f), group) + return f + + return decorator + + def on_message(self, group: int = 0): + return self.on(pyrogram.MessageHandler, group) + + def on_edited_message(self, group: int = 0): + return self.on(pyrogram.EditedMessageHandler, group) + + def on_channel_post(self, group: int = 0): + return self.on(pyrogram.ChannelPostHandler, group) + + def on_edited_channel_post(self, group: int = 0): + return self.on(pyrogram.EditedChannelPostHandler, group) + def add_handler(self, handler: Handler, group: int = 0): self.dispatcher.add_handler(handler, group) From ee2d66b416c04e644201baf7a6229c9665a2bae7 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sat, 7 Apr 2018 23:34:28 +0200 Subject: [PATCH 084/112] Only keep MessageHandler --- pyrogram/__init__.py | 5 +--- pyrogram/client/client.py | 17 ++----------- pyrogram/client/dispatcher/dispatcher.py | 21 +++++++--------- pyrogram/client/handler/__init__.py | 3 --- .../client/handler/channel_post_handler.py | 24 ------------------- .../handler/edited_channel_post_handler.py | 24 ------------------- .../client/handler/edited_message_handler.py | 24 ------------------- 7 files changed, 11 insertions(+), 107 deletions(-) delete mode 100644 pyrogram/client/handler/channel_post_handler.py delete mode 100644 pyrogram/client/handler/edited_channel_post_handler.py delete mode 100644 pyrogram/client/handler/edited_message_handler.py diff --git a/pyrogram/__init__.py b/pyrogram/__init__.py index e0dfe473..e366ae20 100644 --- a/pyrogram/__init__.py +++ b/pyrogram/__init__.py @@ -34,7 +34,4 @@ from .client.input_media_photo import InputMediaPhoto from .client.input_media_video import InputMediaVideo from .client.input_phone_contact import InputPhoneContact from .client import Emoji -from .client.handler import ( - MessageHandler, EditedMessageHandler, ChannelPostHandler, - EditedChannelPostHandler -) +from .client.handler import MessageHandler diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py index cd7fe030..0495fe56 100644 --- a/pyrogram/client/client.py +++ b/pyrogram/client/client.py @@ -189,25 +189,12 @@ class Client: self.dispatcher = Dispatcher(self, workers) self.update_handler = None - def on(self, handler, group: int): + def on_message(self, group: int = 0): def decorator(f): - self.add_handler(handler(f), group) - return f + self.add_handler(pyrogram.MessageHandler(f), group) return decorator - def on_message(self, group: int = 0): - return self.on(pyrogram.MessageHandler, group) - - def on_edited_message(self, group: int = 0): - return self.on(pyrogram.EditedMessageHandler, group) - - def on_channel_post(self, group: int = 0): - return self.on(pyrogram.ChannelPostHandler, group) - - def on_edited_channel_post(self, group: int = 0): - return self.on(pyrogram.EditedChannelPostHandler, group) - def add_handler(self, handler: Handler, group: int = 0): self.dispatcher.add_handler(handler, group) diff --git a/pyrogram/client/dispatcher/dispatcher.py b/pyrogram/client/dispatcher/dispatcher.py index 1584740d..58a17599 100644 --- a/pyrogram/client/dispatcher/dispatcher.py +++ b/pyrogram/client/dispatcher/dispatcher.py @@ -26,8 +26,7 @@ import pyrogram from pyrogram.api import types from . import message_parser from ..handler import ( - Handler, MessageHandler, EditedMessageHandler, - ChannelPostHandler, EditedChannelPostHandler + Handler, MessageHandler ) log = logging.getLogger(__name__) @@ -65,18 +64,14 @@ class Dispatcher: ) def dispatch(self, update): - if update.message: + message = (update.message + or update.channel_post + or update.edited_message + or update.edited_channel_post) + + if message: key = MessageHandler - value = update.message - elif update.edited_message: - key = EditedMessageHandler - value = update.edited_message - elif update.channel_post: - key = ChannelPostHandler - value = update.channel_post - elif update.edited_channel_post: - key = EditedChannelPostHandler - value = update.edited_channel_post + value = message else: return diff --git a/pyrogram/client/handler/__init__.py b/pyrogram/client/handler/__init__.py index f9366fd0..c86ca6ec 100644 --- a/pyrogram/client/handler/__init__.py +++ b/pyrogram/client/handler/__init__.py @@ -16,8 +16,5 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . -from .channel_post_handler import ChannelPostHandler -from .edited_channel_post_handler import EditedChannelPostHandler -from .edited_message_handler import EditedMessageHandler from .handler import Handler from .message_handler import MessageHandler diff --git a/pyrogram/client/handler/channel_post_handler.py b/pyrogram/client/handler/channel_post_handler.py deleted file mode 100644 index 35310d46..00000000 --- a/pyrogram/client/handler/channel_post_handler.py +++ /dev/null @@ -1,24 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 Dan Tès -# -# This file is part of Pyrogram. -# -# Pyrogram is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published -# by the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Pyrogram is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with Pyrogram. If not, see . - -from .handler import Handler - - -class ChannelPostHandler(Handler): - def __init__(self, callback: callable): - super().__init__(callback) diff --git a/pyrogram/client/handler/edited_channel_post_handler.py b/pyrogram/client/handler/edited_channel_post_handler.py deleted file mode 100644 index fb5abd85..00000000 --- a/pyrogram/client/handler/edited_channel_post_handler.py +++ /dev/null @@ -1,24 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 Dan Tès -# -# This file is part of Pyrogram. -# -# Pyrogram is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published -# by the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Pyrogram is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with Pyrogram. If not, see . - -from .handler import Handler - - -class EditedChannelPostHandler(Handler): - def __init__(self, callback: callable): - super().__init__(callback) diff --git a/pyrogram/client/handler/edited_message_handler.py b/pyrogram/client/handler/edited_message_handler.py deleted file mode 100644 index 05fb0690..00000000 --- a/pyrogram/client/handler/edited_message_handler.py +++ /dev/null @@ -1,24 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 Dan Tès -# -# This file is part of Pyrogram. -# -# Pyrogram is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published -# by the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Pyrogram is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with Pyrogram. If not, see . - -from .handler import Handler - - -class EditedMessageHandler(Handler): - def __init__(self, callback: callable): - super().__init__(callback) From 1f05c4223a9b0bbb982e1884fad3e3b2a7d85edc Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sun, 8 Apr 2018 11:58:17 +0200 Subject: [PATCH 085/112] Fix copypasta --- pyrogram/client/client.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py index 0495fe56..ca737f2f 100644 --- a/pyrogram/client/client.py +++ b/pyrogram/client/client.py @@ -192,6 +192,7 @@ class Client: def on_message(self, group: int = 0): def decorator(f): self.add_handler(pyrogram.MessageHandler(f), group) + return f return decorator From 8e8613bc0ffca9bdd7161fa2cac13685e5ab7733 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sun, 8 Apr 2018 12:43:47 +0200 Subject: [PATCH 086/112] Refactor Dispatcher --- pyrogram/client/dispatcher/dispatcher.py | 60 +++++++++++++++++------- 1 file changed, 43 insertions(+), 17 deletions(-) diff --git a/pyrogram/client/dispatcher/dispatcher.py b/pyrogram/client/dispatcher/dispatcher.py index 58a17599..a5ae9049 100644 --- a/pyrogram/client/dispatcher/dispatcher.py +++ b/pyrogram/client/dispatcher/dispatcher.py @@ -33,6 +33,18 @@ log = logging.getLogger(__name__) class Dispatcher: + MESSAGE_UPDATES = ( + types.UpdateNewMessage, + types.UpdateNewChannelMessage + ) + + EDIT_UPDATES = ( + types.UpdateEditMessage, + types.UpdateEditChannelMessage + ) + + ALLOWED_UPDATES = MESSAGE_UPDATES + EDIT_UPDATES + def __init__(self, client, workers): self.client = client self.workers = workers @@ -41,7 +53,10 @@ class Dispatcher: def start(self): for i in range(self.workers): - Thread(target=self.update_worker, name="UpdateWorker#{}".format(i + 1)).start() + Thread( + target=self.update_worker, + name="UpdateWorker#{}".format(i + 1) + ).start() def stop(self): for _ in range(self.workers): @@ -57,7 +72,8 @@ class Dispatcher: else: raise ValueError( "'{0}' is already registered in Group #{1}. " - "You can register a different handler in this group or another '{0}' in a different group".format( + "You can register a different handler in this group " + "or another '{0}' in a different group".format( type(handler).__name__, group ) @@ -96,30 +112,40 @@ class Dispatcher: chats = {i.id: i for i in update[2]} update = update[0] - valid_updates = (types.UpdateNewMessage, types.UpdateNewChannelMessage, - types.UpdateEditMessage, types.UpdateEditChannelMessage) - - if isinstance(update, valid_updates): - message = update.message - - if isinstance(message, types.Message): - m = message_parser.parse_message(self.client, message, users, chats) - elif isinstance(message, types.MessageService): - m = message_parser.parse_message_service(self.client, message, users, chats) + if isinstance(update, Dispatcher.ALLOWED_UPDATES): + if isinstance(update.message, types.Message): + parser = message_parser.parse_message + elif isinstance(update.message, types.MessageService): + parser = message_parser.parse_message_service else: continue + + message = parser( + self.client, + update.message, + users, + chats + ) else: continue - edit = isinstance(update, (types.UpdateEditMessage, types.UpdateEditChannelMessage)) + is_edited_message = isinstance(update, Dispatcher.EDIT_UPDATES) self.dispatch( pyrogram.Update( update_id=0, - message=(m if m.chat.type is not "channel" else None) if not edit else None, - edited_message=(m if m.chat.type is not "channel" else None) if edit else None, - channel_post=(m if m.chat.type is "channel" else None) if not edit else None, - edited_channel_post=(m if m.chat.type is "channel" else None) if edit else None + message=((message if message.chat.type != "channel" + else None) if not is_edited_message + else None), + edited_message=((message if message.chat.type != "channel" + else None) if is_edited_message + else None), + channel_post=((message if message.chat.type == "channel" + else None) if not is_edited_message + else None), + edited_channel_post=((message if message.chat.type == "channel" + else None) if is_edited_message + else None) ) ) except Exception as e: From 1a7ab62ed9bf56cc94f4c1af63b9c791ec24299d Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sun, 8 Apr 2018 13:20:31 +0200 Subject: [PATCH 087/112] Add handler for raw mtproto updates --- pyrogram/__init__.py | 2 +- pyrogram/client/client.py | 10 ++++-- pyrogram/client/dispatcher/dispatcher.py | 31 ++++++++++++------- pyrogram/client/handler/__init__.py | 1 + pyrogram/client/handler/raw_update_handler.py | 24 ++++++++++++++ 5 files changed, 53 insertions(+), 15 deletions(-) create mode 100644 pyrogram/client/handler/raw_update_handler.py diff --git a/pyrogram/__init__.py b/pyrogram/__init__.py index e366ae20..18813d55 100644 --- a/pyrogram/__init__.py +++ b/pyrogram/__init__.py @@ -34,4 +34,4 @@ from .client.input_media_photo import InputMediaPhoto from .client.input_media_video import InputMediaVideo from .client.input_phone_contact import InputPhoneContact from .client import Emoji -from .client.handler import MessageHandler +from .client.handler import MessageHandler, RawUpdateHandler diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py index e67876d9..12030a9c 100644 --- a/pyrogram/client/client.py +++ b/pyrogram/client/client.py @@ -189,13 +189,19 @@ class Client: self.dispatcher = Dispatcher(self, workers) self.update_handler = None - def on_message(self, group: int = 0): + def on(self, handler, group): def decorator(f): - self.add_handler(pyrogram.MessageHandler(f), group) + self.add_handler(handler(f), group) return f return decorator + def on_message(self, group: int = 0): + return self.on(pyrogram.MessageHandler, group) + + def on_raw_update(self, group: int = 0): + return self.on(pyrogram.RawUpdateHandler, group) + def add_handler(self, handler: Handler, group: int = 0): self.dispatcher.add_handler(handler, group) diff --git a/pyrogram/client/dispatcher/dispatcher.py b/pyrogram/client/dispatcher/dispatcher.py index a5ae9049..fb4e46a8 100644 --- a/pyrogram/client/dispatcher/dispatcher.py +++ b/pyrogram/client/dispatcher/dispatcher.py @@ -26,7 +26,7 @@ import pyrogram from pyrogram.api import types from . import message_parser from ..handler import ( - Handler, MessageHandler + Handler, MessageHandler, RawUpdateHandler ) log = logging.getLogger(__name__) @@ -79,23 +79,28 @@ class Dispatcher: ) ) - def dispatch(self, update): - message = (update.message - or update.channel_post - or update.edited_message - or update.edited_channel_post) - - if message: - key = MessageHandler - value = message + def dispatch(self, update, users: dict = None, chats: dict = None, is_raw: bool = False): + if is_raw: + key = RawUpdateHandler + value = update else: - return + message = (update.message + or update.channel_post + or update.edited_message + or update.edited_channel_post) + + if message: + key = MessageHandler + value = message + else: + return for group in self.handlers.values(): handler = group.get(key, None) if handler is not None: - handler.callback(self.client, value) + args = (self, value, users, chats) if is_raw else (self.client, value) + handler.callback(*args) def update_worker(self): name = threading.current_thread().name @@ -112,6 +117,8 @@ class Dispatcher: chats = {i.id: i for i in update[2]} update = update[0] + self.dispatch(update, users=users, chats=chats, is_raw=True) + if isinstance(update, Dispatcher.ALLOWED_UPDATES): if isinstance(update.message, types.Message): parser = message_parser.parse_message diff --git a/pyrogram/client/handler/__init__.py b/pyrogram/client/handler/__init__.py index c86ca6ec..be0bfb56 100644 --- a/pyrogram/client/handler/__init__.py +++ b/pyrogram/client/handler/__init__.py @@ -18,3 +18,4 @@ from .handler import Handler from .message_handler import MessageHandler +from .raw_update_handler import RawUpdateHandler diff --git a/pyrogram/client/handler/raw_update_handler.py b/pyrogram/client/handler/raw_update_handler.py new file mode 100644 index 00000000..758606db --- /dev/null +++ b/pyrogram/client/handler/raw_update_handler.py @@ -0,0 +1,24 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2018 Dan Tès +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from .handler import Handler + + +class RawUpdateHandler(Handler): + def __init__(self, callback: callable): + super().__init__(callback) From 28ffff57e1cb9f9571c42e28290b66464ea0ab2d Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sun, 8 Apr 2018 13:23:26 +0200 Subject: [PATCH 088/112] Move file_id codec outside --- pyrogram/client/dispatcher/message_parser.py | 38 +------------------- pyrogram/client/utils.py | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+), 37 deletions(-) create mode 100644 pyrogram/client/utils.py diff --git a/pyrogram/client/dispatcher/message_parser.py b/pyrogram/client/dispatcher/message_parser.py index a101b78e..42e0a327 100644 --- a/pyrogram/client/dispatcher/message_parser.py +++ b/pyrogram/client/dispatcher/message_parser.py @@ -16,11 +16,11 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . -from base64 import b64encode, b64decode from struct import pack import pyrogram from pyrogram.api import types +from ..utils import encode # TODO: Organize the code better? @@ -534,39 +534,3 @@ def parse_message_service( m.pinned_message = parse_message_service(client, message, users, chats) return m - - -def decode(s: str) -> bytes: - s = b64decode(s + "=" * (-len(s) % 4), "-_") - r = b"" - - assert s[-1] == 2 - - i = 0 - while i < len(s) - 1: - if s[i] != 0: - r += bytes([s[i]]) - else: - r += b"\x00" * s[i + 1] - i += 1 - - i += 1 - - return r - - -def encode(s: bytes) -> str: - r = b"" - n = 0 - - for i in s + bytes([2]): - if i == 0: - n += 1 - else: - if n: - r += b"\x00" + bytes([n]) - n = 0 - - r += bytes([i]) - - return b64encode(r, b"-_").decode().rstrip("=") diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py new file mode 100644 index 00000000..e1819b43 --- /dev/null +++ b/pyrogram/client/utils.py @@ -0,0 +1,37 @@ +from base64 import b64decode, b64encode + + +def decode(s: str) -> bytes: + s = b64decode(s + "=" * (-len(s) % 4), "-_") + r = b"" + + assert s[-1] == 2 + + i = 0 + while i < len(s) - 1: + if s[i] != 0: + r += bytes([s[i]]) + else: + r += b"\x00" * s[i + 1] + i += 1 + + i += 1 + + return r + + +def encode(s: bytes) -> str: + r = b"" + n = 0 + + for i in s + bytes([2]): + if i == 0: + n += 1 + else: + if n: + r += b"\x00" + bytes([n]) + n = 0 + + r += bytes([i]) + + return b64encode(r, b"-_").decode().rstrip("=") From 578047dbece9c5d10243da91c70516e95acf9e1c Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sun, 8 Apr 2018 15:36:22 +0200 Subject: [PATCH 089/112] Move message_parser outside --- pyrogram/client/dispatcher/dispatcher.py | 2 +- pyrogram/client/{dispatcher => }/message_parser.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename pyrogram/client/{dispatcher => }/message_parser.py (99%) diff --git a/pyrogram/client/dispatcher/dispatcher.py b/pyrogram/client/dispatcher/dispatcher.py index fb4e46a8..e1ffe293 100644 --- a/pyrogram/client/dispatcher/dispatcher.py +++ b/pyrogram/client/dispatcher/dispatcher.py @@ -24,7 +24,7 @@ from threading import Thread import pyrogram from pyrogram.api import types -from . import message_parser +from .. import message_parser from ..handler import ( Handler, MessageHandler, RawUpdateHandler ) diff --git a/pyrogram/client/dispatcher/message_parser.py b/pyrogram/client/message_parser.py similarity index 99% rename from pyrogram/client/dispatcher/message_parser.py rename to pyrogram/client/message_parser.py index 42e0a327..f5ff8aa5 100644 --- a/pyrogram/client/dispatcher/message_parser.py +++ b/pyrogram/client/message_parser.py @@ -20,7 +20,7 @@ from struct import pack import pyrogram from pyrogram.api import types -from ..utils import encode +from .utils import encode # TODO: Organize the code better? From 245720278e92643e4912aebafd052a129389edf7 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sun, 8 Apr 2018 16:50:18 +0200 Subject: [PATCH 090/112] Enhance send_photo by accepting file_ids and URLs This is the first step of a total revamp of the current Pyrogram API --- pyrogram/client/client.py | 71 ++++++++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 9 deletions(-) diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py index 34b4273f..74dfdafd 100644 --- a/pyrogram/client/client.py +++ b/pyrogram/client/client.py @@ -45,14 +45,16 @@ from pyrogram.api.errors import ( PhoneCodeExpired, PhoneCodeEmpty, SessionPasswordNeeded, PasswordHashInvalid, FloodWait, PeerIdInvalid, FilePartMissing, ChatAdminRequired, FirstnameInvalid, PhoneNumberBanned, - VolumeLocNotFound, UserMigrate) + VolumeLocNotFound, UserMigrate, FileIdInvalid) from pyrogram.crypto import AES from pyrogram.session import Auth, Session from pyrogram.session.internals import MsgId +from . import message_parser from .dispatcher import Dispatcher from .handler import Handler from .input_media import InputMedia from .style import Markdown, HTML +from .utils import decode log = logging.getLogger(__name__) @@ -133,6 +135,18 @@ class Client: UPDATES_WORKERS = 1 DOWNLOAD_WORKERS = 1 + MEDIA_TYPE_ID = { + 0: "Thumbnail", + 2: "Photo", + 3: "Voice", + 4: "Video", + 5: "Document", + 8: "Sticker", + 9: "Audio", + 10: "GIF", + 13: "VideoNote" + } + def __init__(self, session_name: str, api_id: int or str = None, @@ -1075,7 +1089,9 @@ class Client: photo (``str``): Photo to send. - Pass a file path as string to send a photo that exists on your local machine. + Pass a file_id as string to send a photo that exists on the Telegram servers, + pass an HTTP URL as a string for Telegram to get a photo from the Internet, or + pass a file path as string to upload a new photo that exists on your local machine. caption (``bool``, optional): Photo caption, 0-200 characters. @@ -1109,23 +1125,55 @@ class Client: The size of the file. Returns: - On success, the sent Message is returned. + On success, the sent :obj:`Message ` is returned. Raises: :class:`Error ` """ + file = None style = self.html if parse_mode.lower() == "html" else self.markdown - file = self.save_file(photo, progress=progress) + + if os.path.exists(photo): + file = self.save_file(photo, progress=progress) + media = types.InputMediaUploadedPhoto( + file=file, + ttl_seconds=ttl_seconds + ) + elif photo.startswith("http"): + media = types.InputMediaPhotoExternal( + url=photo, + ttl_seconds=ttl_seconds + ) + else: + try: + decoded = decode(photo) + fmt = " 24 else " Date: Mon, 9 Apr 2018 22:02:44 +0200 Subject: [PATCH 091/112] Enhance send_audio by accepting file_ids and URLs --- pyrogram/client/client.py | 69 ++++++++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 16 deletions(-) diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py index 74dfdafd..3576128a 100644 --- a/pyrogram/client/client.py +++ b/pyrogram/client/client.py @@ -1214,7 +1214,9 @@ class Client: audio (``str``): Audio file to send. - Pass a file path as string to send an audio file that exists on your local machine. + Pass a file_id as string to send an audio file that exists on the Telegram servers, + pass an HTTP URL as a string for Telegram to get an audio file from the Internet, or + pass a file path as string to upload a new audio file that exists on your local machine. caption (``str``, optional): Audio caption, 0-200 characters. @@ -1252,31 +1254,61 @@ class Client: The size of the file. Returns: - On success, the sent Message is returned. + On success, the sent :obj:`Message ` is returned. Raises: :class:`Error ` """ + file = None style = self.html if parse_mode.lower() == "html" else self.markdown - file = self.save_file(audio, progress=progress) + + if os.path.exists(audio): + file = self.save_file(audio, progress=progress) + media = types.InputMediaUploadedDocument( + mime_type=mimetypes.types_map.get("." + audio.split(".")[-1], "audio/mpeg"), + file=file, + attributes=[ + types.DocumentAttributeAudio( + duration=duration, + performer=performer, + title=title + ), + types.DocumentAttributeFilename(os.path.basename(audio)) + ] + ) + elif audio.startswith("http"): + media = types.InputMediaDocumentExternal( + url=audio + ) + else: + try: + decoded = decode(audio) + fmt = " 24 else " Date: Mon, 9 Apr 2018 23:30:50 +0200 Subject: [PATCH 092/112] Add base Filter class --- pyrogram/client/filters/filter.py | 57 +++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 pyrogram/client/filters/filter.py diff --git a/pyrogram/client/filters/filter.py b/pyrogram/client/filters/filter.py new file mode 100644 index 00000000..feec51df --- /dev/null +++ b/pyrogram/client/filters/filter.py @@ -0,0 +1,57 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2018 Dan Tès +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + + +class Filter: + def __call__(self, message): + raise NotImplementedError + + def __invert__(self): + return InvertFilter(self) + + def __and__(self, other): + return AndFilter(self, other) + + def __or__(self, other): + return OrFilter(self, other) + + +class InvertFilter(Filter): + def __init__(self, base): + self.base = base + + def __call__(self, message): + return not self.base(message) + + +class AndFilter(Filter): + def __init__(self, base, other): + self.base = base + self.other = other + + def __call__(self, message): + return self.base(message) and self.other(message) + + +class OrFilter(Filter): + def __init__(self, base, other): + self.base = base + self.other = other + + def __call__(self, message): + return self.base(message) or self.other(message) From fb4e98b0b59807291d1054b6ee44f189163182ca Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Mon, 9 Apr 2018 23:35:51 +0200 Subject: [PATCH 093/112] Evaluate filters before dispatching messages --- pyrogram/client/dispatcher/dispatcher.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pyrogram/client/dispatcher/dispatcher.py b/pyrogram/client/dispatcher/dispatcher.py index e1ffe293..957e806b 100644 --- a/pyrogram/client/dispatcher/dispatcher.py +++ b/pyrogram/client/dispatcher/dispatcher.py @@ -99,7 +99,14 @@ class Dispatcher: handler = group.get(key, None) if handler is not None: - args = (self, value, users, chats) if is_raw else (self.client, value) + if is_raw: + args = (self, value, users, chats) + else: + if not handler.check(value): + continue + + args = (self.client, value) + handler.callback(*args) def update_worker(self): From 142ce0757675cd85d688777429c3cb5e3f4780c7 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 10 Apr 2018 00:24:03 +0200 Subject: [PATCH 094/112] Make handlers accept filters --- pyrogram/client/client.py | 12 ++++++------ pyrogram/client/handler/handler.py | 3 ++- pyrogram/client/handler/message_handler.py | 11 +++++++++-- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py index 3576128a..179b11e2 100644 --- a/pyrogram/client/client.py +++ b/pyrogram/client/client.py @@ -203,15 +203,15 @@ class Client: self.dispatcher = Dispatcher(self, workers) self.update_handler = None - def on(self, handler, group): - def decorator(f): - self.add_handler(handler(f), group) - return f + def on(self, handler, group, filters=None): + def decorator(func): + self.add_handler(handler(func, filters), group) + return func return decorator - def on_message(self, group: int = 0): - return self.on(pyrogram.MessageHandler, group) + def on_message(self, filters=None, group: int = 0): + return self.on(pyrogram.MessageHandler, group, filters) def on_raw_update(self, group: int = 0): return self.on(pyrogram.RawUpdateHandler, group) diff --git a/pyrogram/client/handler/handler.py b/pyrogram/client/handler/handler.py index 5bcde2f9..0e46a205 100644 --- a/pyrogram/client/handler/handler.py +++ b/pyrogram/client/handler/handler.py @@ -18,5 +18,6 @@ class Handler: - def __init__(self, callback: callable): + def __init__(self, callback: callable, filters=None): self.callback = callback + self.filters = filters diff --git a/pyrogram/client/handler/message_handler.py b/pyrogram/client/handler/message_handler.py index 3b751fd7..fdbba245 100644 --- a/pyrogram/client/handler/message_handler.py +++ b/pyrogram/client/handler/message_handler.py @@ -20,5 +20,12 @@ from .handler import Handler class MessageHandler(Handler): - def __init__(self, callback: callable): - super().__init__(callback) + def __init__(self, callback: callable, filters=None): + super().__init__(callback, filters) + + def check(self, message): + return ( + self.filters(message) + if self.filters + else True + ) From b3506a7afabc7ddd59b2bf7979f81a4399711a1a Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 10 Apr 2018 00:25:04 +0200 Subject: [PATCH 095/112] Add a text filter (more to come) --- pyrogram/client/filters/__init__.py | 19 +++++++++++++++++++ pyrogram/client/filters/filters.py | 27 +++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 pyrogram/client/filters/__init__.py create mode 100644 pyrogram/client/filters/filters.py diff --git a/pyrogram/client/filters/__init__.py b/pyrogram/client/filters/__init__.py new file mode 100644 index 00000000..88ae14e3 --- /dev/null +++ b/pyrogram/client/filters/__init__.py @@ -0,0 +1,19 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2018 Dan Tès +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from .filters import Filters diff --git a/pyrogram/client/filters/filters.py b/pyrogram/client/filters/filters.py new file mode 100644 index 00000000..50d841d5 --- /dev/null +++ b/pyrogram/client/filters/filters.py @@ -0,0 +1,27 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2018 Dan Tès +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from .filter import Filter + + +def build(name: str, func: callable) -> type: + return type(name, (Filter,), {"__call__": func})() + + +class Filters: + text = build("Text", lambda self, m: bool(m.text)) From b0a528cadfbb74ba830a9ea3a9fbfbf6d1dce71c Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 10 Apr 2018 00:25:41 +0200 Subject: [PATCH 096/112] Make Filters importable via the main package --- pyrogram/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyrogram/__init__.py b/pyrogram/__init__.py index 18813d55..fbb73eb4 100644 --- a/pyrogram/__init__.py +++ b/pyrogram/__init__.py @@ -35,3 +35,4 @@ from .client.input_media_video import InputMediaVideo from .client.input_phone_contact import InputPhoneContact from .client import Emoji from .client.handler import MessageHandler, RawUpdateHandler +from .client.filters import Filters From 9ce13518ecf1e2d151bce59d61f08d1d7536a03e Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 10 Apr 2018 00:25:51 +0200 Subject: [PATCH 097/112] Add missing notice --- pyrogram/client/utils.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pyrogram/client/utils.py b/pyrogram/client/utils.py index e1819b43..dc258993 100644 --- a/pyrogram/client/utils.py +++ b/pyrogram/client/utils.py @@ -1,3 +1,21 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2018 Dan Tès +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + from base64 import b64decode, b64encode From f2424d3b1e701abfa1727ca4103abea73f849748 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 10 Apr 2018 13:14:10 +0200 Subject: [PATCH 098/112] Add some more filters --- pyrogram/client/filters/filters.py | 37 +++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/pyrogram/client/filters/filters.py b/pyrogram/client/filters/filters.py index 50d841d5..ff043a15 100644 --- a/pyrogram/client/filters/filters.py +++ b/pyrogram/client/filters/filters.py @@ -16,12 +16,43 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . +import re + from .filter import Filter -def build(name: str, func: callable) -> type: - return type(name, (Filter,), {"__call__": func})() +def build(name: str, func: callable, **kwargs) -> type: + d = {"__call__": func} + d.update(kwargs) + + return type(name, (Filter,), d)() class Filters: - text = build("Text", lambda self, m: bool(m.text)) + text = build("Text", lambda _, m: bool(m.text and not m.text.startswith("/"))) + command = build("Command", lambda _, m: bool(m.text and m.text.startswith("/"))) + reply = build("Reply", lambda _, m: bool(m.reply_to_message)) + forwarded = build("Forwarded", lambda _, m: bool(m.forward_date)) + caption = build("Caption", lambda _, m: bool(m.caption)) + + audio = build("Audio", lambda _, m: bool(m.audio)) + document = build("Document", lambda _, m: bool(m.document)) + photo = build("Photo", lambda _, m: bool(m.photo)) + sticker = build("Sticker", lambda _, m: bool(m.sticker)) + video = build("Video", lambda _, m: bool(m.video)) + voice = build("Voice", lambda _, m: bool(m.voice)) + video_note = build("Voice", lambda _, m: bool(m.video_note)) + contact = build("Contact", lambda _, m: bool(m.contact)) + location = build("Location", lambda _, m: bool(m.location)) + venue = build("Venue", lambda _, m: bool(m.venue)) + + private = build("Private", lambda _, m: bool(m.chat.type == "private")) + group = build("Group", lambda _, m: bool(m.chat.type in ("group", "supergroup"))) + channel = build("Channel", lambda _, m: bool(m.chat.type == "channel")) + + @staticmethod + def regex(pattern, flags: int = 0): + return build( + "Regex", lambda _, m: bool(_.p.search(m.text or "")), + p=re.compile(pattern, flags) + ) From c33c7c76fdcb53d584ebc8316d8d8650d00191ac Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 10 Apr 2018 14:52:31 +0200 Subject: [PATCH 099/112] Rework dispatcher --- pyrogram/client/dispatcher/dispatcher.py | 60 +++++++++--------------- 1 file changed, 21 insertions(+), 39 deletions(-) diff --git a/pyrogram/client/dispatcher/dispatcher.py b/pyrogram/client/dispatcher/dispatcher.py index 957e806b..62086acd 100644 --- a/pyrogram/client/dispatcher/dispatcher.py +++ b/pyrogram/client/dispatcher/dispatcher.py @@ -26,7 +26,7 @@ import pyrogram from pyrogram.api import types from .. import message_parser from ..handler import ( - Handler, MessageHandler, RawUpdateHandler + Handler, RawUpdateHandler ) log = logging.getLogger(__name__) @@ -49,7 +49,7 @@ class Dispatcher: self.client = client self.workers = workers self.updates = Queue() - self.handlers = OrderedDict() + self.groups = OrderedDict() def start(self): for i in range(self.workers): @@ -63,51 +63,33 @@ class Dispatcher: self.updates.put(None) def add_handler(self, handler: Handler, group: int): - if group not in self.handlers: - self.handlers[group] = {} - self.handlers = OrderedDict(sorted(self.handlers.items())) + if group not in self.groups: + self.groups[group] = [] + self.groups = OrderedDict(sorted(self.groups.items())) - if type(handler) not in self.handlers[group]: - self.handlers[group][type(handler)] = handler - else: - raise ValueError( - "'{0}' is already registered in Group #{1}. " - "You can register a different handler in this group " - "or another '{0}' in a different group".format( - type(handler).__name__, - group - ) - ) + self.groups[group].append(handler) def dispatch(self, update, users: dict = None, chats: dict = None, is_raw: bool = False): - if is_raw: - key = RawUpdateHandler - value = update - else: - message = (update.message - or update.channel_post - or update.edited_message - or update.edited_channel_post) - - if message: - key = MessageHandler - value = message - else: - return - - for group in self.handlers.values(): - handler = group.get(key, None) - - if handler is not None: + for group in self.groups.values(): + for handler in group: if is_raw: - args = (self, value, users, chats) - else: - if not handler.check(value): + if not isinstance(handler, RawUpdateHandler): continue - args = (self.client, value) + args = (self.client, update, users, chats) + else: + message = (update.message + or update.channel_post + or update.edited_message + or update.edited_channel_post) + + if not handler.check(message): + continue + + args = (self.client, message) handler.callback(*args) + break def update_worker(self): name = threading.current_thread().name From 7537a276201d93841d698bc4b878a74f4bf20973 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 10 Apr 2018 14:54:06 +0200 Subject: [PATCH 100/112] Accept command strings as parameter --- pyrogram/client/filters/filters.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/pyrogram/client/filters/filters.py b/pyrogram/client/filters/filters.py index ff043a15..be6a935d 100644 --- a/pyrogram/client/filters/filters.py +++ b/pyrogram/client/filters/filters.py @@ -30,7 +30,23 @@ def build(name: str, func: callable, **kwargs) -> type: class Filters: text = build("Text", lambda _, m: bool(m.text and not m.text.startswith("/"))) - command = build("Command", lambda _, m: bool(m.text and m.text.startswith("/"))) + + @staticmethod + def command(command: str or list = list()): + return build( + "Command", + lambda _, m: bool( + m.text + and m.text.startswith("/") + and (m.text[1:].split()[0] in _.c) + ), + c=( + [command] + if not isinstance(command, list) + else command + ) + ) + reply = build("Reply", lambda _, m: bool(m.reply_to_message)) forwarded = build("Forwarded", lambda _, m: bool(m.forward_date)) caption = build("Caption", lambda _, m: bool(m.caption)) From 9165c7f591d4cfb8ba7e315e919a2f5bc64c3add Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 10 Apr 2018 14:54:39 +0200 Subject: [PATCH 101/112] Rework decorators impl --- pyrogram/client/client.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py index 179b11e2..6545e9a2 100644 --- a/pyrogram/client/client.py +++ b/pyrogram/client/client.py @@ -203,18 +203,19 @@ class Client: self.dispatcher = Dispatcher(self, workers) self.update_handler = None - def on(self, handler, group, filters=None): + def on_message(self, filters=None, group: int = 0): def decorator(func): - self.add_handler(handler(func, filters), group) + self.add_handler(pyrogram.MessageHandler(func, filters), group) return func return decorator - def on_message(self, filters=None, group: int = 0): - return self.on(pyrogram.MessageHandler, group, filters) - def on_raw_update(self, group: int = 0): - return self.on(pyrogram.RawUpdateHandler, group) + def decorator(func): + self.add_handler(pyrogram.RawUpdateHandler(func), group) + return func + + return decorator def add_handler(self, handler: Handler, group: int = 0): self.dispatcher.add_handler(handler, group) From 3b028698f7986a803e8d8945f80d2e12a08893a6 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 10 Apr 2018 15:04:31 +0200 Subject: [PATCH 102/112] Make command parameter non-optional --- pyrogram/client/filters/filters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyrogram/client/filters/filters.py b/pyrogram/client/filters/filters.py index be6a935d..f57f961f 100644 --- a/pyrogram/client/filters/filters.py +++ b/pyrogram/client/filters/filters.py @@ -32,7 +32,7 @@ class Filters: text = build("Text", lambda _, m: bool(m.text and not m.text.startswith("/"))) @staticmethod - def command(command: str or list = list()): + def command(command: str or list): return build( "Command", lambda _, m: bool( From a6b6b0dfd6d11df3c0693bc21a6dfa28332bc27d Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 10 Apr 2018 16:04:22 +0200 Subject: [PATCH 103/112] Add edited filter --- pyrogram/client/filters/filters.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyrogram/client/filters/filters.py b/pyrogram/client/filters/filters.py index f57f961f..36046185 100644 --- a/pyrogram/client/filters/filters.py +++ b/pyrogram/client/filters/filters.py @@ -50,6 +50,7 @@ class Filters: reply = build("Reply", lambda _, m: bool(m.reply_to_message)) forwarded = build("Forwarded", lambda _, m: bool(m.forward_date)) caption = build("Caption", lambda _, m: bool(m.caption)) + edited = build("Edited", lambda _, m: bool(m.edit_date)) audio = build("Audio", lambda _, m: bool(m.audio)) document = build("Document", lambda _, m: bool(m.document)) From 793ecc2ab5640fb350064e6731668381aaef31d7 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 10 Apr 2018 16:24:39 +0200 Subject: [PATCH 104/112] Add user filter --- pyrogram/client/filters/filters.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pyrogram/client/filters/filters.py b/pyrogram/client/filters/filters.py index 36046185..1a70f130 100644 --- a/pyrogram/client/filters/filters.py +++ b/pyrogram/client/filters/filters.py @@ -73,3 +73,17 @@ class Filters: "Regex", lambda _, m: bool(_.p.search(m.text or "")), p=re.compile(pattern, flags) ) + + @staticmethod + def user(user: int or str or list()): + return build( + "User", + lambda _, m: bool(m.from_user + and (m.from_user.id in _.u + or m.from_user.username.lower() in _.u)), + u=( + {user.lower().strip("@") if type(user) is str else user} + if not isinstance(user, list) + else {i.lower().strip("@") if type(i) is str else i for i in user} + ) + ) From f553e521cec4e468442f98f012bdc354d5d3c805 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 10 Apr 2018 16:26:04 +0200 Subject: [PATCH 105/112] Use sets instead of lists or tuples For faster checks --- pyrogram/client/filters/filters.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyrogram/client/filters/filters.py b/pyrogram/client/filters/filters.py index 1a70f130..1eb8d645 100644 --- a/pyrogram/client/filters/filters.py +++ b/pyrogram/client/filters/filters.py @@ -41,9 +41,9 @@ class Filters: and (m.text[1:].split()[0] in _.c) ), c=( - [command] + {command} if not isinstance(command, list) - else command + else {c for c in command} ) ) @@ -64,7 +64,7 @@ class Filters: venue = build("Venue", lambda _, m: bool(m.venue)) private = build("Private", lambda _, m: bool(m.chat.type == "private")) - group = build("Group", lambda _, m: bool(m.chat.type in ("group", "supergroup"))) + group = build("Group", lambda _, m: bool(m.chat.type in {"group", "supergroup"})) channel = build("Channel", lambda _, m: bool(m.chat.type == "channel")) @staticmethod From dfa1e5128168384492724aaa90d4bce528a8a023 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 10 Apr 2018 16:27:04 +0200 Subject: [PATCH 106/112] Fix type hinting --- pyrogram/client/filters/filters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyrogram/client/filters/filters.py b/pyrogram/client/filters/filters.py index 1eb8d645..5a92b2b1 100644 --- a/pyrogram/client/filters/filters.py +++ b/pyrogram/client/filters/filters.py @@ -75,7 +75,7 @@ class Filters: ) @staticmethod - def user(user: int or str or list()): + def user(user: int or str or list): return build( "User", lambda _, m: bool(m.from_user From cbf9104aa31fddfbf94673451558e40760022f7c Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 10 Apr 2018 18:33:19 +0200 Subject: [PATCH 107/112] Add chat filter --- pyrogram/client/filters/filters.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/pyrogram/client/filters/filters.py b/pyrogram/client/filters/filters.py index 5a92b2b1..eafd8a83 100644 --- a/pyrogram/client/filters/filters.py +++ b/pyrogram/client/filters/filters.py @@ -80,10 +80,26 @@ class Filters: "User", lambda _, m: bool(m.from_user and (m.from_user.id in _.u - or m.from_user.username.lower() in _.u)), + or (m.from_user.username + and m.from_user.username.lower() in _.u))), u=( {user.lower().strip("@") if type(user) is str else user} if not isinstance(user, list) else {i.lower().strip("@") if type(i) is str else i for i in user} ) ) + + @staticmethod + def chat(chat: int or str or list): + return build( + "Chat", + lambda _, m: bool(m.chat + and (m.chat.id in _.c + or (m.chat.username + and m.chat.username.lower() in _.c))), + c=( + {chat.lower().strip("@") if type(chat) is str else chat} + if not isinstance(chat, list) + else {i.lower().strip("@") if type(i) is str else i for i in chat} + ) + ) From 059c1d7a27b9c99fec95c7f961e48a2c6bbd66c8 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 10 Apr 2018 19:23:40 +0200 Subject: [PATCH 108/112] Add service message filters --- pyrogram/client/filters/filters.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/pyrogram/client/filters/filters.py b/pyrogram/client/filters/filters.py index eafd8a83..6058b0ce 100644 --- a/pyrogram/client/filters/filters.py +++ b/pyrogram/client/filters/filters.py @@ -103,3 +103,33 @@ class Filters: else {i.lower().strip("@") if type(i) is str else i for i in chat} ) ) + + class _Service(Filter): + new_chat_members = build("NewChatMembers", lambda _, m: bool(m.new_chat_members)) + left_chat_member = build("LeftChatMember", lambda _, m: bool(m.left_chat_member)) + new_chat_title = build("NewChatTitle", lambda _, m: bool(m.new_chat_title)) + new_chat_photo = build("NewChatPhoto", lambda _, m: bool(m.new_chat_photo)) + delete_chat_photo = build("DeleteChatPhoto", lambda _, m: bool(m.delete_chat_photo)) + group_chat_created = build("GroupChatCreated", lambda _, m: bool(m.group_chat_created)) + supergroup_chat_created = build("SupergroupChatCreated", lambda _, m: bool(m.supergroup_chat_created)) + channel_chat_created = build("ChannelChatCreated", lambda _, m: bool(m.channel_chat_created)) + migrate_to_chat_id = build("MigrateToChatId", lambda _, m: bool(m.migrate_to_chat_id)) + migrate_from_chat_id = build("MigrateFromChatId", lambda _, m: bool(m.migrate_from_chat_id)) + pinned_message = build("PinnedMessage", lambda _, m: bool(m.pinned_message)) + + def __call__(self, message): + return bool( + self.new_chat_members(message) + or self.left_chat_member(message) + or self.new_chat_title(message) + or self.new_chat_photo(message) + or self.delete_chat_photo(message) + or self.group_chat_created(message) + or self.supergroup_chat_created(message) + or self.channel_chat_created(message) + or self.migrate_to_chat_id(message) + or self.migrate_from_chat_id(message) + or self.pinned_message(message) + ) + + service = _Service() From 847b8dd5d4f25282910d28200d6e8be4a9ad1540 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 10 Apr 2018 19:30:55 +0200 Subject: [PATCH 109/112] Add views field in messages --- compiler/api/source/pyrogram.tl | 2 +- pyrogram/client/message_parser.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/api/source/pyrogram.tl b/compiler/api/source/pyrogram.tl index e28b7c27..3faef182 100644 --- a/compiler/api/source/pyrogram.tl +++ b/compiler/api/source/pyrogram.tl @@ -5,7 +5,7 @@ pyrogram.update#b0700000 flags:# update_id:int message:flags.0?Message edited_message:flags.1?Message channel_post:flags.2?Message edited_channel_post:flags.3?Message inline_query:flags.4?InlineQuery chosen_inline_result:flags.5?ChosenInlineResult callback_query:flags.6?CallbackQuery shipping_query:flags.7?ShippingQuery pre_checkout_query:flags.8?PreCheckoutQuery = pyrogram.Update; pyrogram.user#b0700001 flags:# id:int is_bot:Bool first_name:string last_name:flags.0?string username:flags.1?string language_code:flags.2?string = pyrogram.User; pyrogram.chat#b0700002 flags:# id:int type:string title:flags.0?string username:flags.1?string first_name:flags.2?string last_name:flags.3?string all_members_are_administrators:flags.4?Bool photo:flags.5?ChatPhoto description:flags.6?string invite_link:flags.7?string pinned_message:flags.8?Message sticker_set_name:flags.9?string can_set_sticker_set:flags.10?Bool = pyrogram.Chat; -pyrogram.message#b0700003 flags:# message_id:int from_user:flags.0?User date:int chat:Chat forward_from:flags.1?User forward_from_chat:flags.2?Chat forward_from_message_id:flags.3?int forward_signature:flags.4?string forward_date:flags.5?int reply_to_message:flags.6?Message edit_date:flags.7?int media_group_id:flags.8?string author_signature:flags.9?string text:flags.10?string entities:flags.11?Vector caption_entities:flags.12?Vector audio:flags.13?Audio document:flags.14?Document game:flags.15?Game photo:flags.16?Vector sticker:flags.17?Sticker video:flags.18?Video voice:flags.19?Voice video_note:flags.20?VideoNote caption:flags.21?string contact:flags.22?Contact location:flags.23?Location venue:flags.24?Venue new_chat_members:flags.25?Vector left_chat_member:flags.26?User new_chat_title:flags.27?string new_chat_photo:flags.28?Vector delete_chat_photo:flags.29?true group_chat_created:flags.30?true supergroup_chat_created:flags.31?true channel_chat_created:flags.32?true migrate_to_chat_id:flags.33?int migrate_from_chat_id:flags.34?int pinned_message:flags.35?Message invoice:flags.36?Invoice successful_payment:flags.37?SuccessfulPayment connected_website:flags.38?string = pyrogram.Message; +pyrogram.message#b0700003 flags:# message_id:int from_user:flags.0?User date:int chat:Chat forward_from:flags.1?User forward_from_chat:flags.2?Chat forward_from_message_id:flags.3?int forward_signature:flags.4?string forward_date:flags.5?int reply_to_message:flags.6?Message edit_date:flags.7?int media_group_id:flags.8?string author_signature:flags.9?string text:flags.10?string entities:flags.11?Vector caption_entities:flags.12?Vector audio:flags.13?Audio document:flags.14?Document game:flags.15?Game photo:flags.16?Vector sticker:flags.17?Sticker video:flags.18?Video voice:flags.19?Voice video_note:flags.20?VideoNote caption:flags.21?string contact:flags.22?Contact location:flags.23?Location venue:flags.24?Venue new_chat_members:flags.25?Vector left_chat_member:flags.26?User new_chat_title:flags.27?string new_chat_photo:flags.28?Vector delete_chat_photo:flags.29?true group_chat_created:flags.30?true supergroup_chat_created:flags.31?true channel_chat_created:flags.32?true migrate_to_chat_id:flags.33?int migrate_from_chat_id:flags.34?int pinned_message:flags.35?Message invoice:flags.36?Invoice successful_payment:flags.37?SuccessfulPayment connected_website:flags.38?string views:flags.39?int = pyrogram.Message; pyrogram.messageEntity#b0700004 flags:# type:string offset:int length:int url:flags.0?string user:flags.1?User = pyrogram.MessageEntity; pyrogram.photoSize#b0700005 flags:# file_id:string width:int height:int file_size:flags.0?int = pyrogram.PhotoSize; pyrogram.audio#b0700006 flags:# file_id:string duration:int performer:flags.0?string title:flags.1?string mime_type:flags.2?string file_size:flags.3?int = pyrogram.Audio; diff --git a/pyrogram/client/message_parser.py b/pyrogram/client/message_parser.py index f5ff8aa5..9ce55bdd 100644 --- a/pyrogram/client/message_parser.py +++ b/pyrogram/client/message_parser.py @@ -410,7 +410,8 @@ def parse_message( video=video, video_note=video_note, sticker=sticker, - document=document + document=document, + views=message.views ) if message.reply_to_msg_id and replies: From c4f2906009636a670dc688cd1666dc5b3b11178c Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 10 Apr 2018 19:37:52 +0200 Subject: [PATCH 110/112] Add via_bot field in messages --- compiler/api/source/pyrogram.tl | 2 +- pyrogram/client/message_parser.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/api/source/pyrogram.tl b/compiler/api/source/pyrogram.tl index 3faef182..912359eb 100644 --- a/compiler/api/source/pyrogram.tl +++ b/compiler/api/source/pyrogram.tl @@ -5,7 +5,7 @@ pyrogram.update#b0700000 flags:# update_id:int message:flags.0?Message edited_message:flags.1?Message channel_post:flags.2?Message edited_channel_post:flags.3?Message inline_query:flags.4?InlineQuery chosen_inline_result:flags.5?ChosenInlineResult callback_query:flags.6?CallbackQuery shipping_query:flags.7?ShippingQuery pre_checkout_query:flags.8?PreCheckoutQuery = pyrogram.Update; pyrogram.user#b0700001 flags:# id:int is_bot:Bool first_name:string last_name:flags.0?string username:flags.1?string language_code:flags.2?string = pyrogram.User; pyrogram.chat#b0700002 flags:# id:int type:string title:flags.0?string username:flags.1?string first_name:flags.2?string last_name:flags.3?string all_members_are_administrators:flags.4?Bool photo:flags.5?ChatPhoto description:flags.6?string invite_link:flags.7?string pinned_message:flags.8?Message sticker_set_name:flags.9?string can_set_sticker_set:flags.10?Bool = pyrogram.Chat; -pyrogram.message#b0700003 flags:# message_id:int from_user:flags.0?User date:int chat:Chat forward_from:flags.1?User forward_from_chat:flags.2?Chat forward_from_message_id:flags.3?int forward_signature:flags.4?string forward_date:flags.5?int reply_to_message:flags.6?Message edit_date:flags.7?int media_group_id:flags.8?string author_signature:flags.9?string text:flags.10?string entities:flags.11?Vector caption_entities:flags.12?Vector audio:flags.13?Audio document:flags.14?Document game:flags.15?Game photo:flags.16?Vector sticker:flags.17?Sticker video:flags.18?Video voice:flags.19?Voice video_note:flags.20?VideoNote caption:flags.21?string contact:flags.22?Contact location:flags.23?Location venue:flags.24?Venue new_chat_members:flags.25?Vector left_chat_member:flags.26?User new_chat_title:flags.27?string new_chat_photo:flags.28?Vector delete_chat_photo:flags.29?true group_chat_created:flags.30?true supergroup_chat_created:flags.31?true channel_chat_created:flags.32?true migrate_to_chat_id:flags.33?int migrate_from_chat_id:flags.34?int pinned_message:flags.35?Message invoice:flags.36?Invoice successful_payment:flags.37?SuccessfulPayment connected_website:flags.38?string views:flags.39?int = pyrogram.Message; +pyrogram.message#b0700003 flags:# message_id:int from_user:flags.0?User date:int chat:Chat forward_from:flags.1?User forward_from_chat:flags.2?Chat forward_from_message_id:flags.3?int forward_signature:flags.4?string forward_date:flags.5?int reply_to_message:flags.6?Message edit_date:flags.7?int media_group_id:flags.8?string author_signature:flags.9?string text:flags.10?string entities:flags.11?Vector caption_entities:flags.12?Vector audio:flags.13?Audio document:flags.14?Document game:flags.15?Game photo:flags.16?Vector sticker:flags.17?Sticker video:flags.18?Video voice:flags.19?Voice video_note:flags.20?VideoNote caption:flags.21?string contact:flags.22?Contact location:flags.23?Location venue:flags.24?Venue new_chat_members:flags.25?Vector left_chat_member:flags.26?User new_chat_title:flags.27?string new_chat_photo:flags.28?Vector delete_chat_photo:flags.29?true group_chat_created:flags.30?true supergroup_chat_created:flags.31?true channel_chat_created:flags.32?true migrate_to_chat_id:flags.33?int migrate_from_chat_id:flags.34?int pinned_message:flags.35?Message invoice:flags.36?Invoice successful_payment:flags.37?SuccessfulPayment connected_website:flags.38?string views:flags.39?int via_bot:flags.40?User = pyrogram.Message; pyrogram.messageEntity#b0700004 flags:# type:string offset:int length:int url:flags.0?string user:flags.1?User = pyrogram.MessageEntity; pyrogram.photoSize#b0700005 flags:# file_id:string width:int height:int file_size:flags.0?int = pyrogram.PhotoSize; pyrogram.audio#b0700006 flags:# file_id:string duration:int performer:flags.0?string title:flags.1?string mime_type:flags.2?string file_size:flags.3?int = pyrogram.Audio; diff --git a/pyrogram/client/message_parser.py b/pyrogram/client/message_parser.py index 9ce55bdd..8057d844 100644 --- a/pyrogram/client/message_parser.py +++ b/pyrogram/client/message_parser.py @@ -411,7 +411,8 @@ def parse_message( video_note=video_note, sticker=sticker, document=document, - views=message.views + views=message.views, + via_bot=parse_user(users.get(message.via_bot_id, None)) ) if message.reply_to_msg_id and replies: From 84b1e697bb950e1b4f8243c99a4fae536f4f1ddb Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 10 Apr 2018 19:48:44 +0200 Subject: [PATCH 111/112] Add phone_number field in users --- compiler/api/source/pyrogram.tl | 2 +- pyrogram/client/message_parser.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/api/source/pyrogram.tl b/compiler/api/source/pyrogram.tl index 912359eb..a2716ade 100644 --- a/compiler/api/source/pyrogram.tl +++ b/compiler/api/source/pyrogram.tl @@ -3,7 +3,7 @@ ---types--- pyrogram.update#b0700000 flags:# update_id:int message:flags.0?Message edited_message:flags.1?Message channel_post:flags.2?Message edited_channel_post:flags.3?Message inline_query:flags.4?InlineQuery chosen_inline_result:flags.5?ChosenInlineResult callback_query:flags.6?CallbackQuery shipping_query:flags.7?ShippingQuery pre_checkout_query:flags.8?PreCheckoutQuery = pyrogram.Update; -pyrogram.user#b0700001 flags:# id:int is_bot:Bool first_name:string last_name:flags.0?string username:flags.1?string language_code:flags.2?string = pyrogram.User; +pyrogram.user#b0700001 flags:# id:int is_bot:Bool first_name:string last_name:flags.0?string username:flags.1?string language_code:flags.2?string phone_number:flags.3?string = pyrogram.User; pyrogram.chat#b0700002 flags:# id:int type:string title:flags.0?string username:flags.1?string first_name:flags.2?string last_name:flags.3?string all_members_are_administrators:flags.4?Bool photo:flags.5?ChatPhoto description:flags.6?string invite_link:flags.7?string pinned_message:flags.8?Message sticker_set_name:flags.9?string can_set_sticker_set:flags.10?Bool = pyrogram.Chat; pyrogram.message#b0700003 flags:# message_id:int from_user:flags.0?User date:int chat:Chat forward_from:flags.1?User forward_from_chat:flags.2?Chat forward_from_message_id:flags.3?int forward_signature:flags.4?string forward_date:flags.5?int reply_to_message:flags.6?Message edit_date:flags.7?int media_group_id:flags.8?string author_signature:flags.9?string text:flags.10?string entities:flags.11?Vector caption_entities:flags.12?Vector audio:flags.13?Audio document:flags.14?Document game:flags.15?Game photo:flags.16?Vector sticker:flags.17?Sticker video:flags.18?Video voice:flags.19?Voice video_note:flags.20?VideoNote caption:flags.21?string contact:flags.22?Contact location:flags.23?Location venue:flags.24?Venue new_chat_members:flags.25?Vector left_chat_member:flags.26?User new_chat_title:flags.27?string new_chat_photo:flags.28?Vector delete_chat_photo:flags.29?true group_chat_created:flags.30?true supergroup_chat_created:flags.31?true channel_chat_created:flags.32?true migrate_to_chat_id:flags.33?int migrate_from_chat_id:flags.34?int pinned_message:flags.35?Message invoice:flags.36?Invoice successful_payment:flags.37?SuccessfulPayment connected_website:flags.38?string views:flags.39?int via_bot:flags.40?User = pyrogram.Message; pyrogram.messageEntity#b0700004 flags:# type:string offset:int length:int url:flags.0?string user:flags.1?User = pyrogram.MessageEntity; diff --git a/pyrogram/client/message_parser.py b/pyrogram/client/message_parser.py index 8057d844..427b98b1 100644 --- a/pyrogram/client/message_parser.py +++ b/pyrogram/client/message_parser.py @@ -69,7 +69,8 @@ def parse_user(user: types.User) -> pyrogram.User or None: first_name=user.first_name, last_name=user.last_name, username=user.username, - language_code=user.lang_code + language_code=user.lang_code, + phone_number=user.phone ) if user else None From 472ed8e355e797181c299a72a9de9429a732e7b2 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Wed, 11 Apr 2018 03:16:48 +0200 Subject: [PATCH 112/112] Document the new features --- compiler/api/compiler.py | 2 +- docs/source/pyrogram/Filters.rst | 6 + docs/source/pyrogram/InputMedia.rst | 6 - docs/source/pyrogram/InputMediaPhoto.rst | 6 + docs/source/pyrogram/InputMediaVideo.rst | 6 + docs/source/pyrogram/MessageHandler.rst | 6 + docs/source/pyrogram/RawUpdateHandler.rst | 6 + docs/source/pyrogram/index.rst | 29 ++- pyrogram/__init__.py | 2 +- pyrogram/client/client.py | 38 +++- pyrogram/client/dispatcher/dispatcher.py | 6 +- pyrogram/client/filters/filters.py | 177 +++++++++++++----- pyrogram/client/handler/message_handler.py | 31 --- pyrogram/client/handler/raw_update_handler.py | 24 --- .../client/{handler => handlers}/__init__.py | 4 +- .../client/{handler => handlers}/handler.py | 0 pyrogram/client/handlers/handlers.py | 93 +++++++++ 17 files changed, 322 insertions(+), 120 deletions(-) create mode 100644 docs/source/pyrogram/Filters.rst delete mode 100644 docs/source/pyrogram/InputMedia.rst create mode 100644 docs/source/pyrogram/InputMediaPhoto.rst create mode 100644 docs/source/pyrogram/InputMediaVideo.rst create mode 100644 docs/source/pyrogram/MessageHandler.rst create mode 100644 docs/source/pyrogram/RawUpdateHandler.rst delete mode 100644 pyrogram/client/handler/message_handler.py delete mode 100644 pyrogram/client/handler/raw_update_handler.py rename pyrogram/client/{handler => handlers}/__init__.py (86%) rename pyrogram/client/{handler => handlers}/handler.py (100%) create mode 100644 pyrogram/client/handlers/handlers.py diff --git a/compiler/api/compiler.py b/compiler/api/compiler.py index 3d5af589..250de451 100644 --- a/compiler/api/compiler.py +++ b/compiler/api/compiler.py @@ -297,7 +297,7 @@ def start(): "{}: {}{}".format( arg_name, "``optional`` ".format(flag_number) if is_optional else "", - get_docstring_arg_type(arg_type) + get_docstring_arg_type(arg_type, is_pyrogram_type=c.namespace == "pyrogram") ) ) diff --git a/docs/source/pyrogram/Filters.rst b/docs/source/pyrogram/Filters.rst new file mode 100644 index 00000000..083bd64a --- /dev/null +++ b/docs/source/pyrogram/Filters.rst @@ -0,0 +1,6 @@ +Filters +======= + +.. autoclass:: pyrogram.Filters + :members: + :undoc-members: diff --git a/docs/source/pyrogram/InputMedia.rst b/docs/source/pyrogram/InputMedia.rst deleted file mode 100644 index c637bdc0..00000000 --- a/docs/source/pyrogram/InputMedia.rst +++ /dev/null @@ -1,6 +0,0 @@ -InputMedia -========== - -.. autoclass:: pyrogram.InputMedia - :members: - :undoc-members: diff --git a/docs/source/pyrogram/InputMediaPhoto.rst b/docs/source/pyrogram/InputMediaPhoto.rst new file mode 100644 index 00000000..abc3f456 --- /dev/null +++ b/docs/source/pyrogram/InputMediaPhoto.rst @@ -0,0 +1,6 @@ +InputMediaPhoto +=============== + +.. autoclass:: pyrogram.InputMediaPhoto + :members: + :undoc-members: diff --git a/docs/source/pyrogram/InputMediaVideo.rst b/docs/source/pyrogram/InputMediaVideo.rst new file mode 100644 index 00000000..de9c480b --- /dev/null +++ b/docs/source/pyrogram/InputMediaVideo.rst @@ -0,0 +1,6 @@ +InputMediaVideo +=============== + +.. autoclass:: pyrogram.InputMediaVideo + :members: + :undoc-members: diff --git a/docs/source/pyrogram/MessageHandler.rst b/docs/source/pyrogram/MessageHandler.rst new file mode 100644 index 00000000..de908bd3 --- /dev/null +++ b/docs/source/pyrogram/MessageHandler.rst @@ -0,0 +1,6 @@ +MessageHandler +============== + +.. autoclass:: pyrogram.MessageHandler + :members: + :undoc-members: diff --git a/docs/source/pyrogram/RawUpdateHandler.rst b/docs/source/pyrogram/RawUpdateHandler.rst new file mode 100644 index 00000000..3d74a34b --- /dev/null +++ b/docs/source/pyrogram/RawUpdateHandler.rst @@ -0,0 +1,6 @@ +\RawUpdateHandler +================ + +.. autoclass:: pyrogram.RawUpdateHandler + :members: + :undoc-members: diff --git a/docs/source/pyrogram/index.rst b/docs/source/pyrogram/index.rst index 160766eb..7701fd60 100644 --- a/docs/source/pyrogram/index.rst +++ b/docs/source/pyrogram/index.rst @@ -9,9 +9,36 @@ the same parameters as well, thus offering a familiar look to Bot developers. .. toctree:: Client + MessageHandler + RawUpdateHandler + Filters ChatAction ParseMode - InputMedia Error +Types +----- + +.. toctree:: + ../types/pyrogram/User + ../types/pyrogram/Chat + ../types/pyrogram/Message + ../types/pyrogram/MessageEntity + ../types/pyrogram/PhotoSize + ../types/pyrogram/Audio + ../types/pyrogram/Document + ../types/pyrogram/Video + ../types/pyrogram/Voice + ../types/pyrogram/VideoNote + ../types/pyrogram/Contact + ../types/pyrogram/Location + ../types/pyrogram/Venue + ../types/pyrogram/UserProfilePhotos + ../types/pyrogram/ChatPhoto + ../types/pyrogram/ChatMember + InputMediaPhoto + InputMediaVideo + ../types/pyrogram/Sticker + + .. _Telegram Bot API: https://core.telegram.org/bots/api#available-methods diff --git a/pyrogram/__init__.py b/pyrogram/__init__.py index fbb73eb4..d5e26b43 100644 --- a/pyrogram/__init__.py +++ b/pyrogram/__init__.py @@ -34,5 +34,5 @@ from .client.input_media_photo import InputMediaPhoto from .client.input_media_video import InputMediaVideo from .client.input_phone_contact import InputPhoneContact from .client import Emoji -from .client.handler import MessageHandler, RawUpdateHandler +from .client.handlers import MessageHandler, RawUpdateHandler from .client.filters import Filters diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py index 6545e9a2..7e8eb791 100644 --- a/pyrogram/client/client.py +++ b/pyrogram/client/client.py @@ -51,7 +51,6 @@ from pyrogram.session import Auth, Session from pyrogram.session.internals import MsgId from . import message_parser from .dispatcher import Dispatcher -from .handler import Handler from .input_media import InputMedia from .style import Markdown, HTML from .utils import decode @@ -204,6 +203,19 @@ class Client: self.update_handler = None def on_message(self, filters=None, group: int = 0): + """Use this decorator to automatically register a function for handling + messages. This does the same thing as :meth:`add_handler` using the + MessageHandler. + + Args: + filters (:obj:`Filters `): + Pass one or more filters to allow only a subset of messages to be passed + in your function. + + group (``int``, optional): + The group identifier, defaults to 0. + """ + def decorator(func): self.add_handler(pyrogram.MessageHandler(func, filters), group) return func @@ -211,13 +223,35 @@ class Client: return decorator def on_raw_update(self, group: int = 0): + """Use this decorator to automatically register a function for handling + raw updates. This does the same thing as :meth:`add_handler` using the + RawUpdateHandler. + + Args: + group (``int``, optional): + The group identifier, defaults to 0. + """ + def decorator(func): self.add_handler(pyrogram.RawUpdateHandler(func), group) return func return decorator - def add_handler(self, handler: Handler, group: int = 0): + def add_handler(self, handler, group: int = 0): + """Use this method to register an event handler. + + You can register multiple handlers, but at most one handler within a group + will be used for a single event. To handle the same event more than once, register + your handler using a different group id (lower group id == higher priority). + + Args: + handler (:obj:`Handler `): + The handler to be registered. + + group (``int``, optional): + The group identifier, defaults to 0. + """ self.dispatcher.add_handler(handler, group) def start(self): diff --git a/pyrogram/client/dispatcher/dispatcher.py b/pyrogram/client/dispatcher/dispatcher.py index 62086acd..2710c4c0 100644 --- a/pyrogram/client/dispatcher/dispatcher.py +++ b/pyrogram/client/dispatcher/dispatcher.py @@ -25,9 +25,7 @@ from threading import Thread import pyrogram from pyrogram.api import types from .. import message_parser -from ..handler import ( - Handler, RawUpdateHandler -) +from ..handlers import RawUpdateHandler log = logging.getLogger(__name__) @@ -62,7 +60,7 @@ class Dispatcher: for _ in range(self.workers): self.updates.put(None) - def add_handler(self, handler: Handler, group: int): + def add_handler(self, handler, group: int): if group not in self.groups: self.groups[group] = [] self.groups = OrderedDict(sorted(self.groups.items())) diff --git a/pyrogram/client/filters/filters.py b/pyrogram/client/filters/filters.py index 6058b0ce..ac2ecf67 100644 --- a/pyrogram/client/filters/filters.py +++ b/pyrogram/client/filters/filters.py @@ -29,10 +29,71 @@ def build(name: str, func: callable, **kwargs) -> type: class Filters: + """This class provides access to all the Filters available in Pyrogram. + It is intended to be used when adding an handler.""" + text = build("Text", lambda _, m: bool(m.text and not m.text.startswith("/"))) + """Filter text messages.""" + + reply = build("Reply", lambda _, m: bool(m.reply_to_message)) + """Filter messages that are replies to other messages.""" + + forwarded = build("Forwarded", lambda _, m: bool(m.forward_date)) + """Filter messages that are forwarded.""" + + caption = build("Caption", lambda _, m: bool(m.caption)) + """Filter media messages that contain captions.""" + + edited = build("Edited", lambda _, m: bool(m.edit_date)) + """Filter edited messages.""" + + audio = build("Audio", lambda _, m: bool(m.audio)) + """Filter messages that contain an :obj:`Audio `.""" + + document = build("Document", lambda _, m: bool(m.document)) + """Filter messages that contain a :obj:`Document `.""" + + photo = build("Photo", lambda _, m: bool(m.photo)) + """Filter messages that contain a :obj:`Photo `.""" + + sticker = build("Sticker", lambda _, m: bool(m.sticker)) + """Filter messages that contain a :obj:`Sticker `.""" + + video = build("Video", lambda _, m: bool(m.video)) + """Filter messages that contain a :obj:`Video `.""" + + voice = build("Voice", lambda _, m: bool(m.voice)) + """Filter messages that contain a :obj:`Voice ` note.""" + + video_note = build("Voice", lambda _, m: bool(m.video_note)) + """Filter messages that contain a :obj:`VideoNote `.""" + + contact = build("Contact", lambda _, m: bool(m.contact)) + """Filter messages that contain a :obj:`Contact `.""" + + location = build("Location", lambda _, m: bool(m.location)) + """Filter messages that contain a :obj:`Location `.""" + + venue = build("Venue", lambda _, m: bool(m.venue)) + """Filter messages that contain a :obj:`Venue `.""" + + private = build("Private", lambda _, m: bool(m.chat.type == "private")) + """Filter messages sent in private chats.""" + + group = build("Group", lambda _, m: bool(m.chat.type in {"group", "supergroup"})) + """Filter messages sent in group or supergroup chats.""" + + channel = build("Channel", lambda _, m: bool(m.chat.type == "channel")) + """Filter messages sent in channels.""" @staticmethod def command(command: str or list): + """Filter commands, i.e.: text messages starting with '/'. + + Args: + command (``str`` | ``list``): + The command or list of commands as strings the filter should look for. + """ return build( "Command", lambda _, m: bool( @@ -47,28 +108,17 @@ class Filters: ) ) - reply = build("Reply", lambda _, m: bool(m.reply_to_message)) - forwarded = build("Forwarded", lambda _, m: bool(m.forward_date)) - caption = build("Caption", lambda _, m: bool(m.caption)) - edited = build("Edited", lambda _, m: bool(m.edit_date)) - - audio = build("Audio", lambda _, m: bool(m.audio)) - document = build("Document", lambda _, m: bool(m.document)) - photo = build("Photo", lambda _, m: bool(m.photo)) - sticker = build("Sticker", lambda _, m: bool(m.sticker)) - video = build("Video", lambda _, m: bool(m.video)) - voice = build("Voice", lambda _, m: bool(m.voice)) - video_note = build("Voice", lambda _, m: bool(m.video_note)) - contact = build("Contact", lambda _, m: bool(m.contact)) - location = build("Location", lambda _, m: bool(m.location)) - venue = build("Venue", lambda _, m: bool(m.venue)) - - private = build("Private", lambda _, m: bool(m.chat.type == "private")) - group = build("Group", lambda _, m: bool(m.chat.type in {"group", "supergroup"})) - channel = build("Channel", lambda _, m: bool(m.chat.type == "channel")) - @staticmethod def regex(pattern, flags: int = 0): + """Filter messages that match a given RegEx pattern. + + Args: + pattern (``str``): + The RegEx pattern. + + flags (``int``, optional): + RegEx flags. + """ return build( "Regex", lambda _, m: bool(_.p.search(m.text or "")), p=re.compile(pattern, flags) @@ -76,6 +126,12 @@ class Filters: @staticmethod def user(user: int or str or list): + """Filter messages coming from specific users. + + Args: + user (``int`` | ``str`` | ``list``): + The user or list of user IDs (int) or usernames (str) the filter should look for. + """ return build( "User", lambda _, m: bool(m.from_user @@ -91,6 +147,12 @@ class Filters: @staticmethod def chat(chat: int or str or list): + """Filter messages coming from specific chats. + + Args: + chat (``int`` | ``str`` | ``list``): + The chat or list of chat IDs (int) or usernames (str) the filter should look for. + """ return build( "Chat", lambda _, m: bool(m.chat @@ -104,32 +166,53 @@ class Filters: ) ) - class _Service(Filter): - new_chat_members = build("NewChatMembers", lambda _, m: bool(m.new_chat_members)) - left_chat_member = build("LeftChatMember", lambda _, m: bool(m.left_chat_member)) - new_chat_title = build("NewChatTitle", lambda _, m: bool(m.new_chat_title)) - new_chat_photo = build("NewChatPhoto", lambda _, m: bool(m.new_chat_photo)) - delete_chat_photo = build("DeleteChatPhoto", lambda _, m: bool(m.delete_chat_photo)) - group_chat_created = build("GroupChatCreated", lambda _, m: bool(m.group_chat_created)) - supergroup_chat_created = build("SupergroupChatCreated", lambda _, m: bool(m.supergroup_chat_created)) - channel_chat_created = build("ChannelChatCreated", lambda _, m: bool(m.channel_chat_created)) - migrate_to_chat_id = build("MigrateToChatId", lambda _, m: bool(m.migrate_to_chat_id)) - migrate_from_chat_id = build("MigrateFromChatId", lambda _, m: bool(m.migrate_from_chat_id)) - pinned_message = build("PinnedMessage", lambda _, m: bool(m.pinned_message)) + new_chat_members = build("NewChatMembers", lambda _, m: bool(m.new_chat_members)) + """Filter service messages for new chat members.""" - def __call__(self, message): - return bool( - self.new_chat_members(message) - or self.left_chat_member(message) - or self.new_chat_title(message) - or self.new_chat_photo(message) - or self.delete_chat_photo(message) - or self.group_chat_created(message) - or self.supergroup_chat_created(message) - or self.channel_chat_created(message) - or self.migrate_to_chat_id(message) - or self.migrate_from_chat_id(message) - or self.pinned_message(message) - ) + left_chat_member = build("LeftChatMember", lambda _, m: bool(m.left_chat_member)) + """Filter service messages for members that left the chat.""" - service = _Service() + new_chat_title = build("NewChatTitle", lambda _, m: bool(m.new_chat_title)) + """Filter service messages for new chat titles.""" + + new_chat_photo = build("NewChatPhoto", lambda _, m: bool(m.new_chat_photo)) + """Filter service messages for new chat photos.""" + + delete_chat_photo = build("DeleteChatPhoto", lambda _, m: bool(m.delete_chat_photo)) + """Filter service messages for deleted photos.""" + + group_chat_created = build("GroupChatCreated", lambda _, m: bool(m.group_chat_created)) + """Filter service messages for group chat creations.""" + + supergroup_chat_created = build("SupergroupChatCreated", lambda _, m: bool(m.supergroup_chat_created)) + """Filter service messages for supergroup chat creations.""" + + channel_chat_created = build("ChannelChatCreated", lambda _, m: bool(m.channel_chat_created)) + """Filter service messages for channel chat creations.""" + + migrate_to_chat_id = build("MigrateToChatId", lambda _, m: bool(m.migrate_to_chat_id)) + """Filter service messages that contain migrate_to_chat_id.""" + + migrate_from_chat_id = build("MigrateFromChatId", lambda _, m: bool(m.migrate_from_chat_id)) + """Filter service messages that contain migrate_from_chat_id.""" + + pinned_message = build("PinnedMessage", lambda _, m: bool(m.pinned_message)) + """Filter service messages for pinned messages.""" + + service = build( + "Service", + lambda _, m: bool( + _.new_chat_members(m) + or _.left_chat_member(m) + or _.new_chat_title(m) + or _.new_chat_photo(m) + or _.delete_chat_photo(m) + or _.group_chat_created(m) + or _.supergroup_chat_created(m) + or _.channel_chat_created(m) + or _.migrate_to_chat_id(m) + or _.migrate_from_chat_id(m) + or _.pinned_m(m) + ) + ) + """Filter all service messages""" diff --git a/pyrogram/client/handler/message_handler.py b/pyrogram/client/handler/message_handler.py deleted file mode 100644 index fdbba245..00000000 --- a/pyrogram/client/handler/message_handler.py +++ /dev/null @@ -1,31 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 Dan Tès -# -# This file is part of Pyrogram. -# -# Pyrogram is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published -# by the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Pyrogram is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with Pyrogram. If not, see . - -from .handler import Handler - - -class MessageHandler(Handler): - def __init__(self, callback: callable, filters=None): - super().__init__(callback, filters) - - def check(self, message): - return ( - self.filters(message) - if self.filters - else True - ) diff --git a/pyrogram/client/handler/raw_update_handler.py b/pyrogram/client/handler/raw_update_handler.py deleted file mode 100644 index 758606db..00000000 --- a/pyrogram/client/handler/raw_update_handler.py +++ /dev/null @@ -1,24 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 Dan Tès -# -# This file is part of Pyrogram. -# -# Pyrogram is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published -# by the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Pyrogram is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with Pyrogram. If not, see . - -from .handler import Handler - - -class RawUpdateHandler(Handler): - def __init__(self, callback: callable): - super().__init__(callback) diff --git a/pyrogram/client/handler/__init__.py b/pyrogram/client/handlers/__init__.py similarity index 86% rename from pyrogram/client/handler/__init__.py rename to pyrogram/client/handlers/__init__.py index be0bfb56..d9c48359 100644 --- a/pyrogram/client/handler/__init__.py +++ b/pyrogram/client/handlers/__init__.py @@ -16,6 +16,4 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . -from .handler import Handler -from .message_handler import MessageHandler -from .raw_update_handler import RawUpdateHandler +from .handlers import MessageHandler, RawUpdateHandler diff --git a/pyrogram/client/handler/handler.py b/pyrogram/client/handlers/handler.py similarity index 100% rename from pyrogram/client/handler/handler.py rename to pyrogram/client/handlers/handler.py diff --git a/pyrogram/client/handlers/handlers.py b/pyrogram/client/handlers/handlers.py new file mode 100644 index 00000000..ca43282f --- /dev/null +++ b/pyrogram/client/handlers/handlers.py @@ -0,0 +1,93 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2018 Dan Tès +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from .handler import Handler + + +class MessageHandler(Handler): + """The Message handler class. It is used to handle text, media and service messages coming from + any chat (private, group, channel). + + Args: + callback (``callable``): + Pass a function that will be called when a new Message arrives. It takes *(client, message)* + as positional arguments (look at the section below for a detailed description). + + filters (:obj:`Filters `): + Pass one or more filters to allow only a subset of messages to be passed + in your callback function. + + Other parameters: + client (:obj:`Client `): + The Client itself, useful when you want to call other API methods inside the message handler. + + message (:obj:`Message `): + The received message. + """ + + def __init__(self, callback: callable, filters=None): + super().__init__(callback, filters) + + def check(self, message): + return ( + self.filters(message) + if self.filters + else True + ) + + +class RawUpdateHandler(Handler): + """The Raw Update handler class. It is used to handle raw updates. + + Args: + callback (``callable``): + A function that will be called when a new update is received from the server. It takes + *(client, update, users, chats)* as positional arguments (look at the section below for + a detailed description). + + Other Parameters: + client (:class:`Client `): + The Client itself, useful when you want to call other API methods inside the update handler. + + update (``Update``): + The received update, which can be one of the many single Updates listed in the *updates* + field you see in the :obj:`Update ` type. + + users (``dict``): + Dictionary of all :obj:`User ` mentioned in the update. + You can access extra info about the user (such as *first_name*, *last_name*, etc...) by using + the IDs you find in the *update* argument (e.g.: *users[1768841572]*). + + chats (``dict``): + Dictionary of all :obj:`Chat ` and + :obj:`Channel ` mentioned in the update. + You can access extra info about the chat (such as *title*, *participants_count*, etc...) + by using the IDs you find in the *update* argument (e.g.: *chats[1701277281]*). + + Note: + The following Empty or Forbidden types may exist inside the *users* and *chats* dictionaries. + They mean you have been blocked by the user or banned from the group/channel. + + - :obj:`UserEmpty ` + - :obj:`ChatEmpty ` + - :obj:`ChatForbidden ` + - :obj:`ChannelForbidden ` + """ + + def __init__(self, callback: callable): + super().__init__(callback)