mirror of
https://github.com/pyrogram/pyrogram
synced 2025-08-30 13:57:54 +00:00
Merge develop -> asyncio
This commit is contained in:
commit
a8dfe61f7e
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -4,7 +4,7 @@ about: Create a bug report affecting the library
|
|||||||
labels: "bug"
|
labels: "bug"
|
||||||
---
|
---
|
||||||
|
|
||||||
<!-- WARNING: Ignoring this template will lead to the issue being closed as incomplete -->
|
<!-- WARNING: Ignoring this template could lead to the issue being closed as incomplete -->
|
||||||
|
|
||||||
## Checklist
|
## Checklist
|
||||||
- [ ] I am sure the error is coming from Pyrogram's code and not elsewhere.
|
- [ ] I am sure the error is coming from Pyrogram's code and not elsewhere.
|
||||||
@ -15,7 +15,7 @@ labels: "bug"
|
|||||||
A clear and concise description of the problem.
|
A clear and concise description of the problem.
|
||||||
|
|
||||||
## Steps to Reproduce
|
## Steps to Reproduce
|
||||||
[A minimal, complete and verifiable example](https://stackoverflow.com/help/mcve).
|
[A minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example).
|
||||||
|
|
||||||
## Traceback
|
## Traceback
|
||||||
The full traceback (if applicable).
|
The full traceback (if applicable).
|
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -4,7 +4,7 @@ about: Suggest ideas, new features or enhancements
|
|||||||
labels: "enhancement"
|
labels: "enhancement"
|
||||||
---
|
---
|
||||||
|
|
||||||
<!-- WARNING: Ignoring this template will lead to the issue being closed as incomplete -->
|
<!-- WARNING: Ignoring this template could lead to the issue being closed as incomplete -->
|
||||||
|
|
||||||
## Checklist
|
## Checklist
|
||||||
- [ ] I believe the idea is awesome and would benefit the library.
|
- [ ] I believe the idea is awesome and would benefit the library.
|
||||||
|
6
.github/ISSUE_TEMPLATE/question.md
vendored
6
.github/ISSUE_TEMPLATE/question.md
vendored
@ -5,11 +5,11 @@ title: For Q&A purposes, please read this template body
|
|||||||
labels: "question"
|
labels: "question"
|
||||||
---
|
---
|
||||||
|
|
||||||
<!-- WARNING: Ignoring this template will lead to the issue being closed as incomplete -->
|
<!-- WARNING: Ignoring this template could lead to the issue being closed as incomplete -->
|
||||||
|
|
||||||
# Important
|
# Important
|
||||||
This place is for issues about Pyrogram, it's **not a forum**.
|
This place is for issues about Pyrogram, it's **not a forum**.
|
||||||
|
|
||||||
If you'd like to post a question, please move to https://stackoverflow.com or join the Telegram community by following the description in https://t.me/pyrogram.
|
If you'd like to post a question, please move to https://stackoverflow.com or join the Telegram community at https://t.me/pyrogram. Useful information on how to ask good questions can be found here: https://stackoverflow.com/help/how-to-ask.
|
||||||
|
|
||||||
Thanks.
|
Thanks.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
## Include
|
## Include
|
||||||
include README.md COPYING COPYING.lesser NOTICE requirements.txt
|
include README.md COPYING COPYING.lesser NOTICE requirements.txt
|
||||||
recursive-include compiler *.py *.tl *.tsv *.txt
|
recursive-include compiler *.py *.tl *.tsv *.txt
|
||||||
recursive-include pyrogram mime.types
|
recursive-include pyrogram mime.types schema.sql
|
||||||
|
|
||||||
## Exclude
|
## Exclude
|
||||||
prune pyrogram/api/errors/exceptions
|
prune pyrogram/api/errors/exceptions
|
||||||
|
@ -99,4 +99,10 @@ RESULT_ID_DUPLICATE The result contains items with duplicated identifiers
|
|||||||
ACCESS_TOKEN_INVALID The bot access token is invalid
|
ACCESS_TOKEN_INVALID The bot access token is invalid
|
||||||
INVITE_HASH_EXPIRED The chat invite link is no longer valid
|
INVITE_HASH_EXPIRED The chat invite link is no longer valid
|
||||||
USER_BANNED_IN_CHANNEL You are limited, check @SpamBot for details
|
USER_BANNED_IN_CHANNEL You are limited, check @SpamBot for details
|
||||||
MESSAGE_EDIT_TIME_EXPIRED You can no longer edit this message
|
MESSAGE_EDIT_TIME_EXPIRED You can no longer edit this message
|
||||||
|
FOLDER_ID_INVALID The folder id is invalid
|
||||||
|
MEGAGROUP_PREHISTORY_HIDDEN The action failed because the supergroup has the pre-history hidden
|
||||||
|
CHAT_LINK_EXISTS The action failed because the supergroup is linked to a channel
|
||||||
|
LINK_NOT_MODIFIED The chat link was not modified because you tried to link to the same target
|
||||||
|
BROADCAST_ID_INVALID The channel is invalid
|
||||||
|
MEGAGROUP_ID_INVALID The supergroup is invalid
|
|
@ -7,5 +7,6 @@ HISTORY_GET_FAILED Telegram is having internal problems. Please try again later
|
|||||||
REG_ID_GENERATE_FAILED Telegram is having internal problems. Please try again later
|
REG_ID_GENERATE_FAILED Telegram is having internal problems. Please try again later
|
||||||
RANDOM_ID_DUPLICATE Telegram is having internal problems. Please try again later
|
RANDOM_ID_DUPLICATE Telegram is having internal problems. Please try again later
|
||||||
WORKER_BUSY_TOO_LONG_RETRY Telegram is having internal problems. Please try again later
|
WORKER_BUSY_TOO_LONG_RETRY Telegram is having internal problems. Please try again later
|
||||||
INTERDC_X_CALL_ERROR Telegram is having internal problems. Please try again later
|
INTERDC_X_CALL_ERROR Telegram is having internal problems at DC{x}. Please try again later
|
||||||
INTERDC_X_CALL_RICH_ERROR Telegram is having internal problems. Please try again later
|
INTERDC_X_CALL_RICH_ERROR Telegram is having internal problems at DC{x}. Please try again later
|
||||||
|
FOLDER_DEAC_AUTOFIX_ALL Telegram is having internal problems. Please try again later
|
|
@ -35,8 +35,7 @@ backwards-incompatible changes made in that version.
|
|||||||
When upgrading to a new version of Pyrogram, you will need to check all the breaking changes in order to find
|
When upgrading to a new version of Pyrogram, you will need to check all the breaking changes in order to find
|
||||||
incompatible code in your application, but also to take advantage of new features and improvements.
|
incompatible code in your application, but also to take advantage of new features and improvements.
|
||||||
|
|
||||||
Releases
|
**Contents**
|
||||||
--------
|
|
||||||
|
|
||||||
""".lstrip("\n")
|
""".lstrip("\n")
|
||||||
|
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
User-agent: *
|
User-agent: *
|
||||||
|
|
||||||
Allow: /
|
Allow: /
|
||||||
|
|
||||||
|
Disallow: /dev/*
|
||||||
|
Disallow: /old/*
|
||||||
|
|
||||||
Sitemap: https://docs.pyrogram.org/sitemap.xml
|
Sitemap: https://docs.pyrogram.org/sitemap.xml
|
@ -34,13 +34,13 @@ Message
|
|||||||
- :meth:`~Message.click`
|
- :meth:`~Message.click`
|
||||||
- :meth:`~Message.delete`
|
- :meth:`~Message.delete`
|
||||||
- :meth:`~Message.download`
|
- :meth:`~Message.download`
|
||||||
- :meth:`~Message.edit`
|
- :meth:`~Message.forward`
|
||||||
|
- :meth:`~Message.pin`
|
||||||
|
- :meth:`~Message.edit_text`
|
||||||
- :meth:`~Message.edit_caption`
|
- :meth:`~Message.edit_caption`
|
||||||
- :meth:`~Message.edit_media`
|
- :meth:`~Message.edit_media`
|
||||||
- :meth:`~Message.edit_reply_markup`
|
- :meth:`~Message.edit_reply_markup`
|
||||||
- :meth:`~Message.forward`
|
- :meth:`~Message.reply_text`
|
||||||
- :meth:`~Message.pin`
|
|
||||||
- :meth:`~Message.reply`
|
|
||||||
- :meth:`~Message.reply_animation`
|
- :meth:`~Message.reply_animation`
|
||||||
- :meth:`~Message.reply_audio`
|
- :meth:`~Message.reply_audio`
|
||||||
- :meth:`~Message.reply_cached_media`
|
- :meth:`~Message.reply_cached_media`
|
||||||
@ -59,13 +59,35 @@ Message
|
|||||||
- :meth:`~Message.reply_video_note`
|
- :meth:`~Message.reply_video_note`
|
||||||
- :meth:`~Message.reply_voice`
|
- :meth:`~Message.reply_voice`
|
||||||
|
|
||||||
CallbackQuery
|
Chat
|
||||||
^^^^^^^^^^^^^
|
^^^^
|
||||||
|
|
||||||
.. hlist::
|
.. hlist::
|
||||||
:columns: 2
|
:columns: 2
|
||||||
|
|
||||||
|
- :meth:`~Chat.archive`
|
||||||
|
- :meth:`~Chat.unarchive`
|
||||||
|
|
||||||
|
User
|
||||||
|
^^^^
|
||||||
|
|
||||||
|
.. hlist::
|
||||||
|
:columns: 2
|
||||||
|
|
||||||
|
- :meth:`~User.archive`
|
||||||
|
- :meth:`~User.unarchive`
|
||||||
|
|
||||||
|
CallbackQuery
|
||||||
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. hlist::
|
||||||
|
:columns: 4
|
||||||
|
|
||||||
- :meth:`~CallbackQuery.answer`
|
- :meth:`~CallbackQuery.answer`
|
||||||
|
- :meth:`~CallbackQuery.edit_text`
|
||||||
|
- :meth:`~CallbackQuery.edit_caption`
|
||||||
|
- :meth:`~CallbackQuery.edit_media`
|
||||||
|
- :meth:`~CallbackQuery.edit_reply_markup`
|
||||||
|
|
||||||
InlineQuery
|
InlineQuery
|
||||||
^^^^^^^^^^^
|
^^^^^^^^^^^
|
||||||
@ -84,13 +106,13 @@ Details
|
|||||||
.. automethod:: Message.click()
|
.. automethod:: Message.click()
|
||||||
.. automethod:: Message.delete()
|
.. automethod:: Message.delete()
|
||||||
.. automethod:: Message.download()
|
.. automethod:: Message.download()
|
||||||
.. automethod:: Message.edit()
|
.. automethod:: Message.forward()
|
||||||
|
.. automethod:: Message.pin()
|
||||||
|
.. automethod:: Message.edit_text()
|
||||||
.. automethod:: Message.edit_caption()
|
.. automethod:: Message.edit_caption()
|
||||||
.. automethod:: Message.edit_media()
|
.. automethod:: Message.edit_media()
|
||||||
.. automethod:: Message.edit_reply_markup()
|
.. automethod:: Message.edit_reply_markup()
|
||||||
.. automethod:: Message.forward()
|
.. automethod:: Message.reply_text()
|
||||||
.. automethod:: Message.pin()
|
|
||||||
.. automethod:: Message.reply()
|
|
||||||
.. automethod:: Message.reply_animation()
|
.. automethod:: Message.reply_animation()
|
||||||
.. automethod:: Message.reply_audio()
|
.. automethod:: Message.reply_audio()
|
||||||
.. automethod:: Message.reply_cached_media()
|
.. automethod:: Message.reply_cached_media()
|
||||||
@ -109,8 +131,20 @@ Details
|
|||||||
.. automethod:: Message.reply_video_note()
|
.. automethod:: Message.reply_video_note()
|
||||||
.. automethod:: Message.reply_voice()
|
.. automethod:: Message.reply_voice()
|
||||||
|
|
||||||
|
.. Chat
|
||||||
|
.. automethod:: Chat.archive()
|
||||||
|
.. automethod:: Chat.unarchive()
|
||||||
|
|
||||||
|
.. User
|
||||||
|
.. automethod:: User.archive()
|
||||||
|
.. automethod:: User.unarchive()
|
||||||
|
|
||||||
.. CallbackQuery
|
.. CallbackQuery
|
||||||
.. automethod:: CallbackQuery.answer()
|
.. automethod:: CallbackQuery.answer()
|
||||||
|
.. automethod:: CallbackQuery.edit_text()
|
||||||
|
.. automethod:: CallbackQuery.edit_caption()
|
||||||
|
.. automethod:: CallbackQuery.edit_media()
|
||||||
|
.. automethod:: CallbackQuery.edit_reply_markup()
|
||||||
|
|
||||||
.. InlineQuery
|
.. InlineQuery
|
||||||
.. automethod:: InlineQuery.answer()
|
.. automethod:: InlineQuery.answer()
|
||||||
|
@ -13,4 +13,7 @@ This is the Client class. It exposes high-level methods for an easy access to th
|
|||||||
with app:
|
with app:
|
||||||
app.send_message("me", "Hi!")
|
app.send_message("me", "Hi!")
|
||||||
|
|
||||||
|
Details
|
||||||
|
-------
|
||||||
|
|
||||||
.. autoclass:: pyrogram.Client()
|
.. autoclass:: pyrogram.Client()
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
Update Filters
|
Update Filters
|
||||||
==============
|
==============
|
||||||
|
|
||||||
|
Details
|
||||||
|
-------
|
||||||
|
|
||||||
.. autoclass:: pyrogram.Filters
|
.. autoclass:: pyrogram.Filters
|
||||||
:members:
|
:members:
|
||||||
|
@ -31,10 +31,8 @@ Utilities
|
|||||||
- :meth:`~Client.run`
|
- :meth:`~Client.run`
|
||||||
- :meth:`~Client.add_handler`
|
- :meth:`~Client.add_handler`
|
||||||
- :meth:`~Client.remove_handler`
|
- :meth:`~Client.remove_handler`
|
||||||
- :meth:`~Client.send`
|
|
||||||
- :meth:`~Client.resolve_peer`
|
|
||||||
- :meth:`~Client.save_file`
|
|
||||||
- :meth:`~Client.stop_transmission`
|
- :meth:`~Client.stop_transmission`
|
||||||
|
- :meth:`~Client.export_session_string`
|
||||||
|
|
||||||
Messages
|
Messages
|
||||||
^^^^^^^^
|
^^^^^^^^
|
||||||
@ -58,11 +56,15 @@ Messages
|
|||||||
- :meth:`~Client.send_venue`
|
- :meth:`~Client.send_venue`
|
||||||
- :meth:`~Client.send_contact`
|
- :meth:`~Client.send_contact`
|
||||||
- :meth:`~Client.send_cached_media`
|
- :meth:`~Client.send_cached_media`
|
||||||
- :meth:`~Client.send_chat_action`
|
|
||||||
- :meth:`~Client.edit_message_text`
|
- :meth:`~Client.edit_message_text`
|
||||||
- :meth:`~Client.edit_message_caption`
|
- :meth:`~Client.edit_message_caption`
|
||||||
- :meth:`~Client.edit_message_reply_markup`
|
|
||||||
- :meth:`~Client.edit_message_media`
|
- :meth:`~Client.edit_message_media`
|
||||||
|
- :meth:`~Client.edit_message_reply_markup`
|
||||||
|
- :meth:`~Client.edit_inline_text`
|
||||||
|
- :meth:`~Client.edit_inline_caption`
|
||||||
|
- :meth:`~Client.edit_inline_media`
|
||||||
|
- :meth:`~Client.edit_inline_reply_markup`
|
||||||
|
- :meth:`~Client.send_chat_action`
|
||||||
- :meth:`~Client.delete_messages`
|
- :meth:`~Client.delete_messages`
|
||||||
- :meth:`~Client.get_messages`
|
- :meth:`~Client.get_messages`
|
||||||
- :meth:`~Client.get_history`
|
- :meth:`~Client.get_history`
|
||||||
@ -104,6 +106,8 @@ Chats
|
|||||||
- :meth:`~Client.get_dialogs_count`
|
- :meth:`~Client.get_dialogs_count`
|
||||||
- :meth:`~Client.restrict_chat`
|
- :meth:`~Client.restrict_chat`
|
||||||
- :meth:`~Client.update_chat_username`
|
- :meth:`~Client.update_chat_username`
|
||||||
|
- :meth:`~Client.archive_chats`
|
||||||
|
- :meth:`~Client.unarchive_chats`
|
||||||
|
|
||||||
Users
|
Users
|
||||||
^^^^^
|
^^^^^
|
||||||
@ -157,6 +161,18 @@ Bots
|
|||||||
- :meth:`~Client.set_game_score`
|
- :meth:`~Client.set_game_score`
|
||||||
- :meth:`~Client.get_game_high_scores`
|
- :meth:`~Client.get_game_high_scores`
|
||||||
|
|
||||||
|
Advanced Usage (Raw API)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Learn more about these methods at :doc:`Advanced Usage <../topics/advanced-usage>`.
|
||||||
|
|
||||||
|
.. hlist::
|
||||||
|
:columns: 4
|
||||||
|
|
||||||
|
- :meth:`~Client.send`
|
||||||
|
- :meth:`~Client.resolve_peer`
|
||||||
|
- :meth:`~Client.save_file`
|
||||||
|
|
||||||
-----
|
-----
|
||||||
|
|
||||||
Details
|
Details
|
||||||
@ -170,10 +186,8 @@ Details
|
|||||||
.. automethod:: Client.run()
|
.. automethod:: Client.run()
|
||||||
.. automethod:: Client.add_handler()
|
.. automethod:: Client.add_handler()
|
||||||
.. automethod:: Client.remove_handler()
|
.. automethod:: Client.remove_handler()
|
||||||
.. automethod:: Client.send()
|
|
||||||
.. automethod:: Client.resolve_peer()
|
|
||||||
.. automethod:: Client.save_file()
|
|
||||||
.. automethod:: Client.stop_transmission()
|
.. automethod:: Client.stop_transmission()
|
||||||
|
.. automethod:: Client.export_session_string()
|
||||||
|
|
||||||
.. Messages
|
.. Messages
|
||||||
.. automethod:: Client.send_message()
|
.. automethod:: Client.send_message()
|
||||||
@ -195,8 +209,12 @@ Details
|
|||||||
.. automethod:: Client.send_chat_action()
|
.. automethod:: Client.send_chat_action()
|
||||||
.. automethod:: Client.edit_message_text()
|
.. automethod:: Client.edit_message_text()
|
||||||
.. automethod:: Client.edit_message_caption()
|
.. automethod:: Client.edit_message_caption()
|
||||||
.. automethod:: Client.edit_message_reply_markup()
|
|
||||||
.. automethod:: Client.edit_message_media()
|
.. automethod:: Client.edit_message_media()
|
||||||
|
.. automethod:: Client.edit_message_reply_markup()
|
||||||
|
.. automethod:: Client.edit_inline_text()
|
||||||
|
.. automethod:: Client.edit_inline_caption()
|
||||||
|
.. automethod:: Client.edit_inline_media()
|
||||||
|
.. automethod:: Client.edit_inline_reply_markup()
|
||||||
.. automethod:: Client.delete_messages()
|
.. automethod:: Client.delete_messages()
|
||||||
.. automethod:: Client.get_messages()
|
.. automethod:: Client.get_messages()
|
||||||
.. automethod:: Client.get_history()
|
.. automethod:: Client.get_history()
|
||||||
@ -233,6 +251,8 @@ Details
|
|||||||
.. automethod:: Client.get_dialogs_count()
|
.. automethod:: Client.get_dialogs_count()
|
||||||
.. automethod:: Client.restrict_chat()
|
.. automethod:: Client.restrict_chat()
|
||||||
.. automethod:: Client.update_chat_username()
|
.. automethod:: Client.update_chat_username()
|
||||||
|
.. automethod:: Client.archive_chats()
|
||||||
|
.. automethod:: Client.unarchive_chats()
|
||||||
|
|
||||||
.. Users
|
.. Users
|
||||||
.. automethod:: Client.get_me()
|
.. automethod:: Client.get_me()
|
||||||
@ -265,3 +285,8 @@ Details
|
|||||||
.. automethod:: Client.send_game()
|
.. automethod:: Client.send_game()
|
||||||
.. automethod:: Client.set_game_score()
|
.. automethod:: Client.set_game_score()
|
||||||
.. automethod:: Client.get_game_high_scores()
|
.. automethod:: Client.get_game_high_scores()
|
||||||
|
|
||||||
|
.. Advanced Usage
|
||||||
|
.. automethod:: Client.send()
|
||||||
|
.. automethod:: Client.resolve_peer()
|
||||||
|
.. automethod:: Client.save_file()
|
@ -30,10 +30,8 @@ Users & Chats
|
|||||||
- :class:`ChatPreview`
|
- :class:`ChatPreview`
|
||||||
- :class:`ChatPhoto`
|
- :class:`ChatPhoto`
|
||||||
- :class:`ChatMember`
|
- :class:`ChatMember`
|
||||||
- :class:`ChatMembers`
|
|
||||||
- :class:`ChatPermissions`
|
- :class:`ChatPermissions`
|
||||||
- :class:`Dialog`
|
- :class:`Dialog`
|
||||||
- :class:`Dialogs`
|
|
||||||
|
|
||||||
Messages & Media
|
Messages & Media
|
||||||
^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^
|
||||||
@ -42,10 +40,8 @@ Messages & Media
|
|||||||
:columns: 5
|
:columns: 5
|
||||||
|
|
||||||
- :class:`Message`
|
- :class:`Message`
|
||||||
- :class:`Messages`
|
|
||||||
- :class:`MessageEntity`
|
- :class:`MessageEntity`
|
||||||
- :class:`Photo`
|
- :class:`Photo`
|
||||||
- :class:`ProfilePhotos`
|
|
||||||
- :class:`Thumbnail`
|
- :class:`Thumbnail`
|
||||||
- :class:`Audio`
|
- :class:`Audio`
|
||||||
- :class:`Document`
|
- :class:`Document`
|
||||||
@ -61,8 +57,8 @@ Messages & Media
|
|||||||
- :class:`Poll`
|
- :class:`Poll`
|
||||||
- :class:`PollOption`
|
- :class:`PollOption`
|
||||||
|
|
||||||
Keyboards
|
Bots & Keyboards
|
||||||
^^^^^^^^^
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
.. hlist::
|
.. hlist::
|
||||||
:columns: 4
|
:columns: 4
|
||||||
@ -75,7 +71,6 @@ Keyboards
|
|||||||
- :class:`ForceReply`
|
- :class:`ForceReply`
|
||||||
- :class:`CallbackQuery`
|
- :class:`CallbackQuery`
|
||||||
- :class:`GameHighScore`
|
- :class:`GameHighScore`
|
||||||
- :class:`GameHighScores`
|
|
||||||
- :class:`CallbackGame`
|
- :class:`CallbackGame`
|
||||||
|
|
||||||
Input Media
|
Input Media
|
||||||
@ -123,17 +118,13 @@ Details
|
|||||||
.. autoclass:: ChatPreview()
|
.. autoclass:: ChatPreview()
|
||||||
.. autoclass:: ChatPhoto()
|
.. autoclass:: ChatPhoto()
|
||||||
.. autoclass:: ChatMember()
|
.. autoclass:: ChatMember()
|
||||||
.. autoclass:: ChatMembers()
|
|
||||||
.. autoclass:: ChatPermissions()
|
.. autoclass:: ChatPermissions()
|
||||||
.. autoclass:: Dialog()
|
.. autoclass:: Dialog()
|
||||||
.. autoclass:: Dialogs()
|
|
||||||
|
|
||||||
.. Messages & Media
|
.. Messages & Media
|
||||||
.. autoclass:: Message()
|
.. autoclass:: Message()
|
||||||
.. autoclass:: Messages()
|
|
||||||
.. autoclass:: MessageEntity()
|
.. autoclass:: MessageEntity()
|
||||||
.. autoclass:: Photo()
|
.. autoclass:: Photo()
|
||||||
.. autoclass:: ProfilePhotos()
|
|
||||||
.. autoclass:: Thumbnail()
|
.. autoclass:: Thumbnail()
|
||||||
.. autoclass:: Audio()
|
.. autoclass:: Audio()
|
||||||
.. autoclass:: Document()
|
.. autoclass:: Document()
|
||||||
@ -149,7 +140,7 @@ Details
|
|||||||
.. autoclass:: Poll()
|
.. autoclass:: Poll()
|
||||||
.. autoclass:: PollOption()
|
.. autoclass:: PollOption()
|
||||||
|
|
||||||
.. Keyboards
|
.. Bots & Keyboards
|
||||||
.. autoclass:: ReplyKeyboardMarkup()
|
.. autoclass:: ReplyKeyboardMarkup()
|
||||||
.. autoclass:: KeyboardButton()
|
.. autoclass:: KeyboardButton()
|
||||||
.. autoclass:: ReplyKeyboardRemove()
|
.. autoclass:: ReplyKeyboardRemove()
|
||||||
@ -158,7 +149,6 @@ Details
|
|||||||
.. autoclass:: ForceReply()
|
.. autoclass:: ForceReply()
|
||||||
.. autoclass:: CallbackQuery()
|
.. autoclass:: CallbackQuery()
|
||||||
.. autoclass:: GameHighScore()
|
.. autoclass:: GameHighScore()
|
||||||
.. autoclass:: GameHighScores()
|
|
||||||
.. autoclass:: CallbackGame()
|
.. autoclass:: CallbackGame()
|
||||||
|
|
||||||
.. Input Media
|
.. Input Media
|
||||||
|
@ -53,6 +53,22 @@ Why Pyrogram?
|
|||||||
|
|
||||||
.. _TgCrypto: https://github.com/pyrogram/tgcrypto
|
.. _TgCrypto: https://github.com/pyrogram/tgcrypto
|
||||||
|
|
||||||
|
How stable and reliable is Pyrogram?
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
So far, since its first public release, Pyrogram has always shown itself to be quite reliable in handling client-server
|
||||||
|
interconnections and just as stable when keeping long running applications online. The only annoying issues faced are
|
||||||
|
actually coming from Telegram servers internal errors and down times, from which Pyrogram is able to recover itself
|
||||||
|
automatically.
|
||||||
|
|
||||||
|
To challenge the framework, the creator is constantly keeping a public
|
||||||
|
`welcome bot <https://github.com/pyrogram/pyrogram/blob/develop/examples/welcomebot.py>`_ online 24/7 on his own,
|
||||||
|
relatively-busy account for well over a year now.
|
||||||
|
|
||||||
|
In addition to that, about six months ago, one of the most popular Telegram bot has been rewritten
|
||||||
|
:doc:`using Pyrogram <powered-by>` and is serving more than 200,000 Monthly Active Users since
|
||||||
|
then, uninterruptedly and without any need for restarting it.
|
||||||
|
|
||||||
What can MTProto do more than the Bot API?
|
What can MTProto do more than the Bot API?
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
|
|
||||||
@ -134,20 +150,60 @@ in a bunch of seconds:
|
|||||||
import logging
|
import logging
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
Another way to confirm you aren't able to connect to Telegram is by pinging these IP addresses and see whether ping
|
Another way to confirm you aren't able to connect to Telegram is by pinging the IP addresses below and see whether ping
|
||||||
fails or not:
|
fails or not.
|
||||||
|
|
||||||
- DC1: ``149.154.175.50``
|
What are the IP addresses of Telegram Data Centers?
|
||||||
- DC2: ``149.154.167.51``
|
---------------------------------------------------
|
||||||
- DC3: ``149.154.175.100``
|
|
||||||
- DC4: ``149.154.167.91``
|
The Telegram cloud is currently composed by a decentralized, multi-DC infrastructure (each of which can work
|
||||||
- DC5: ``91.108.56.149``
|
independently) spread in 5 different locations. However, some of the less busy DCs have been lately dismissed and their
|
||||||
|
IP addresses are now kept as aliases.
|
||||||
|
|
||||||
|
.. csv-table:: Production Environment
|
||||||
|
:header: ID, Location, IPv4, IPv6
|
||||||
|
:widths: auto
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
DC1, "MIA, Miami FL, USA", ``149.154.175.50``, ``2001:b28:f23d:f001::a``
|
||||||
|
DC2, "AMS, Amsterdam, NL", ``149.154.167.51``, ``2001:67c:4e8:f002::a``
|
||||||
|
DC3*, "MIA, Miami FL, USA", ``149.154.175.100``, ``2001:b28:f23d:f003::a``
|
||||||
|
DC4, "AMS, Amsterdam, NL", ``149.154.167.91``, ``2001:67c:4e8:f004::a``
|
||||||
|
DC5, "SIN, Singapore, SG", ``91.108.56.149``, ``2001:b28:f23f:f005::a``
|
||||||
|
|
||||||
|
.. csv-table:: Test Environment
|
||||||
|
:header: ID, Location, IPv4, IPv6
|
||||||
|
:widths: auto
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
DC1, "MIA, Miami FL, USA", ``149.154.175.10``, ``2001:b28:f23d:f001::e``
|
||||||
|
DC2, "AMS, Amsterdam, NL", ``149.154.167.40``, ``2001:67c:4e8:f002::e``
|
||||||
|
DC3*, "MIA, Miami FL, USA", ``149.154.175.117``, ``2001:b28:f23d:f003::e``
|
||||||
|
|
||||||
|
***** Alias DC
|
||||||
|
|
||||||
|
More info about the Test Environment can be found :doc:`here <topics/test-servers>`.
|
||||||
|
|
||||||
|
I want to migrate my account from DCX to DCY.
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
This question is often asked by people who find their account(s) always being connected to DC1 - USA (for example), but
|
||||||
|
are connecting from a place far away (e.g DC4 - Europe), thus resulting in slower interactions when using the API
|
||||||
|
because of the great physical distance between the user and its associated DC.
|
||||||
|
|
||||||
|
When registering an account for the first time, is up to Telegram to decide which DC the new user is going to be created
|
||||||
|
in, based on the phone number origin.
|
||||||
|
|
||||||
|
Even though Telegram `documentations <https://core.telegram.org/api/datacenter#user-migration>`_ state the server might
|
||||||
|
decide to automatically migrate a user in case of prolonged usages from a distant, unusual location and albeit this
|
||||||
|
mechanism is also `confirmed <https://twitter.com/telegram/status/427131446655197184>`_ to exist by Telegram itself,
|
||||||
|
it's currently not possible to have your account migrated, in any way, simply because the feature was once planned but
|
||||||
|
not yet implemented.
|
||||||
|
|
||||||
I keep getting PEER_ID_INVALID error!
|
I keep getting PEER_ID_INVALID error!
|
||||||
-------------------------------------------
|
-------------------------------------
|
||||||
|
|
||||||
The error in question is ``[400 PEER_ID_INVALID]``, and could mean several
|
The error in question is ``[400 PEER_ID_INVALID]``, and could mean several things:
|
||||||
things:
|
|
||||||
|
|
||||||
- The chat id you tried to use is simply wrong, double check it.
|
- The chat id you tried to use is simply wrong, double check it.
|
||||||
- The chat id refers to a group or channel you are not a member of.
|
- The chat id refers to a group or channel you are not a member of.
|
||||||
|
@ -58,7 +58,7 @@ Terms
|
|||||||
Pyrogram --- to automate some behaviours, like sending messages or reacting to text commands or any other event.
|
Pyrogram --- to automate some behaviours, like sending messages or reacting to text commands or any other event.
|
||||||
|
|
||||||
Session
|
Session
|
||||||
Also known as *login session*, is a strictly personal piece of information created and held by both parties
|
Also known as *login session*, is a strictly personal piece of data created and held by both parties
|
||||||
(client and server) which is used to grant permission into a single account without having to start a new
|
(client and server) which is used to grant permission into a single account without having to start a new
|
||||||
authorization process from scratch.
|
authorization process from scratch.
|
||||||
|
|
||||||
|
@ -130,6 +130,7 @@ Meta
|
|||||||
topics/auto-auth
|
topics/auto-auth
|
||||||
topics/session-settings
|
topics/session-settings
|
||||||
topics/tgcrypto
|
topics/tgcrypto
|
||||||
|
topics/storage-engines
|
||||||
topics/text-formatting
|
topics/text-formatting
|
||||||
topics/serialize
|
topics/serialize
|
||||||
topics/proxy
|
topics/proxy
|
||||||
|
95
docs/source/topics/storage-engines.rst
Normal file
95
docs/source/topics/storage-engines.rst
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
Storage Engines
|
||||||
|
===============
|
||||||
|
|
||||||
|
Every time you login to Telegram, some personal piece of data are created and held by both parties (the client, Pyrogram
|
||||||
|
and the server, Telegram). This session data is uniquely bound to your own account, indefinitely (until you logout or
|
||||||
|
decide to manually terminate it) and is used to authorize a client to execute API calls on behalf of your identity.
|
||||||
|
|
||||||
|
Persisting Sessions
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
In order to make a client reconnect successfully between restarts, that is, without having to start a new
|
||||||
|
authorization process from scratch each time, Pyrogram needs to store the generated session data somewhere.
|
||||||
|
|
||||||
|
Other useful data being stored is peers' cache. In short, peers are all those entities you can chat with, such as users
|
||||||
|
or bots, basic groups, but also channels and supergroups. Because of how Telegram works, a unique pair of **id** and
|
||||||
|
**access_hash** is needed to contact a peer. This, plus other useful info such as the peer type, is what is stored
|
||||||
|
inside a session storage.
|
||||||
|
|
||||||
|
So, if you ever wondered how is Pyrogram able to contact peers just by asking for their ids, it's because of this very
|
||||||
|
reason: the peer *id* is looked up in the internal database and the available *access_hash* is retrieved, which is then
|
||||||
|
used to correctly invoke API methods.
|
||||||
|
|
||||||
|
Different Storage Engines
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Let's now talk about how Pyrogram actually stores all the relevant data. Pyrogram offers two different types of storage
|
||||||
|
engines: a **File Storage** and a **Memory Storage**. These engines are well integrated in the library and require a
|
||||||
|
minimal effort to set up. Here's how they work:
|
||||||
|
|
||||||
|
File Storage
|
||||||
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
|
This is the most common storage engine. It is implemented by using **SQLite**, which will store the session and peers
|
||||||
|
details. The database will be saved to disk as a single portable file and is designed to efficiently save and retrieve
|
||||||
|
peers whenever they are needed.
|
||||||
|
|
||||||
|
To use this type of engine, simply pass any name of your choice to the ``session_name`` parameter of the
|
||||||
|
:obj:`~pyrogram.Client` constructor, as usual:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from pyrogram import Client
|
||||||
|
|
||||||
|
with Client("my_account") as app:
|
||||||
|
print(app.get_me())
|
||||||
|
|
||||||
|
Once you successfully log in (either with a user or a bot identity), a session file will be created and saved to disk as
|
||||||
|
``my_account.session``. Any subsequent client restart will make Pyrogram search for a file named that way and the
|
||||||
|
session database will be automatically loaded.
|
||||||
|
|
||||||
|
Memory Storage
|
||||||
|
^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
In case you don't want to have any session file saved on disk, you can use an in-memory storage by passing the special
|
||||||
|
session name "**:memory:**" to the ``session_name`` parameter of the :obj:`~pyrogram.Client` constructor:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from pyrogram import Client
|
||||||
|
|
||||||
|
with Client(":memory:") as app:
|
||||||
|
print(app.get_me())
|
||||||
|
|
||||||
|
This database is still backed by SQLite, but exists purely in memory. However, once you stop a client, the entire
|
||||||
|
database is discarded and the session details used for logging in again will be lost forever.
|
||||||
|
|
||||||
|
Session Strings
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Session strings are useful when you want to run authorized Pyrogram clients on platforms like
|
||||||
|
`Heroku <https://www.heroku.com/>`_, where their ephemeral filesystems makes it much harder for a file-based storage
|
||||||
|
engine to properly work as intended.
|
||||||
|
|
||||||
|
In case you want to use an in-memory storage, but also want to keep access to the session you created, call
|
||||||
|
:meth:`~pyrogram.Client.export_session_string` anytime before stopping the client...
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from pyrogram import Client
|
||||||
|
|
||||||
|
with Client(":memory:") as app:
|
||||||
|
print(app.export_session_string())
|
||||||
|
|
||||||
|
...and save the resulting string somewhere. You can use this string as session name the next time you want to login
|
||||||
|
using the same session; the storage used will still be completely in-memory:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from pyrogram import Client
|
||||||
|
|
||||||
|
session_string = "...ZnUIFD8jsjXTb8g_vpxx48k1zkov9sapD-tzjz-S4WZv70M..."
|
||||||
|
|
||||||
|
with Client(session_string) as app:
|
||||||
|
print(app.get_me())
|
||||||
|
|
@ -24,7 +24,7 @@ if sys.version_info[:3] in [(3, 5, 0), (3, 5, 1), (3, 5, 2)]:
|
|||||||
# Monkey patch the standard "typing" module because Python versions from 3.5.0 to 3.5.2 have a broken one.
|
# Monkey patch the standard "typing" module because Python versions from 3.5.0 to 3.5.2 have a broken one.
|
||||||
sys.modules["typing"] = typing
|
sys.modules["typing"] = typing
|
||||||
|
|
||||||
__version__ = "0.14.1-asyncio"
|
__version__ = "0.15.0-asyncio"
|
||||||
__license__ = "GNU Lesser General Public License v3 or later (LGPLv3+)"
|
__license__ = "GNU Lesser General Public License v3 or later (LGPLv3+)"
|
||||||
__copyright__ = "Copyright (C) 2017-2019 Dan <https://github.com/delivrance>"
|
__copyright__ = "Copyright (C) 2017-2019 Dan <https://github.com/delivrance>"
|
||||||
|
|
||||||
|
@ -29,7 +29,6 @@ import shutil
|
|||||||
import tempfile
|
import tempfile
|
||||||
import time
|
import time
|
||||||
from configparser import ConfigParser
|
from configparser import ConfigParser
|
||||||
from datetime import datetime
|
|
||||||
from hashlib import sha256, md5
|
from hashlib import sha256, md5
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@ -54,6 +53,7 @@ from pyrogram.session import Auth, Session
|
|||||||
from .ext import utils, Syncer, BaseClient, Dispatcher
|
from .ext import utils, Syncer, BaseClient, Dispatcher
|
||||||
from .ext.utils import ainput
|
from .ext.utils import ainput
|
||||||
from .methods import Methods
|
from .methods import Methods
|
||||||
|
from .storage import Storage, FileStorage, MemoryStorage
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -63,8 +63,13 @@ class Client(Methods, BaseClient):
|
|||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
session_name (``str``):
|
session_name (``str``):
|
||||||
Name to uniquely identify a session of either a User or a Bot, e.g.: "my_account". This name will be used
|
Pass a string of your choice to give a name to the client session, e.g.: "*my_account*". This name will be
|
||||||
to save a file to disk that stores details needed for reconnecting without asking again for credentials.
|
used to save a file on disk that stores details needed to reconnect without asking again for credentials.
|
||||||
|
Alternatively, if you don't want a file to be saved on disk, pass the special name "**:memory:**" to start
|
||||||
|
an in-memory session that will be discarded as soon as you stop the Client. In order to reconnect again
|
||||||
|
using a memory storage without having to login again, you can use
|
||||||
|
:meth:`~pyrogram.Client.export_session_string` before stopping the client to get a session string you can
|
||||||
|
pass here as argument.
|
||||||
|
|
||||||
api_id (``int``, *optional*):
|
api_id (``int``, *optional*):
|
||||||
The *api_id* part of your Telegram API Key, as integer. E.g.: 12345
|
The *api_id* part of your Telegram API Key, as integer. E.g.: 12345
|
||||||
@ -178,7 +183,7 @@ class Client(Methods, BaseClient):
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
session_name: str,
|
session_name: Union[str, Storage],
|
||||||
api_id: Union[int, str] = None,
|
api_id: Union[int, str] = None,
|
||||||
api_hash: str = None,
|
api_hash: str = None,
|
||||||
app_version: str = None,
|
app_version: str = None,
|
||||||
@ -225,12 +230,23 @@ class Client(Methods, BaseClient):
|
|||||||
self.first_name = first_name
|
self.first_name = first_name
|
||||||
self.last_name = last_name
|
self.last_name = last_name
|
||||||
self.workers = workers
|
self.workers = workers
|
||||||
self.workdir = workdir
|
self.workdir = Path(workdir)
|
||||||
self.config_file = config_file
|
self.config_file = Path(config_file)
|
||||||
self.plugins = plugins
|
self.plugins = plugins
|
||||||
self.no_updates = no_updates
|
self.no_updates = no_updates
|
||||||
self.takeout = takeout
|
self.takeout = takeout
|
||||||
|
|
||||||
|
if isinstance(session_name, str):
|
||||||
|
if session_name == ":memory:" or len(session_name) >= MemoryStorage.SESSION_STRING_SIZE:
|
||||||
|
session_name = re.sub(r"[\n\s]+", "", session_name)
|
||||||
|
self.storage = MemoryStorage(session_name)
|
||||||
|
else:
|
||||||
|
self.storage = FileStorage(session_name, self.workdir)
|
||||||
|
elif isinstance(session_name, Storage):
|
||||||
|
self.storage = session_name
|
||||||
|
else:
|
||||||
|
raise ValueError("Unknown storage engine")
|
||||||
|
|
||||||
self.dispatcher = Dispatcher(self, workers)
|
self.dispatcher = Dispatcher(self, workers)
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
@ -271,50 +287,32 @@ class Client(Methods, BaseClient):
|
|||||||
if self.is_started:
|
if self.is_started:
|
||||||
raise ConnectionError("Client has already been started")
|
raise ConnectionError("Client has already been started")
|
||||||
|
|
||||||
if self.BOT_TOKEN_RE.match(self.session_name):
|
|
||||||
self.is_bot = True
|
|
||||||
self.bot_token = self.session_name
|
|
||||||
self.session_name = self.session_name.split(":")[0]
|
|
||||||
log.warning('\nWARNING: You are using a bot token as session name!\n'
|
|
||||||
'This usage will be deprecated soon. Please use a session file name to load '
|
|
||||||
'an existing session and the bot_token argument to create new sessions.\n'
|
|
||||||
'More info: https://docs.pyrogram.org/intro/auth#bot-authorization\n')
|
|
||||||
|
|
||||||
self.load_config()
|
self.load_config()
|
||||||
await self.load_session()
|
await self.load_session()
|
||||||
self.load_plugins()
|
self.load_plugins()
|
||||||
|
|
||||||
self.session = Session(
|
self.session = Session(self, self.storage.dc_id, self.storage.auth_key)
|
||||||
self,
|
|
||||||
self.dc_id,
|
|
||||||
self.auth_key
|
|
||||||
)
|
|
||||||
|
|
||||||
await self.session.start()
|
await self.session.start()
|
||||||
self.is_started = True
|
self.is_started = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if self.user_id is None:
|
if self.storage.user_id is None:
|
||||||
if self.bot_token is None:
|
if self.bot_token is None:
|
||||||
self.is_bot = False
|
self.storage.is_bot = False
|
||||||
await self.authorize_user()
|
await self.authorize_user()
|
||||||
else:
|
else:
|
||||||
self.is_bot = True
|
self.storage.is_bot = True
|
||||||
await self.authorize_bot()
|
await self.authorize_bot()
|
||||||
|
|
||||||
self.save_session()
|
if not self.storage.is_bot:
|
||||||
|
|
||||||
if not self.is_bot:
|
|
||||||
if self.takeout:
|
if self.takeout:
|
||||||
self.takeout_id = (await self.send(functions.account.InitTakeoutSession())).id
|
self.takeout_id = (await self.send(functions.account.InitTakeoutSession())).id
|
||||||
log.warning("Takeout session {} initiated".format(self.takeout_id))
|
log.warning("Takeout session {} initiated".format(self.takeout_id))
|
||||||
|
|
||||||
now = time.time()
|
now = time.time()
|
||||||
|
|
||||||
if abs(now - self.date) > Client.OFFLINE_SLEEP:
|
if abs(now - self.storage.date) > Client.OFFLINE_SLEEP:
|
||||||
self.peers_by_username = {}
|
|
||||||
self.peers_by_phone = {}
|
|
||||||
|
|
||||||
await self.get_initial_dialogs()
|
await self.get_initial_dialogs()
|
||||||
await self.get_contacts()
|
await self.get_contacts()
|
||||||
else:
|
else:
|
||||||
@ -516,19 +514,14 @@ class Client(Methods, BaseClient):
|
|||||||
except UserMigrate as e:
|
except UserMigrate as e:
|
||||||
await self.session.stop()
|
await self.session.stop()
|
||||||
|
|
||||||
self.dc_id = e.x
|
self.storage.dc_id = e.x
|
||||||
self.auth_key = await Auth(self.dc_id, self.test_mode, self.ipv6, self._proxy).create()
|
self.storage.auth_key = await Auth(self, self.storage.dc_id).create()
|
||||||
|
self.session = Session(self, self.storage.dc_id, self.storage.auth_key)
|
||||||
self.session = Session(
|
|
||||||
self,
|
|
||||||
self.dc_id,
|
|
||||||
self.auth_key
|
|
||||||
)
|
|
||||||
|
|
||||||
await self.session.start()
|
await self.session.start()
|
||||||
await self.authorize_bot()
|
await self.authorize_bot()
|
||||||
else:
|
else:
|
||||||
self.user_id = r.user.id
|
self.storage.user_id = r.user.id
|
||||||
|
|
||||||
print("Logged in successfully as @{}".format(r.user.username))
|
print("Logged in successfully as @{}".format(r.user.username))
|
||||||
|
|
||||||
@ -569,20 +562,10 @@ class Client(Methods, BaseClient):
|
|||||||
except (PhoneMigrate, NetworkMigrate) as e:
|
except (PhoneMigrate, NetworkMigrate) as e:
|
||||||
await self.session.stop()
|
await self.session.stop()
|
||||||
|
|
||||||
self.dc_id = e.x
|
self.storage.dc_id = e.x
|
||||||
|
self.storage.auth_key = await Auth(self, self.storage.dc_id).create()
|
||||||
|
|
||||||
self.auth_key = await Auth(
|
self.session = Session(self, self.storage.dc_id, self.storage.auth_key)
|
||||||
self.dc_id,
|
|
||||||
self.test_mode,
|
|
||||||
self.ipv6,
|
|
||||||
self._proxy
|
|
||||||
).create()
|
|
||||||
|
|
||||||
self.session = Session(
|
|
||||||
self,
|
|
||||||
self.dc_id,
|
|
||||||
self.auth_key
|
|
||||||
)
|
|
||||||
|
|
||||||
await self.session.start()
|
await self.session.start()
|
||||||
except (PhoneNumberInvalid, PhoneNumberBanned) as e:
|
except (PhoneNumberInvalid, PhoneNumberBanned) as e:
|
||||||
@ -762,13 +745,13 @@ class Client(Methods, BaseClient):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.password = None
|
self.password = None
|
||||||
self.user_id = r.user.id
|
self.storage.user_id = r.user.id
|
||||||
|
|
||||||
print("Logged in successfully as {}".format(r.user.first_name))
|
print("Logged in successfully as {}".format(r.user.first_name))
|
||||||
|
|
||||||
def fetch_peers(
|
def fetch_peers(
|
||||||
self,
|
self,
|
||||||
entities: List[
|
peers: List[
|
||||||
Union[
|
Union[
|
||||||
types.User,
|
types.User,
|
||||||
types.Chat, types.ChatForbidden,
|
types.Chat, types.ChatForbidden,
|
||||||
@ -777,64 +760,57 @@ class Client(Methods, BaseClient):
|
|||||||
]
|
]
|
||||||
) -> bool:
|
) -> bool:
|
||||||
is_min = False
|
is_min = False
|
||||||
|
parsed_peers = []
|
||||||
|
|
||||||
for entity in entities:
|
for peer in peers:
|
||||||
if isinstance(entity, types.User):
|
username = None
|
||||||
user_id = entity.id
|
phone_number = None
|
||||||
|
|
||||||
access_hash = entity.access_hash
|
if isinstance(peer, types.User):
|
||||||
|
peer_id = peer.id
|
||||||
|
access_hash = peer.access_hash
|
||||||
|
|
||||||
|
username = peer.username
|
||||||
|
phone_number = peer.phone
|
||||||
|
|
||||||
|
if peer.bot:
|
||||||
|
peer_type = "bot"
|
||||||
|
else:
|
||||||
|
peer_type = "user"
|
||||||
|
|
||||||
if access_hash is None:
|
if access_hash is None:
|
||||||
is_min = True
|
is_min = True
|
||||||
continue
|
continue
|
||||||
|
|
||||||
username = entity.username
|
|
||||||
phone = entity.phone
|
|
||||||
|
|
||||||
input_peer = types.InputPeerUser(
|
|
||||||
user_id=user_id,
|
|
||||||
access_hash=access_hash
|
|
||||||
)
|
|
||||||
|
|
||||||
self.peers_by_id[user_id] = input_peer
|
|
||||||
|
|
||||||
if username is not None:
|
if username is not None:
|
||||||
self.peers_by_username[username.lower()] = input_peer
|
username = username.lower()
|
||||||
|
elif isinstance(peer, (types.Chat, types.ChatForbidden)):
|
||||||
|
peer_id = -peer.id
|
||||||
|
access_hash = 0
|
||||||
|
peer_type = "group"
|
||||||
|
elif isinstance(peer, (types.Channel, types.ChannelForbidden)):
|
||||||
|
peer_id = int("-100" + str(peer.id))
|
||||||
|
access_hash = peer.access_hash
|
||||||
|
|
||||||
if phone is not None:
|
username = getattr(peer, "username", None)
|
||||||
self.peers_by_phone[phone] = input_peer
|
|
||||||
|
|
||||||
if isinstance(entity, (types.Chat, types.ChatForbidden)):
|
if peer.broadcast:
|
||||||
chat_id = entity.id
|
peer_type = "channel"
|
||||||
peer_id = -chat_id
|
else:
|
||||||
|
peer_type = "supergroup"
|
||||||
input_peer = types.InputPeerChat(
|
|
||||||
chat_id=chat_id
|
|
||||||
)
|
|
||||||
|
|
||||||
self.peers_by_id[peer_id] = input_peer
|
|
||||||
|
|
||||||
if isinstance(entity, (types.Channel, types.ChannelForbidden)):
|
|
||||||
channel_id = entity.id
|
|
||||||
peer_id = int("-100" + str(channel_id))
|
|
||||||
|
|
||||||
access_hash = entity.access_hash
|
|
||||||
|
|
||||||
if access_hash is None:
|
if access_hash is None:
|
||||||
is_min = True
|
is_min = True
|
||||||
continue
|
continue
|
||||||
|
|
||||||
username = getattr(entity, "username", None)
|
|
||||||
|
|
||||||
input_peer = types.InputPeerChannel(
|
|
||||||
channel_id=channel_id,
|
|
||||||
access_hash=access_hash
|
|
||||||
)
|
|
||||||
|
|
||||||
self.peers_by_id[peer_id] = input_peer
|
|
||||||
|
|
||||||
if username is not None:
|
if username is not None:
|
||||||
self.peers_by_username[username.lower()] = input_peer
|
username = username.lower()
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
|
parsed_peers.append((peer_id, access_hash, peer_type, username, phone_number))
|
||||||
|
|
||||||
|
self.storage.update_peers(parsed_peers)
|
||||||
|
|
||||||
return is_min
|
return is_min
|
||||||
|
|
||||||
@ -849,37 +825,7 @@ class Client(Methods, BaseClient):
|
|||||||
final_file_path = ""
|
final_file_path = ""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data, file_name, done, progress, progress_args, path = packet
|
data, directory, file_name, done, progress, progress_args, path = packet
|
||||||
|
|
||||||
directory, file_name = os.path.split(file_name)
|
|
||||||
directory = directory or "downloads"
|
|
||||||
|
|
||||||
media_type_str = Client.MEDIA_TYPE_ID[data.media_type]
|
|
||||||
|
|
||||||
if not data.file_name:
|
|
||||||
guessed_extension = self.guess_extension(data.mime_type)
|
|
||||||
|
|
||||||
if data.media_type in (0, 1, 2, 14):
|
|
||||||
extension = ".jpg"
|
|
||||||
elif data.media_type == 3:
|
|
||||||
extension = guessed_extension or ".ogg"
|
|
||||||
elif data.media_type in (4, 10, 13):
|
|
||||||
extension = guessed_extension or ".mp4"
|
|
||||||
elif data.media_type == 5:
|
|
||||||
extension = guessed_extension or ".zip"
|
|
||||||
elif data.media_type == 8:
|
|
||||||
extension = guessed_extension or ".webp"
|
|
||||||
elif data.media_type == 9:
|
|
||||||
extension = guessed_extension or ".mp3"
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
|
|
||||||
file_name = "{}_{}_{}{}".format(
|
|
||||||
media_type_str,
|
|
||||||
datetime.fromtimestamp(data.date or time.time()).strftime("%Y-%m-%d_%H-%M-%S"),
|
|
||||||
self.rnd_id(),
|
|
||||||
extension
|
|
||||||
)
|
|
||||||
|
|
||||||
temp_file_path = await self.get_file(
|
temp_file_path = await self.get_file(
|
||||||
media_type=data.media_type,
|
media_type=data.media_type,
|
||||||
@ -1092,71 +1038,75 @@ class Client(Methods, BaseClient):
|
|||||||
self._proxy["password"] = parser.get("proxy", "password", fallback=None) or None
|
self._proxy["password"] = parser.get("proxy", "password", fallback=None) or None
|
||||||
|
|
||||||
if self.plugins:
|
if self.plugins:
|
||||||
self.plugins["enabled"] = bool(self.plugins.get("enabled", True))
|
self.plugins = {
|
||||||
self.plugins["include"] = "\n".join(self.plugins.get("include", [])) or None
|
"enabled": bool(self.plugins.get("enabled", True)),
|
||||||
self.plugins["exclude"] = "\n".join(self.plugins.get("exclude", [])) or None
|
"root": self.plugins.get("root", None),
|
||||||
|
"include": self.plugins.get("include", []),
|
||||||
|
"exclude": self.plugins.get("exclude", [])
|
||||||
|
}
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
section = parser["plugins"]
|
section = parser["plugins"]
|
||||||
|
|
||||||
self.plugins = {
|
self.plugins = {
|
||||||
"enabled": section.getboolean("enabled", True),
|
"enabled": section.getboolean("enabled", True),
|
||||||
"root": section.get("root"),
|
"root": section.get("root", None),
|
||||||
"include": section.get("include") or None,
|
"include": section.get("include", []),
|
||||||
"exclude": section.get("exclude") or None
|
"exclude": section.get("exclude", [])
|
||||||
}
|
}
|
||||||
except KeyError:
|
|
||||||
self.plugins = {}
|
|
||||||
|
|
||||||
if self.plugins:
|
include = self.plugins["include"]
|
||||||
for option in ["include", "exclude"]:
|
exclude = self.plugins["exclude"]
|
||||||
if self.plugins[option] is not None:
|
|
||||||
self.plugins[option] = [
|
if include:
|
||||||
(i.split()[0], i.split()[1:] or None)
|
self.plugins["include"] = include.strip().split("\n")
|
||||||
for i in self.plugins[option].strip().split("\n")
|
|
||||||
]
|
if exclude:
|
||||||
|
self.plugins["exclude"] = exclude.strip().split("\n")
|
||||||
|
|
||||||
|
except KeyError:
|
||||||
|
self.plugins = None
|
||||||
|
|
||||||
async def load_session(self):
|
async def load_session(self):
|
||||||
try:
|
self.storage.open()
|
||||||
with open(os.path.join(self.workdir, "{}.session".format(self.session_name)), encoding="utf-8") as f:
|
|
||||||
s = json.load(f)
|
|
||||||
except FileNotFoundError:
|
|
||||||
self.dc_id = 1
|
|
||||||
self.date = 0
|
|
||||||
self.auth_key = await Auth(self.dc_id, self.test_mode, self.ipv6, self._proxy).create()
|
|
||||||
else:
|
|
||||||
self.dc_id = s["dc_id"]
|
|
||||||
self.test_mode = s["test_mode"]
|
|
||||||
self.auth_key = base64.b64decode("".join(s["auth_key"]))
|
|
||||||
self.user_id = s["user_id"]
|
|
||||||
self.date = s.get("date", 0)
|
|
||||||
# TODO: replace default with False once token session name will be deprecated
|
|
||||||
self.is_bot = s.get("is_bot", self.is_bot)
|
|
||||||
|
|
||||||
for k, v in s.get("peers_by_id", {}).items():
|
session_empty = any([
|
||||||
self.peers_by_id[int(k)] = utils.get_input_peer(int(k), v)
|
self.storage.test_mode is None,
|
||||||
|
self.storage.auth_key is None,
|
||||||
|
self.storage.user_id is None,
|
||||||
|
self.storage.is_bot is None
|
||||||
|
])
|
||||||
|
|
||||||
for k, v in s.get("peers_by_username", {}).items():
|
if session_empty:
|
||||||
peer = self.peers_by_id.get(v, None)
|
self.storage.dc_id = 1
|
||||||
|
self.storage.date = 0
|
||||||
|
|
||||||
if peer:
|
self.storage.test_mode = self.test_mode
|
||||||
self.peers_by_username[k] = peer
|
self.storage.auth_key = await Auth(self, self.storage.dc_id).create()
|
||||||
|
self.storage.user_id = None
|
||||||
for k, v in s.get("peers_by_phone", {}).items():
|
self.storage.is_bot = None
|
||||||
peer = self.peers_by_id.get(v, None)
|
|
||||||
|
|
||||||
if peer:
|
|
||||||
self.peers_by_phone[k] = peer
|
|
||||||
|
|
||||||
def load_plugins(self):
|
def load_plugins(self):
|
||||||
if self.plugins.get("enabled", False):
|
if self.plugins:
|
||||||
root = self.plugins["root"]
|
plugins = self.plugins.copy()
|
||||||
include = self.plugins["include"]
|
|
||||||
exclude = self.plugins["exclude"]
|
for option in ["include", "exclude"]:
|
||||||
|
if plugins[option]:
|
||||||
|
plugins[option] = [
|
||||||
|
(i.split()[0], i.split()[1:] or None)
|
||||||
|
for i in self.plugins[option]
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
if plugins.get("enabled", False):
|
||||||
|
root = plugins["root"]
|
||||||
|
include = plugins["include"]
|
||||||
|
exclude = plugins["exclude"]
|
||||||
|
|
||||||
count = 0
|
count = 0
|
||||||
|
|
||||||
if include is None:
|
if not include:
|
||||||
for path in sorted(Path(root).rglob("*.py")):
|
for path in sorted(Path(root).rglob("*.py")):
|
||||||
module_path = '.'.join(path.parent.parts + (path.stem,))
|
module_path = '.'.join(path.parent.parts + (path.stem,))
|
||||||
module = import_module(module_path)
|
module = import_module(module_path)
|
||||||
@ -1213,7 +1163,7 @@ class Client(Methods, BaseClient):
|
|||||||
log.warning('[{}] [LOAD] Ignoring non-existent function "{}" from "{}"'.format(
|
log.warning('[{}] [LOAD] Ignoring non-existent function "{}" from "{}"'.format(
|
||||||
self.session_name, name, module_path))
|
self.session_name, name, module_path))
|
||||||
|
|
||||||
if exclude is not None:
|
if exclude:
|
||||||
for path, handlers in exclude:
|
for path, handlers in exclude:
|
||||||
module_path = root + "." + path
|
module_path = root + "." + path
|
||||||
warn_non_existent_functions = True
|
warn_non_existent_functions = True
|
||||||
@ -1258,28 +1208,7 @@ class Client(Methods, BaseClient):
|
|||||||
log.warning('[{}] No plugin loaded from "{}"'.format(
|
log.warning('[{}] No plugin loaded from "{}"'.format(
|
||||||
self.session_name, root))
|
self.session_name, root))
|
||||||
|
|
||||||
def save_session(self):
|
async def get_initial_dialogs_chunk(self, offset_date: int = 0):
|
||||||
auth_key = base64.b64encode(self.auth_key).decode()
|
|
||||||
auth_key = [auth_key[i: i + 43] for i in range(0, len(auth_key), 43)]
|
|
||||||
|
|
||||||
os.makedirs(self.workdir, exist_ok=True)
|
|
||||||
|
|
||||||
with open(os.path.join(self.workdir, "{}.session".format(self.session_name)), "w", encoding="utf-8") as f:
|
|
||||||
json.dump(
|
|
||||||
dict(
|
|
||||||
dc_id=self.dc_id,
|
|
||||||
test_mode=self.test_mode,
|
|
||||||
auth_key=auth_key,
|
|
||||||
user_id=self.user_id,
|
|
||||||
date=self.date,
|
|
||||||
is_bot=self.is_bot,
|
|
||||||
),
|
|
||||||
f,
|
|
||||||
indent=4
|
|
||||||
)
|
|
||||||
|
|
||||||
async def get_initial_dialogs_chunk(self,
|
|
||||||
offset_date: int = 0):
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
r = await self.send(
|
r = await self.send(
|
||||||
@ -1296,7 +1225,7 @@ class Client(Methods, BaseClient):
|
|||||||
log.warning("get_dialogs flood: waiting {} seconds".format(e.x))
|
log.warning("get_dialogs flood: waiting {} seconds".format(e.x))
|
||||||
await asyncio.sleep(e.x)
|
await asyncio.sleep(e.x)
|
||||||
else:
|
else:
|
||||||
log.info("Total peers: {}".format(len(self.peers_by_id)))
|
log.info("Total peers: {}".format(self.storage.peers_count))
|
||||||
return r
|
return r
|
||||||
|
|
||||||
async def get_initial_dialogs(self):
|
async def get_initial_dialogs(self):
|
||||||
@ -1335,7 +1264,7 @@ class Client(Methods, BaseClient):
|
|||||||
KeyError: In case the peer doesn't exist in the internal database.
|
KeyError: In case the peer doesn't exist in the internal database.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return self.peers_by_id[peer_id]
|
return self.storage.get_peer_by_id(peer_id)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
if type(peer_id) is str:
|
if type(peer_id) is str:
|
||||||
if peer_id in ("self", "me"):
|
if peer_id in ("self", "me"):
|
||||||
@ -1346,15 +1275,17 @@ class Client(Methods, BaseClient):
|
|||||||
try:
|
try:
|
||||||
int(peer_id)
|
int(peer_id)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
if peer_id not in self.peers_by_username:
|
try:
|
||||||
|
return self.storage.get_peer_by_username(peer_id)
|
||||||
|
except KeyError:
|
||||||
await self.send(functions.contacts.ResolveUsername(username=peer_id
|
await self.send(functions.contacts.ResolveUsername(username=peer_id
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return self.peers_by_username[peer_id]
|
return self.storage.get_peer_by_username(peer_id)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
return self.peers_by_phone[peer_id]
|
return self.storage.get_peer_by_phone_number(peer_id)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise PeerIdInvalid
|
raise PeerIdInvalid
|
||||||
|
|
||||||
@ -1362,7 +1293,10 @@ class Client(Methods, BaseClient):
|
|||||||
self.fetch_peers(
|
self.fetch_peers(
|
||||||
await self.send(
|
await self.send(
|
||||||
functions.users.GetUsers(
|
functions.users.GetUsers(
|
||||||
id=[types.InputUser(user_id=peer_id, access_hash=0)]
|
id=[types.InputUser(
|
||||||
|
user_id=peer_id,
|
||||||
|
access_hash=0
|
||||||
|
)]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -1370,7 +1304,10 @@ class Client(Methods, BaseClient):
|
|||||||
if str(peer_id).startswith("-100"):
|
if str(peer_id).startswith("-100"):
|
||||||
await self.send(
|
await self.send(
|
||||||
functions.channels.GetChannels(
|
functions.channels.GetChannels(
|
||||||
id=[types.InputChannel(channel_id=int(str(peer_id)[4:]), access_hash=0)]
|
id=[types.InputChannel(
|
||||||
|
channel_id=int(str(peer_id)[4:]),
|
||||||
|
access_hash=0
|
||||||
|
)]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@ -1381,7 +1318,7 @@ class Client(Methods, BaseClient):
|
|||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return self.peers_by_id[peer_id]
|
return self.storage.get_peer_by_id(peer_id)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise PeerIdInvalid
|
raise PeerIdInvalid
|
||||||
|
|
||||||
@ -1469,7 +1406,7 @@ class Client(Methods, BaseClient):
|
|||||||
is_missing_part = file_id is not None
|
is_missing_part = file_id is not None
|
||||||
file_id = file_id or self.rnd_id()
|
file_id = file_id or self.rnd_id()
|
||||||
md5_sum = md5() if not is_big and not is_missing_part else None
|
md5_sum = md5() if not is_big and not is_missing_part else None
|
||||||
pool = [Session(self, self.dc_id, self.auth_key, is_media=True) for _ in range(pool_size)]
|
pool = [Session(self, self.storage.dc_id, self.storage.auth_key, is_media=True) for _ in range(pool_size)]
|
||||||
workers = [asyncio.ensure_future(worker(session)) for session in pool for _ in range(workers_count)]
|
workers = [asyncio.ensure_future(worker(session)) for session in pool for _ in range(workers_count)]
|
||||||
queue = asyncio.Queue(16)
|
queue = asyncio.Queue(16)
|
||||||
|
|
||||||
@ -1559,7 +1496,7 @@ class Client(Methods, BaseClient):
|
|||||||
session = self.media_sessions.get(dc_id, None)
|
session = self.media_sessions.get(dc_id, None)
|
||||||
|
|
||||||
if session is None:
|
if session is None:
|
||||||
if dc_id != self.dc_id:
|
if dc_id != self.storage.dc_id:
|
||||||
exported_auth = await self.send(
|
exported_auth = await self.send(
|
||||||
functions.auth.ExportAuthorization(
|
functions.auth.ExportAuthorization(
|
||||||
dc_id=dc_id
|
dc_id=dc_id
|
||||||
@ -1569,9 +1506,7 @@ class Client(Methods, BaseClient):
|
|||||||
session = Session(
|
session = Session(
|
||||||
self,
|
self,
|
||||||
dc_id,
|
dc_id,
|
||||||
await Auth(dc_id, self.test_mode, self.ipv6, self._proxy).create(),
|
await Auth(self, dc_id).create(), is_media=True)
|
||||||
is_media=True
|
|
||||||
)
|
|
||||||
|
|
||||||
await session.start()
|
await session.start()
|
||||||
|
|
||||||
@ -1584,12 +1519,7 @@ class Client(Methods, BaseClient):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
session = Session(
|
session = Session(self, dc_id, self.storage.auth_key, is_media=True)
|
||||||
self,
|
|
||||||
dc_id,
|
|
||||||
self.auth_key,
|
|
||||||
is_media=True
|
|
||||||
)
|
|
||||||
|
|
||||||
await session.start()
|
await session.start()
|
||||||
|
|
||||||
@ -1677,10 +1607,7 @@ class Client(Methods, BaseClient):
|
|||||||
cdn_session = Session(
|
cdn_session = Session(
|
||||||
self,
|
self,
|
||||||
r.dc_id,
|
r.dc_id,
|
||||||
await Auth(r.dc_id, self.test_mode, self.ipv6, self._proxy).create(),
|
await Auth(self, r.dc_id).create(), is_media=True, is_cdn=True)
|
||||||
is_media=True,
|
|
||||||
is_cdn=True
|
|
||||||
)
|
|
||||||
|
|
||||||
await cdn_session.start()
|
await cdn_session.start()
|
||||||
|
|
||||||
@ -1776,3 +1703,11 @@ class Client(Methods, BaseClient):
|
|||||||
|
|
||||||
if extensions:
|
if extensions:
|
||||||
return extensions.split(" ")[0]
|
return extensions.split(" ")[0]
|
||||||
|
|
||||||
|
def export_session_string(self):
|
||||||
|
"""Export the current session as serialized string.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
``str``: The session serialized into a printable, url-safe string.
|
||||||
|
"""
|
||||||
|
return self.storage.export_session_string()
|
||||||
|
@ -19,6 +19,5 @@
|
|||||||
from .base_client import BaseClient
|
from .base_client import BaseClient
|
||||||
from .dispatcher import Dispatcher
|
from .dispatcher import Dispatcher
|
||||||
from .emoji import Emoji
|
from .emoji import Emoji
|
||||||
from .syncer import Syncer
|
|
||||||
from .file_data import FileData
|
from .file_data import FileData
|
||||||
|
from .syncer import Syncer
|
||||||
|
@ -20,8 +20,11 @@ import asyncio
|
|||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from pyrogram import __version__
|
from pyrogram import __version__
|
||||||
|
|
||||||
from ..style import Markdown, HTML
|
from ..style import Markdown, HTML
|
||||||
from ...session.internals import MsgId
|
from ...session.internals import MsgId
|
||||||
|
|
||||||
@ -44,6 +47,8 @@ class BaseClient:
|
|||||||
|
|
||||||
LANG_CODE = "en"
|
LANG_CODE = "en"
|
||||||
|
|
||||||
|
PARENT_DIR = Path(sys.argv[0]).parent
|
||||||
|
|
||||||
INVITE_LINK_RE = re.compile(r"^(?:https?://)?(?:www\.)?(?:t(?:elegram)?\.(?:org|me|dog)/joinchat/)([\w-]+)$")
|
INVITE_LINK_RE = re.compile(r"^(?:https?://)?(?:www\.)?(?:t(?:elegram)?\.(?:org|me|dog)/joinchat/)([\w-]+)$")
|
||||||
BOT_TOKEN_RE = re.compile(r"^\d+:[\w-]+$")
|
BOT_TOKEN_RE = re.compile(r"^\d+:[\w-]+$")
|
||||||
DIALOGS_AT_ONCE = 100
|
DIALOGS_AT_ONCE = 100
|
||||||
@ -51,8 +56,8 @@ class BaseClient:
|
|||||||
DOWNLOAD_WORKERS = 4
|
DOWNLOAD_WORKERS = 4
|
||||||
OFFLINE_SLEEP = 900
|
OFFLINE_SLEEP = 900
|
||||||
WORKERS = 4
|
WORKERS = 4
|
||||||
WORKDIR = "."
|
WORKDIR = PARENT_DIR
|
||||||
CONFIG_FILE = "./config.ini"
|
CONFIG_FILE = PARENT_DIR / "config.ini"
|
||||||
|
|
||||||
MEDIA_TYPE_ID = {
|
MEDIA_TYPE_ID = {
|
||||||
0: "photo_thumbnail",
|
0: "photo_thumbnail",
|
||||||
@ -83,18 +88,10 @@ class BaseClient:
|
|||||||
mime_types_to_extensions[mime_type] = " ".join(extensions)
|
mime_types_to_extensions[mime_type] = " ".join(extensions)
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.is_bot = None
|
self.storage = None
|
||||||
self.dc_id = None
|
|
||||||
self.auth_key = None
|
|
||||||
self.user_id = None
|
|
||||||
self.date = None
|
|
||||||
|
|
||||||
self.rnd_id = MsgId
|
self.rnd_id = MsgId
|
||||||
|
|
||||||
self.peers_by_id = {}
|
|
||||||
self.peers_by_username = {}
|
|
||||||
self.peers_by_phone = {}
|
|
||||||
|
|
||||||
self.markdown = Markdown(self)
|
self.markdown = Markdown(self)
|
||||||
self.html = HTML(self)
|
self.html = HTML(self)
|
||||||
|
|
||||||
@ -155,3 +152,21 @@ class BaseClient:
|
|||||||
|
|
||||||
def get_profile_photos(self, *args, **kwargs):
|
def get_profile_photos(self, *args, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def edit_message_text(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def edit_inline_text(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def edit_message_media(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def edit_inline_media(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def edit_message_reply_markup(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def edit_inline_reply_markup(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
@ -22,6 +22,8 @@ from collections import OrderedDict
|
|||||||
|
|
||||||
import pyrogram
|
import pyrogram
|
||||||
from pyrogram.api import types
|
from pyrogram.api import types
|
||||||
|
|
||||||
|
from . import utils
|
||||||
from ..handlers import (
|
from ..handlers import (
|
||||||
CallbackQueryHandler, MessageHandler, DeletedMessagesHandler,
|
CallbackQueryHandler, MessageHandler, DeletedMessagesHandler,
|
||||||
UserStatusHandler, RawUpdateHandler, InlineQueryHandler, PollHandler
|
UserStatusHandler, RawUpdateHandler, InlineQueryHandler, PollHandler
|
||||||
@ -65,7 +67,7 @@ class Dispatcher:
|
|||||||
return await pyrogram.Message._parse(self.client, update.message, users, chats), MessageHandler
|
return await pyrogram.Message._parse(self.client, update.message, users, chats), MessageHandler
|
||||||
|
|
||||||
async def deleted_messages_parser(update, users, chats):
|
async def deleted_messages_parser(update, users, chats):
|
||||||
return pyrogram.Messages._parse_deleted(self.client, update), DeletedMessagesHandler
|
return utils.parse_deleted_messages(self.client, update), DeletedMessagesHandler
|
||||||
|
|
||||||
async def callback_query_parser(update, users, chats):
|
async def callback_query_parser(update, users, chats):
|
||||||
return await pyrogram.CallbackQuery._parse(self.client, update, users), CallbackQueryHandler
|
return await pyrogram.CallbackQuery._parse(self.client, update, users), CallbackQueryHandler
|
||||||
@ -106,6 +108,7 @@ class Dispatcher:
|
|||||||
await i
|
await i
|
||||||
|
|
||||||
self.update_worker_tasks.clear()
|
self.update_worker_tasks.clear()
|
||||||
|
self.groups.clear()
|
||||||
|
|
||||||
log.info("Stopped {} UpdateWorkerTasks".format(self.workers))
|
log.info("Stopped {} UpdateWorkerTasks".format(self.workers))
|
||||||
|
|
||||||
|
@ -17,15 +17,9 @@
|
|||||||
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import base64
|
|
||||||
import json
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from . import utils
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -85,48 +79,13 @@ class Syncer:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def sync(cls, client):
|
def sync(cls, client):
|
||||||
temporary = os.path.join(client.workdir, "{}.sync".format(client.session_name))
|
|
||||||
persistent = os.path.join(client.workdir, "{}.session".format(client.session_name))
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
auth_key = base64.b64encode(client.auth_key).decode()
|
start = time.time()
|
||||||
auth_key = [auth_key[i: i + 43] for i in range(0, len(auth_key), 43)]
|
client.storage.save()
|
||||||
|
|
||||||
data = dict(
|
|
||||||
dc_id=client.dc_id,
|
|
||||||
test_mode=client.test_mode,
|
|
||||||
auth_key=auth_key,
|
|
||||||
user_id=client.user_id,
|
|
||||||
date=int(time.time()),
|
|
||||||
is_bot=bool(client.is_bot),
|
|
||||||
peers_by_id={
|
|
||||||
k: getattr(v, "access_hash", None)
|
|
||||||
for k, v in client.peers_by_id.copy().items()
|
|
||||||
},
|
|
||||||
peers_by_username={
|
|
||||||
k: utils.get_peer_id(v)
|
|
||||||
for k, v in client.peers_by_username.copy().items()
|
|
||||||
},
|
|
||||||
peers_by_phone={
|
|
||||||
k: utils.get_peer_id(v)
|
|
||||||
for k, v in client.peers_by_phone.copy().items()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
os.makedirs(client.workdir, exist_ok=True)
|
|
||||||
|
|
||||||
with open(temporary, "w", encoding="utf-8") as f:
|
|
||||||
json.dump(data, f, indent=4)
|
|
||||||
|
|
||||||
f.flush()
|
|
||||||
os.fsync(f.fileno())
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.critical(e, exc_info=True)
|
log.critical(e, exc_info=True)
|
||||||
else:
|
else:
|
||||||
shutil.move(temporary, persistent)
|
log.info('Synced "{}" in {:.6} ms'.format(
|
||||||
log.info("Synced {}".format(client.session_name))
|
client.storage.name,
|
||||||
finally:
|
(time.time() - start) * 1000
|
||||||
try:
|
))
|
||||||
os.remove(temporary)
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
|
@ -17,18 +17,20 @@
|
|||||||
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import base64
|
||||||
import struct
|
import struct
|
||||||
import sys
|
import sys
|
||||||
from base64 import b64decode, b64encode
|
|
||||||
from concurrent.futures.thread import ThreadPoolExecutor
|
from concurrent.futures.thread import ThreadPoolExecutor
|
||||||
from typing import Union
|
from typing import Union, List
|
||||||
|
|
||||||
|
import pyrogram
|
||||||
|
|
||||||
from . import BaseClient
|
from . import BaseClient
|
||||||
from ...api import types
|
from ...api import types
|
||||||
|
|
||||||
|
|
||||||
def decode(s: str) -> bytes:
|
def decode(s: str) -> bytes:
|
||||||
s = b64decode(s + "=" * (-len(s) % 4), "-_")
|
s = base64.urlsafe_b64decode(s + "=" * (-len(s) % 4))
|
||||||
r = b""
|
r = b""
|
||||||
|
|
||||||
assert s[-1] == 2
|
assert s[-1] == 2
|
||||||
@ -60,7 +62,7 @@ def encode(s: bytes) -> str:
|
|||||||
|
|
||||||
r += bytes([i])
|
r += bytes([i])
|
||||||
|
|
||||||
return b64encode(r, b"-_").decode().rstrip("=")
|
return base64.urlsafe_b64encode(r).decode().rstrip("=")
|
||||||
|
|
||||||
|
|
||||||
async def ainput(prompt: str = ""):
|
async def ainput(prompt: str = ""):
|
||||||
@ -147,3 +149,69 @@ def get_input_media_from_file_id(
|
|||||||
)
|
)
|
||||||
|
|
||||||
raise ValueError("Unknown media type: {}".format(file_id_str))
|
raise ValueError("Unknown media type: {}".format(file_id_str))
|
||||||
|
|
||||||
|
|
||||||
|
async def parse_messages(client, messages: types.messages.Messages, replies: int = 1) -> List["pyrogram.Message"]:
|
||||||
|
users = {i.id: i for i in messages.users}
|
||||||
|
chats = {i.id: i for i in messages.chats}
|
||||||
|
|
||||||
|
if not messages.messages:
|
||||||
|
return pyrogram.List()
|
||||||
|
|
||||||
|
parsed_messages = []
|
||||||
|
|
||||||
|
for message in messages.messages:
|
||||||
|
parsed_messages.append(await pyrogram.Message._parse(client, message, users, chats, replies=0))
|
||||||
|
|
||||||
|
if replies:
|
||||||
|
messages_with_replies = {i.id: getattr(i, "reply_to_msg_id", None) for i in messages.messages}
|
||||||
|
reply_message_ids = [i[0] for i in filter(lambda x: x[1] is not None, messages_with_replies.items())]
|
||||||
|
|
||||||
|
if reply_message_ids:
|
||||||
|
reply_messages = await client.get_messages(
|
||||||
|
parsed_messages[0].chat.id,
|
||||||
|
reply_to_message_ids=reply_message_ids,
|
||||||
|
replies=replies - 1
|
||||||
|
)
|
||||||
|
|
||||||
|
for message in parsed_messages:
|
||||||
|
reply_id = messages_with_replies[message.message_id]
|
||||||
|
|
||||||
|
for reply in reply_messages:
|
||||||
|
if reply.message_id == reply_id:
|
||||||
|
message.reply_to_message = reply
|
||||||
|
|
||||||
|
return pyrogram.List(parsed_messages)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_deleted_messages(client, update) -> List["pyrogram.Message"]:
|
||||||
|
messages = update.messages
|
||||||
|
channel_id = getattr(update, "channel_id", None)
|
||||||
|
|
||||||
|
parsed_messages = []
|
||||||
|
|
||||||
|
for message in messages:
|
||||||
|
parsed_messages.append(
|
||||||
|
pyrogram.Message(
|
||||||
|
message_id=message,
|
||||||
|
chat=pyrogram.Chat(
|
||||||
|
id=int("-100" + str(channel_id)),
|
||||||
|
type="channel",
|
||||||
|
client=client
|
||||||
|
) if channel_id is not None else None,
|
||||||
|
client=client
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return pyrogram.List(parsed_messages)
|
||||||
|
|
||||||
|
|
||||||
|
def unpack_inline_message_id(inline_message_id: str) -> types.InputBotInlineMessageID:
|
||||||
|
r = inline_message_id + "=" * (-len(inline_message_id) % 4)
|
||||||
|
r = struct.unpack("<iqq", base64.b64decode(r, altchars="-_"))
|
||||||
|
|
||||||
|
return types.InputBotInlineMessageID(
|
||||||
|
dc_id=r[0],
|
||||||
|
id=r[1],
|
||||||
|
access_hash=r[2]
|
||||||
|
)
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .filter import Filter
|
from .filter import Filter
|
||||||
from ..types.keyboards import InlineKeyboardMarkup, ReplyKeyboardMarkup
|
from ..types.bots_and_keyboards import InlineKeyboardMarkup, ReplyKeyboardMarkup
|
||||||
|
|
||||||
|
|
||||||
def create(name: str, func: callable, **kwargs) -> type:
|
def create(name: str, func: callable, **kwargs) -> type:
|
||||||
|
@ -20,16 +20,15 @@ from .handler import Handler
|
|||||||
|
|
||||||
|
|
||||||
class DeletedMessagesHandler(Handler):
|
class DeletedMessagesHandler(Handler):
|
||||||
"""The deleted Messages handler class. Used to handle deleted messages coming from any chat
|
"""The deleted messages handler class. Used to handle deleted messages coming from any chat
|
||||||
(private, group, channel). It is intended to be used with
|
(private, group, channel). It is intended to be used with :meth:`~Client.add_handler`
|
||||||
:meth:`~Client.add_handler`
|
|
||||||
|
|
||||||
For a nicer way to register this handler, have a look at the
|
For a nicer way to register this handler, have a look at the
|
||||||
:meth:`~Client.on_deleted_messages` decorator.
|
:meth:`~Client.on_deleted_messages` decorator.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
callback (``callable``):
|
callback (``callable``):
|
||||||
Pass a function that will be called when one or more Messages have been deleted.
|
Pass a function that will be called when one or more messages have been deleted.
|
||||||
It takes *(client, messages)* as positional arguments (look at the section below for a detailed description).
|
It takes *(client, messages)* as positional arguments (look at the section below for a detailed description).
|
||||||
|
|
||||||
filters (:obj:`Filters`):
|
filters (:obj:`Filters`):
|
||||||
@ -40,12 +39,12 @@ class DeletedMessagesHandler(Handler):
|
|||||||
client (:obj:`Client`):
|
client (:obj:`Client`):
|
||||||
The Client itself, useful when you want to call other API methods inside the message handler.
|
The Client itself, useful when you want to call other API methods inside the message handler.
|
||||||
|
|
||||||
messages (:obj:`Messages`):
|
messages (List of :obj:`Message`):
|
||||||
The deleted messages.
|
The deleted messages, as list.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, callback: callable, filters=None):
|
def __init__(self, callback: callable, filters=None):
|
||||||
super().__init__(callback, filters)
|
super().__init__(callback, filters)
|
||||||
|
|
||||||
def check(self, messages):
|
def check(self, messages):
|
||||||
return super().check(messages.messages[0])
|
return super().check(messages[0])
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from typing import Union
|
from typing import Union, List
|
||||||
|
|
||||||
import pyrogram
|
import pyrogram
|
||||||
from pyrogram.api import functions
|
from pyrogram.api import functions
|
||||||
@ -29,7 +29,7 @@ class GetGameHighScores(BaseClient):
|
|||||||
user_id: Union[int, str],
|
user_id: Union[int, str],
|
||||||
chat_id: Union[int, str],
|
chat_id: Union[int, str],
|
||||||
message_id: int = None
|
message_id: int = None
|
||||||
) -> "pyrogram.GameHighScores":
|
) -> List["pyrogram.GameHighScore"]:
|
||||||
"""Get data for high score tables.
|
"""Get data for high score tables.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
@ -49,20 +49,19 @@ class GetGameHighScores(BaseClient):
|
|||||||
Required if inline_message_id is not specified.
|
Required if inline_message_id is not specified.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
:obj:`GameHighScores`: On success.
|
List of :obj:`GameHighScore`: On success.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
RPCError: In case of a Telegram RPC error.
|
RPCError: In case of a Telegram RPC error.
|
||||||
"""
|
"""
|
||||||
# TODO: inline_message_id
|
# TODO: inline_message_id
|
||||||
|
|
||||||
return pyrogram.GameHighScores._parse(
|
r = await self.send(
|
||||||
self,
|
functions.messages.GetGameHighScores(
|
||||||
await self.send(
|
peer=await self.resolve_peer(chat_id),
|
||||||
functions.messages.GetGameHighScores(
|
id=message_id,
|
||||||
peer=await self.resolve_peer(chat_id),
|
user_id=await self.resolve_peer(user_id)
|
||||||
id=message_id,
|
|
||||||
user_id=await self.resolve_peer(user_id)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return pyrogram.List(pyrogram.GameHighScore._parse(self, score, r.users) for score in r.scores)
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from .archive_chats import ArchiveChats
|
||||||
from .delete_chat_photo import DeleteChatPhoto
|
from .delete_chat_photo import DeleteChatPhoto
|
||||||
from .export_chat_invite_link import ExportChatInviteLink
|
from .export_chat_invite_link import ExportChatInviteLink
|
||||||
from .get_chat import GetChat
|
from .get_chat import GetChat
|
||||||
@ -36,6 +37,7 @@ from .restrict_chat_member import RestrictChatMember
|
|||||||
from .set_chat_description import SetChatDescription
|
from .set_chat_description import SetChatDescription
|
||||||
from .set_chat_photo import SetChatPhoto
|
from .set_chat_photo import SetChatPhoto
|
||||||
from .set_chat_title import SetChatTitle
|
from .set_chat_title import SetChatTitle
|
||||||
|
from .unarchive_chats import UnarchiveChats
|
||||||
from .unban_chat_member import UnbanChatMember
|
from .unban_chat_member import UnbanChatMember
|
||||||
from .unpin_chat_message import UnpinChatMessage
|
from .unpin_chat_message import UnpinChatMessage
|
||||||
from .update_chat_username import UpdateChatUsername
|
from .update_chat_username import UpdateChatUsername
|
||||||
@ -64,6 +66,8 @@ class Chats(
|
|||||||
IterChatMembers,
|
IterChatMembers,
|
||||||
UpdateChatUsername,
|
UpdateChatUsername,
|
||||||
RestrictChat,
|
RestrictChat,
|
||||||
GetDialogsCount
|
GetDialogsCount,
|
||||||
|
ArchiveChats,
|
||||||
|
UnarchiveChats
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
59
pyrogram/client/methods/chats/archive_chats.py
Normal file
59
pyrogram/client/methods/chats/archive_chats.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# Pyrogram - Telegram MTProto API Client Library for Python
|
||||||
|
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
|
||||||
|
#
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from typing import Union, List
|
||||||
|
|
||||||
|
from pyrogram.api import functions, types
|
||||||
|
|
||||||
|
from ...ext import BaseClient
|
||||||
|
|
||||||
|
|
||||||
|
class ArchiveChats(BaseClient):
|
||||||
|
async def archive_chats(
|
||||||
|
self,
|
||||||
|
chat_ids: Union[int, str, List[Union[int, str]]],
|
||||||
|
) -> bool:
|
||||||
|
"""Archive one or more chats.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
chat_ids (``int`` | ``str`` | List[``int``, ``str``]):
|
||||||
|
Unique identifier (int) or username (str) of the target chat.
|
||||||
|
You can also pass a list of ids (int) or usernames (str).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
``bool``: On success, True is returned.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RPCError: In case of a Telegram RPC error.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not isinstance(chat_ids, list):
|
||||||
|
chat_ids = [chat_ids]
|
||||||
|
|
||||||
|
await self.send(
|
||||||
|
functions.folders.EditPeerFolders(
|
||||||
|
folder_peers=[
|
||||||
|
types.InputFolderPeer(
|
||||||
|
peer=await self.resolve_peer(chat),
|
||||||
|
folder_id=1
|
||||||
|
) for chat in chat_ids
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
@ -21,6 +21,7 @@ from typing import Union
|
|||||||
import pyrogram
|
import pyrogram
|
||||||
from pyrogram.api import functions, types
|
from pyrogram.api import functions, types
|
||||||
from pyrogram.errors import UserNotParticipant
|
from pyrogram.errors import UserNotParticipant
|
||||||
|
|
||||||
from ...ext import BaseClient
|
from ...ext import BaseClient
|
||||||
|
|
||||||
|
|
||||||
@ -51,13 +52,18 @@ class GetChatMember(BaseClient):
|
|||||||
user = await self.resolve_peer(user_id)
|
user = await self.resolve_peer(user_id)
|
||||||
|
|
||||||
if isinstance(chat, types.InputPeerChat):
|
if isinstance(chat, types.InputPeerChat):
|
||||||
full_chat = await self.send(
|
r = await self.send(
|
||||||
functions.messages.GetFullChat(
|
functions.messages.GetFullChat(
|
||||||
chat_id=chat.chat_id
|
chat_id=chat.chat_id
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
for member in pyrogram.ChatMembers._parse(self, full_chat).chat_members:
|
members = r.full_chat.participants.participants
|
||||||
|
users = {i.id: i for i in r.users}
|
||||||
|
|
||||||
|
for member in members:
|
||||||
|
member = pyrogram.ChatMember._parse(self, member, users)
|
||||||
|
|
||||||
if isinstance(user, types.InputPeerSelf):
|
if isinstance(user, types.InputPeerSelf):
|
||||||
if member.user.is_self:
|
if member.user.is_self:
|
||||||
return member
|
return member
|
||||||
|
@ -18,11 +18,12 @@
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from typing import Union
|
from typing import Union, List
|
||||||
|
|
||||||
import pyrogram
|
import pyrogram
|
||||||
from pyrogram.api import functions, types
|
from pyrogram.api import functions, types
|
||||||
from pyrogram.errors import FloodWait
|
from pyrogram.errors import FloodWait
|
||||||
|
|
||||||
from ...ext import BaseClient
|
from ...ext import BaseClient
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -45,7 +46,7 @@ class GetChatMembers(BaseClient):
|
|||||||
limit: int = 200,
|
limit: int = 200,
|
||||||
query: str = "",
|
query: str = "",
|
||||||
filter: str = Filters.ALL
|
filter: str = Filters.ALL
|
||||||
) -> "pyrogram.ChatMembers":
|
) -> List["pyrogram.ChatMember"]:
|
||||||
"""Get a chunk of the members list of a chat.
|
"""Get a chunk of the members list of a chat.
|
||||||
|
|
||||||
You can get up to 200 chat members at once.
|
You can get up to 200 chat members at once.
|
||||||
@ -59,15 +60,16 @@ class GetChatMembers(BaseClient):
|
|||||||
|
|
||||||
offset (``int``, *optional*):
|
offset (``int``, *optional*):
|
||||||
Sequential number of the first member to be returned.
|
Sequential number of the first member to be returned.
|
||||||
Defaults to 0 [1]_.
|
Only applicable to supergroups and channels. Defaults to 0 [1]_.
|
||||||
|
|
||||||
limit (``int``, *optional*):
|
limit (``int``, *optional*):
|
||||||
Limits the number of members to be retrieved.
|
Limits the number of members to be retrieved.
|
||||||
|
Only applicable to supergroups and channels.
|
||||||
Defaults to 200, which is also the maximum server limit allowed per method call.
|
Defaults to 200, which is also the maximum server limit allowed per method call.
|
||||||
|
|
||||||
query (``str``, *optional*):
|
query (``str``, *optional*):
|
||||||
Query string to filter members based on their display names and usernames.
|
Query string to filter members based on their display names and usernames.
|
||||||
Defaults to "" (empty string) [2]_.
|
Only applicable to supergroups and channels. Defaults to "" (empty string) [2]_.
|
||||||
|
|
||||||
filter (``str``, *optional*):
|
filter (``str``, *optional*):
|
||||||
Filter used to select the kind of members you want to retrieve. Only applicable for supergroups
|
Filter used to select the kind of members you want to retrieve. Only applicable for supergroups
|
||||||
@ -78,6 +80,7 @@ class GetChatMembers(BaseClient):
|
|||||||
*"bots"* - bots only,
|
*"bots"* - bots only,
|
||||||
*"recent"* - recent members only,
|
*"recent"* - recent members only,
|
||||||
*"administrators"* - chat administrators only.
|
*"administrators"* - chat administrators only.
|
||||||
|
Only applicable to supergroups and channels.
|
||||||
Defaults to *"all"*.
|
Defaults to *"all"*.
|
||||||
|
|
||||||
.. [1] Server limit: on supergroups, you can get up to 10,000 members for a single query and up to 200 members
|
.. [1] Server limit: on supergroups, you can get up to 10,000 members for a single query and up to 200 members
|
||||||
@ -86,7 +89,7 @@ class GetChatMembers(BaseClient):
|
|||||||
.. [2] A query string is applicable only for *"all"*, *"kicked"* and *"restricted"* filters only.
|
.. [2] A query string is applicable only for *"all"*, *"kicked"* and *"restricted"* filters only.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
:obj:`ChatMembers`: On success, an object containing a list of chat members is returned.
|
List of :obj:`ChatMember`: On success, a list of chat members is returned.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
RPCError: In case of a Telegram RPC error.
|
RPCError: In case of a Telegram RPC error.
|
||||||
@ -95,14 +98,16 @@ class GetChatMembers(BaseClient):
|
|||||||
peer = await self.resolve_peer(chat_id)
|
peer = await self.resolve_peer(chat_id)
|
||||||
|
|
||||||
if isinstance(peer, types.InputPeerChat):
|
if isinstance(peer, types.InputPeerChat):
|
||||||
return pyrogram.ChatMembers._parse(
|
r = await self.send(
|
||||||
self,
|
functions.messages.GetFullChat(
|
||||||
await self.send(
|
chat_id=peer.chat_id
|
||||||
functions.messages.GetFullChat(
|
|
||||||
chat_id=peer.chat_id
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
members = r.full_chat.participants.participants
|
||||||
|
users = {i.id: i for i in r.users}
|
||||||
|
|
||||||
|
return pyrogram.List(pyrogram.ChatMember._parse(self, member, users) for member in members)
|
||||||
elif isinstance(peer, types.InputPeerChannel):
|
elif isinstance(peer, types.InputPeerChannel):
|
||||||
filter = filter.lower()
|
filter = filter.lower()
|
||||||
|
|
||||||
@ -123,18 +128,20 @@ class GetChatMembers(BaseClient):
|
|||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
return pyrogram.ChatMembers._parse(
|
r = await self.send(
|
||||||
self,
|
functions.channels.GetParticipants(
|
||||||
await self.send(
|
channel=peer,
|
||||||
functions.channels.GetParticipants(
|
filter=filter,
|
||||||
channel=peer,
|
offset=offset,
|
||||||
filter=filter,
|
limit=limit,
|
||||||
offset=offset,
|
hash=0
|
||||||
limit=limit,
|
|
||||||
hash=0
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
members = r.participants
|
||||||
|
users = {i.id: i for i in r.users}
|
||||||
|
|
||||||
|
return pyrogram.List(pyrogram.ChatMember._parse(self, member, users) for member in members)
|
||||||
except FloodWait as e:
|
except FloodWait as e:
|
||||||
log.warning("Sleeping for {}s".format(e.x))
|
log.warning("Sleeping for {}s".format(e.x))
|
||||||
await asyncio.sleep(e.x)
|
await asyncio.sleep(e.x)
|
||||||
|
@ -18,10 +18,12 @@
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
from typing import List
|
||||||
|
|
||||||
import pyrogram
|
import pyrogram
|
||||||
from pyrogram.api import functions, types
|
from pyrogram.api import functions, types
|
||||||
from pyrogram.errors import FloodWait
|
from pyrogram.errors import FloodWait
|
||||||
|
|
||||||
from ...ext import BaseClient
|
from ...ext import BaseClient
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -33,7 +35,7 @@ class GetDialogs(BaseClient):
|
|||||||
offset_date: int = 0,
|
offset_date: int = 0,
|
||||||
limit: int = 100,
|
limit: int = 100,
|
||||||
pinned_only: bool = False
|
pinned_only: bool = False
|
||||||
) -> "pyrogram.Dialogs":
|
) -> List["pyrogram.Dialog"]:
|
||||||
"""Get a chunk of the user's dialogs.
|
"""Get a chunk of the user's dialogs.
|
||||||
|
|
||||||
You can get up to 100 dialogs at once.
|
You can get up to 100 dialogs at once.
|
||||||
@ -53,7 +55,7 @@ class GetDialogs(BaseClient):
|
|||||||
Defaults to False.
|
Defaults to False.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
:obj:`Dialogs`: On success, an object containing a list of dialogs is returned.
|
List of :obj:`Dialog`: On success, a list of dialogs is returned.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
RPCError: In case of a Telegram RPC error.
|
RPCError: In case of a Telegram RPC error.
|
||||||
@ -80,4 +82,32 @@ class GetDialogs(BaseClient):
|
|||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
return await pyrogram.Dialogs._parse(self, r)
|
users = {i.id: i for i in r.users}
|
||||||
|
chats = {i.id: i for i in r.chats}
|
||||||
|
|
||||||
|
messages = {}
|
||||||
|
|
||||||
|
for message in r.messages:
|
||||||
|
to_id = message.to_id
|
||||||
|
|
||||||
|
if isinstance(to_id, types.PeerUser):
|
||||||
|
if message.out:
|
||||||
|
chat_id = to_id.user_id
|
||||||
|
else:
|
||||||
|
chat_id = message.from_id
|
||||||
|
elif isinstance(to_id, types.PeerChat):
|
||||||
|
chat_id = -to_id.chat_id
|
||||||
|
else:
|
||||||
|
chat_id = int("-100" + str(to_id.channel_id))
|
||||||
|
|
||||||
|
messages[chat_id] = await pyrogram.Message._parse(self, message, users, chats)
|
||||||
|
|
||||||
|
parsed_dialogs = []
|
||||||
|
|
||||||
|
for dialog in r.dialogs:
|
||||||
|
if not isinstance(dialog, types.Dialog):
|
||||||
|
continue
|
||||||
|
|
||||||
|
parsed_dialogs.append(pyrogram.Dialog._parse(self, dialog, messages, users, chats))
|
||||||
|
|
||||||
|
return pyrogram.List(parsed_dialogs)
|
||||||
|
@ -19,10 +19,10 @@
|
|||||||
from string import ascii_lowercase
|
from string import ascii_lowercase
|
||||||
from typing import Union, AsyncGenerator, Optional
|
from typing import Union, AsyncGenerator, Optional
|
||||||
|
|
||||||
from async_generator import async_generator, yield_
|
|
||||||
|
|
||||||
import pyrogram
|
import pyrogram
|
||||||
|
from async_generator import async_generator, yield_
|
||||||
from pyrogram.api import types
|
from pyrogram.api import types
|
||||||
|
|
||||||
from ...ext import BaseClient
|
from ...ext import BaseClient
|
||||||
|
|
||||||
|
|
||||||
@ -103,13 +103,13 @@ class IterChatMembers(BaseClient):
|
|||||||
offset = 0
|
offset = 0
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
chat_members = (await self.get_chat_members(
|
chat_members = await self.get_chat_members(
|
||||||
chat_id=chat_id,
|
chat_id=chat_id,
|
||||||
offset=offset,
|
offset=offset,
|
||||||
limit=limit,
|
limit=limit,
|
||||||
query=q,
|
query=q,
|
||||||
filter=filter
|
filter=filter
|
||||||
)).chat_members
|
)
|
||||||
|
|
||||||
if not chat_members:
|
if not chat_members:
|
||||||
break
|
break
|
||||||
|
@ -18,9 +18,9 @@
|
|||||||
|
|
||||||
from typing import AsyncGenerator, Optional
|
from typing import AsyncGenerator, Optional
|
||||||
|
|
||||||
|
import pyrogram
|
||||||
from async_generator import async_generator, yield_
|
from async_generator import async_generator, yield_
|
||||||
|
|
||||||
import pyrogram
|
|
||||||
from ...ext import BaseClient
|
from ...ext import BaseClient
|
||||||
|
|
||||||
|
|
||||||
@ -56,9 +56,9 @@ class IterDialogs(BaseClient):
|
|||||||
total = limit or (1 << 31) - 1
|
total = limit or (1 << 31) - 1
|
||||||
limit = min(100, total)
|
limit = min(100, total)
|
||||||
|
|
||||||
pinned_dialogs = (await self.get_dialogs(
|
pinned_dialogs = await self.get_dialogs(
|
||||||
pinned_only=True
|
pinned_only=True
|
||||||
)).dialogs
|
)
|
||||||
|
|
||||||
for dialog in pinned_dialogs:
|
for dialog in pinned_dialogs:
|
||||||
await yield_(dialog)
|
await yield_(dialog)
|
||||||
@ -69,10 +69,10 @@ class IterDialogs(BaseClient):
|
|||||||
return
|
return
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
dialogs = (await self.get_dialogs(
|
dialogs = await self.get_dialogs(
|
||||||
offset_date=offset_date,
|
offset_date=offset_date,
|
||||||
limit=limit
|
limit=limit
|
||||||
)).dialogs
|
)
|
||||||
|
|
||||||
if not dialogs:
|
if not dialogs:
|
||||||
return
|
return
|
||||||
|
59
pyrogram/client/methods/chats/unarchive_chats.py
Normal file
59
pyrogram/client/methods/chats/unarchive_chats.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# Pyrogram - Telegram MTProto API Client Library for Python
|
||||||
|
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
|
||||||
|
#
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from typing import Union, List
|
||||||
|
|
||||||
|
from pyrogram.api import functions, types
|
||||||
|
|
||||||
|
from ...ext import BaseClient
|
||||||
|
|
||||||
|
|
||||||
|
class UnarchiveChats(BaseClient):
|
||||||
|
async def unarchive_chats(
|
||||||
|
self,
|
||||||
|
chat_ids: Union[int, str, List[Union[int, str]]],
|
||||||
|
) -> bool:
|
||||||
|
"""Unarchive one or more chats.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
chat_ids (``int`` | ``str`` | List[``int``, ``str``]):
|
||||||
|
Unique identifier (int) or username (str) of the target chat.
|
||||||
|
You can also pass a list of ids (int) or usernames (str).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
``bool``: On success, True is returned.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RPCError: In case of a Telegram RPC error.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not isinstance(chat_ids, list):
|
||||||
|
chat_ids = [chat_ids]
|
||||||
|
|
||||||
|
await self.send(
|
||||||
|
functions.folders.EditPeerFolders(
|
||||||
|
folder_peers=[
|
||||||
|
types.InputFolderPeer(
|
||||||
|
peer=await self.resolve_peer(chat),
|
||||||
|
folder_id=0
|
||||||
|
) for chat in chat_ids
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
@ -34,6 +34,9 @@ class AddContacts(BaseClient):
|
|||||||
contacts (List of :obj:`InputPhoneContact`):
|
contacts (List of :obj:`InputPhoneContact`):
|
||||||
The contact list to be added
|
The contact list to be added
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
:obj:`types.contacts.ImportedContacts`
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
RPCError: In case of a Telegram RPC error.
|
RPCError: In case of a Telegram RPC error.
|
||||||
"""
|
"""
|
||||||
|
@ -46,5 +46,4 @@ class GetContacts(BaseClient):
|
|||||||
log.warning("get_contacts flood: waiting {} seconds".format(e.x))
|
log.warning("get_contacts flood: waiting {} seconds".format(e.x))
|
||||||
await asyncio.sleep(e.x)
|
await asyncio.sleep(e.x)
|
||||||
else:
|
else:
|
||||||
log.info("Total contacts: {}".format(len(self.peers_by_phone)))
|
return pyrogram.List(pyrogram.User._parse(self, user) for user in contacts.users)
|
||||||
return [pyrogram.User._parse(self, user) for user in contacts.users]
|
|
||||||
|
@ -18,6 +18,10 @@
|
|||||||
|
|
||||||
from .delete_messages import DeleteMessages
|
from .delete_messages import DeleteMessages
|
||||||
from .download_media import DownloadMedia
|
from .download_media import DownloadMedia
|
||||||
|
from .edit_inline_caption import EditInlineCaption
|
||||||
|
from .edit_inline_media import EditInlineMedia
|
||||||
|
from .edit_inline_reply_markup import EditInlineReplyMarkup
|
||||||
|
from .edit_inline_text import EditInlineText
|
||||||
from .edit_message_caption import EditMessageCaption
|
from .edit_message_caption import EditMessageCaption
|
||||||
from .edit_message_media import EditMessageMedia
|
from .edit_message_media import EditMessageMedia
|
||||||
from .edit_message_reply_markup import EditMessageReplyMarkup
|
from .edit_message_reply_markup import EditMessageReplyMarkup
|
||||||
@ -82,6 +86,10 @@ class Messages(
|
|||||||
SendCachedMedia,
|
SendCachedMedia,
|
||||||
GetHistoryCount,
|
GetHistoryCount,
|
||||||
SendAnimatedSticker,
|
SendAnimatedSticker,
|
||||||
ReadHistory
|
ReadHistory,
|
||||||
|
EditInlineText,
|
||||||
|
EditInlineCaption,
|
||||||
|
EditInlineMedia,
|
||||||
|
EditInlineReplyMarkup
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
@ -18,19 +18,25 @@
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import binascii
|
import binascii
|
||||||
|
import os
|
||||||
import struct
|
import struct
|
||||||
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
from threading import Event
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import pyrogram
|
import pyrogram
|
||||||
from pyrogram.client.ext import BaseClient, FileData, utils
|
from pyrogram.client.ext import BaseClient, FileData, utils
|
||||||
from pyrogram.errors import FileIdInvalid
|
from pyrogram.errors import FileIdInvalid
|
||||||
|
|
||||||
|
DEFAULT_DOWNLOAD_DIR = "downloads/"
|
||||||
|
|
||||||
|
|
||||||
class DownloadMedia(BaseClient):
|
class DownloadMedia(BaseClient):
|
||||||
async def download_media(
|
async def download_media(
|
||||||
self,
|
self,
|
||||||
message: Union["pyrogram.Message", str],
|
message: Union["pyrogram.Message", str],
|
||||||
file_name: str = "",
|
file_name: str = DEFAULT_DOWNLOAD_DIR,
|
||||||
block: bool = True,
|
block: bool = True,
|
||||||
progress: callable = None,
|
progress: callable = None,
|
||||||
progress_args: tuple = ()
|
progress_args: tuple = ()
|
||||||
@ -86,6 +92,7 @@ class DownloadMedia(BaseClient):
|
|||||||
error_message = "This message doesn't contain any downloadable media"
|
error_message = "This message doesn't contain any downloadable media"
|
||||||
available_media = ("audio", "document", "photo", "sticker", "animation", "video", "voice", "video_note")
|
available_media = ("audio", "document", "photo", "sticker", "animation", "video", "voice", "video_note")
|
||||||
|
|
||||||
|
media_file_name = None
|
||||||
file_size = None
|
file_size = None
|
||||||
mime_type = None
|
mime_type = None
|
||||||
date = None
|
date = None
|
||||||
@ -105,13 +112,13 @@ class DownloadMedia(BaseClient):
|
|||||||
file_id_str = media
|
file_id_str = media
|
||||||
else:
|
else:
|
||||||
file_id_str = media.file_id
|
file_id_str = media.file_id
|
||||||
file_name = getattr(media, "file_name", "")
|
media_file_name = getattr(media, "file_name", "")
|
||||||
file_size = getattr(media, "file_size", None)
|
file_size = getattr(media, "file_size", None)
|
||||||
mime_type = getattr(media, "mime_type", None)
|
mime_type = getattr(media, "mime_type", None)
|
||||||
date = getattr(media, "date", None)
|
date = getattr(media, "date", None)
|
||||||
|
|
||||||
data = FileData(
|
data = FileData(
|
||||||
file_name=file_name,
|
file_name=media_file_name,
|
||||||
file_size=file_size,
|
file_size=file_size,
|
||||||
mime_type=mime_type,
|
mime_type=mime_type,
|
||||||
date=date
|
date=date
|
||||||
@ -168,7 +175,40 @@ class DownloadMedia(BaseClient):
|
|||||||
done = asyncio.Event()
|
done = asyncio.Event()
|
||||||
path = [None]
|
path = [None]
|
||||||
|
|
||||||
self.download_queue.put_nowait((data, file_name, done, progress, progress_args, path))
|
directory, file_name = os.path.split(file_name)
|
||||||
|
file_name = file_name or data.file_name or ""
|
||||||
|
|
||||||
|
if not os.path.isabs(file_name):
|
||||||
|
directory = self.PARENT_DIR / (directory or DEFAULT_DOWNLOAD_DIR)
|
||||||
|
|
||||||
|
media_type_str = self.MEDIA_TYPE_ID[data.media_type]
|
||||||
|
|
||||||
|
if not file_name:
|
||||||
|
guessed_extension = self.guess_extension(data.mime_type)
|
||||||
|
|
||||||
|
if data.media_type in (0, 1, 2, 14):
|
||||||
|
extension = ".jpg"
|
||||||
|
elif data.media_type == 3:
|
||||||
|
extension = guessed_extension or ".ogg"
|
||||||
|
elif data.media_type in (4, 10, 13):
|
||||||
|
extension = guessed_extension or ".mp4"
|
||||||
|
elif data.media_type == 5:
|
||||||
|
extension = guessed_extension or ".zip"
|
||||||
|
elif data.media_type == 8:
|
||||||
|
extension = guessed_extension or ".webp"
|
||||||
|
elif data.media_type == 9:
|
||||||
|
extension = guessed_extension or ".mp3"
|
||||||
|
else:
|
||||||
|
extension = ".unknown"
|
||||||
|
|
||||||
|
file_name = "{}_{}_{}{}".format(
|
||||||
|
media_type_str,
|
||||||
|
datetime.fromtimestamp(data.date or time.time()).strftime("%Y-%m-%d_%H-%M-%S"),
|
||||||
|
self.rnd_id(),
|
||||||
|
extension
|
||||||
|
)
|
||||||
|
|
||||||
|
self.download_queue.put_nowait((data, directory, file_name, done, progress, progress_args, path))
|
||||||
|
|
||||||
if block:
|
if block:
|
||||||
await done.wait()
|
await done.wait()
|
||||||
|
58
pyrogram/client/methods/messages/edit_inline_caption.py
Normal file
58
pyrogram/client/methods/messages/edit_inline_caption.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# Pyrogram - Telegram MTProto API Client Library for Python
|
||||||
|
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
|
||||||
|
#
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import pyrogram
|
||||||
|
from pyrogram.client.ext import BaseClient
|
||||||
|
|
||||||
|
|
||||||
|
class EditInlineCaption(BaseClient):
|
||||||
|
async def edit_inline_caption(
|
||||||
|
self,
|
||||||
|
inline_message_id: str,
|
||||||
|
caption: str,
|
||||||
|
parse_mode: str = "",
|
||||||
|
reply_markup: "pyrogram.InlineKeyboardMarkup" = None
|
||||||
|
) -> bool:
|
||||||
|
"""Edit the caption of **inline** media messages.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
inline_message_id (``str``):
|
||||||
|
Identifier of the inline message.
|
||||||
|
|
||||||
|
caption (``str``):
|
||||||
|
New caption of the media message.
|
||||||
|
|
||||||
|
parse_mode (``str``, *optional*):
|
||||||
|
Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline
|
||||||
|
URLs in your message. Defaults to "markdown".
|
||||||
|
|
||||||
|
reply_markup (:obj:`InlineKeyboardMarkup`, *optional*):
|
||||||
|
An InlineKeyboardMarkup object.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
``bool``: On success, True is returned.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RPCError: In case of a Telegram RPC error.
|
||||||
|
"""
|
||||||
|
return await self.edit_inline_text(
|
||||||
|
inline_message_id=inline_message_id,
|
||||||
|
text=caption,
|
||||||
|
parse_mode=parse_mode,
|
||||||
|
reply_markup=reply_markup
|
||||||
|
)
|
104
pyrogram/client/methods/messages/edit_inline_media.py
Normal file
104
pyrogram/client/methods/messages/edit_inline_media.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
# Pyrogram - Telegram MTProto API Client Library for Python
|
||||||
|
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
|
||||||
|
#
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import pyrogram
|
||||||
|
from pyrogram.api import functions, types
|
||||||
|
from pyrogram.client.ext import BaseClient, utils
|
||||||
|
from pyrogram.client.types import (
|
||||||
|
InputMediaPhoto, InputMediaVideo, InputMediaAudio,
|
||||||
|
InputMediaAnimation, InputMediaDocument
|
||||||
|
)
|
||||||
|
from pyrogram.client.types.input_media import InputMedia
|
||||||
|
|
||||||
|
|
||||||
|
class EditInlineMedia(BaseClient):
|
||||||
|
async def edit_inline_media(
|
||||||
|
self,
|
||||||
|
inline_message_id: str,
|
||||||
|
media: InputMedia,
|
||||||
|
reply_markup: "pyrogram.InlineKeyboardMarkup" = None
|
||||||
|
) -> bool:
|
||||||
|
"""Edit **inline** animation, audio, document, photo or video messages.
|
||||||
|
|
||||||
|
When the inline message is edited, a new file can't be uploaded. Use a previously uploaded file via its file_id
|
||||||
|
or specify a URL.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
inline_message_id (``str``):
|
||||||
|
Required if *chat_id* and *message_id* are not specified.
|
||||||
|
Identifier of the inline message.
|
||||||
|
|
||||||
|
media (:obj:`InputMedia`):
|
||||||
|
One of the InputMedia objects describing an animation, audio, document, photo or video.
|
||||||
|
|
||||||
|
reply_markup (:obj:`InlineKeyboardMarkup`, *optional*):
|
||||||
|
An InlineKeyboardMarkup object.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
``bool``: On success, True is returned.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RPCError: In case of a Telegram RPC error.
|
||||||
|
"""
|
||||||
|
style = self.html if media.parse_mode.lower() == "html" else self.markdown
|
||||||
|
caption = media.caption
|
||||||
|
|
||||||
|
if isinstance(media, InputMediaPhoto):
|
||||||
|
if media.media.startswith("http"):
|
||||||
|
media = types.InputMediaPhotoExternal(
|
||||||
|
url=media.media
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
media = utils.get_input_media_from_file_id(media.media, 2)
|
||||||
|
elif isinstance(media, InputMediaVideo):
|
||||||
|
if media.media.startswith("http"):
|
||||||
|
media = types.InputMediaDocumentExternal(
|
||||||
|
url=media.media
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
media = utils.get_input_media_from_file_id(media.media, 4)
|
||||||
|
elif isinstance(media, InputMediaAudio):
|
||||||
|
if media.media.startswith("http"):
|
||||||
|
media = types.InputMediaDocumentExternal(
|
||||||
|
url=media.media
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
media = utils.get_input_media_from_file_id(media.media, 9)
|
||||||
|
elif isinstance(media, InputMediaAnimation):
|
||||||
|
if media.media.startswith("http"):
|
||||||
|
media = types.InputMediaDocumentExternal(
|
||||||
|
url=media.media
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
media = utils.get_input_media_from_file_id(media.media, 10)
|
||||||
|
elif isinstance(media, InputMediaDocument):
|
||||||
|
if media.media.startswith("http"):
|
||||||
|
media = types.InputMediaDocumentExternal(
|
||||||
|
url=media.media
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
media = utils.get_input_media_from_file_id(media.media, 5)
|
||||||
|
|
||||||
|
return await self.send(
|
||||||
|
functions.messages.EditInlineBotMessage(
|
||||||
|
id=utils.unpack_inline_message_id(inline_message_id),
|
||||||
|
media=media,
|
||||||
|
reply_markup=reply_markup.write() if reply_markup else None,
|
||||||
|
**style.parse(caption)
|
||||||
|
)
|
||||||
|
)
|
50
pyrogram/client/methods/messages/edit_inline_reply_markup.py
Normal file
50
pyrogram/client/methods/messages/edit_inline_reply_markup.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# Pyrogram - Telegram MTProto API Client Library for Python
|
||||||
|
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
|
||||||
|
#
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import pyrogram
|
||||||
|
from pyrogram.api import functions
|
||||||
|
from pyrogram.client.ext import BaseClient, utils
|
||||||
|
|
||||||
|
|
||||||
|
class EditInlineReplyMarkup(BaseClient):
|
||||||
|
async def edit_inline_reply_markup(
|
||||||
|
self,
|
||||||
|
inline_message_id: str,
|
||||||
|
reply_markup: "pyrogram.InlineKeyboardMarkup" = None
|
||||||
|
) -> bool:
|
||||||
|
"""Edit only the reply markup of **inline** messages sent via the bot (for inline bots).
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
inline_message_id (``str``):
|
||||||
|
Identifier of the inline message.
|
||||||
|
|
||||||
|
reply_markup (:obj:`InlineKeyboardMarkup`, *optional*):
|
||||||
|
An InlineKeyboardMarkup object.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
``bool``: On success, True is returned.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RPCError: In case of a Telegram RPC error.
|
||||||
|
"""
|
||||||
|
return await self.send(
|
||||||
|
functions.messages.EditInlineBotMessage(
|
||||||
|
id=utils.unpack_inline_message_id(inline_message_id),
|
||||||
|
reply_markup=reply_markup.write() if reply_markup else None,
|
||||||
|
)
|
||||||
|
)
|
67
pyrogram/client/methods/messages/edit_inline_text.py
Normal file
67
pyrogram/client/methods/messages/edit_inline_text.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
# Pyrogram - Telegram MTProto API Client Library for Python
|
||||||
|
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
|
||||||
|
#
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import pyrogram
|
||||||
|
from pyrogram.api import functions
|
||||||
|
from pyrogram.client.ext import BaseClient, utils
|
||||||
|
|
||||||
|
|
||||||
|
class EditInlineText(BaseClient):
|
||||||
|
async def edit_inline_text(
|
||||||
|
self,
|
||||||
|
inline_message_id: str,
|
||||||
|
text: str,
|
||||||
|
parse_mode: str = "",
|
||||||
|
disable_web_page_preview: bool = None,
|
||||||
|
reply_markup: "pyrogram.InlineKeyboardMarkup" = None
|
||||||
|
) -> bool:
|
||||||
|
"""Edit the text of **inline** messages.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
inline_message_id (``str``):
|
||||||
|
Identifier of the inline message.
|
||||||
|
|
||||||
|
text (``str``):
|
||||||
|
New text of the message.
|
||||||
|
|
||||||
|
parse_mode (``str``, *optional*):
|
||||||
|
Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline
|
||||||
|
URLs in your message. Defaults to "markdown".
|
||||||
|
|
||||||
|
disable_web_page_preview (``bool``, *optional*):
|
||||||
|
Disables link previews for links in this message.
|
||||||
|
|
||||||
|
reply_markup (:obj:`InlineKeyboardMarkup`, *optional*):
|
||||||
|
An InlineKeyboardMarkup object.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
``bool``: On success, True is returned.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RPCError: In case of a Telegram RPC error.
|
||||||
|
"""
|
||||||
|
style = self.html if parse_mode.lower() == "html" else self.markdown
|
||||||
|
|
||||||
|
return await self.send(
|
||||||
|
functions.messages.EditInlineBotMessage(
|
||||||
|
id=utils.unpack_inline_message_id(inline_message_id),
|
||||||
|
no_webpage=disable_web_page_preview or None,
|
||||||
|
reply_markup=reply_markup.write() if reply_markup else None,
|
||||||
|
**style.parse(text)
|
||||||
|
)
|
||||||
|
)
|
@ -19,7 +19,6 @@
|
|||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import pyrogram
|
import pyrogram
|
||||||
from pyrogram.api import functions, types
|
|
||||||
from pyrogram.client.ext import BaseClient
|
from pyrogram.client.ext import BaseClient
|
||||||
|
|
||||||
|
|
||||||
@ -32,7 +31,7 @@ class EditMessageCaption(BaseClient):
|
|||||||
parse_mode: str = "",
|
parse_mode: str = "",
|
||||||
reply_markup: "pyrogram.InlineKeyboardMarkup" = None
|
reply_markup: "pyrogram.InlineKeyboardMarkup" = None
|
||||||
) -> "pyrogram.Message":
|
) -> "pyrogram.Message":
|
||||||
"""Edit captions of messages.
|
"""Edit the caption of media messages.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
chat_id (``int`` | ``str``):
|
chat_id (``int`` | ``str``):
|
||||||
@ -44,11 +43,11 @@ class EditMessageCaption(BaseClient):
|
|||||||
Message identifier in the chat specified in chat_id.
|
Message identifier in the chat specified in chat_id.
|
||||||
|
|
||||||
caption (``str``):
|
caption (``str``):
|
||||||
New caption of the message.
|
New caption of the media message.
|
||||||
|
|
||||||
parse_mode (``str``, *optional*):
|
parse_mode (``str``, *optional*):
|
||||||
Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline
|
Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline
|
||||||
URLs in your caption. Defaults to "markdown".
|
URLs in your message. Defaults to "markdown".
|
||||||
|
|
||||||
reply_markup (:obj:`InlineKeyboardMarkup`, *optional*):
|
reply_markup (:obj:`InlineKeyboardMarkup`, *optional*):
|
||||||
An InlineKeyboardMarkup object.
|
An InlineKeyboardMarkup object.
|
||||||
@ -59,21 +58,10 @@ class EditMessageCaption(BaseClient):
|
|||||||
Raises:
|
Raises:
|
||||||
RPCError: In case of a Telegram RPC error.
|
RPCError: In case of a Telegram RPC error.
|
||||||
"""
|
"""
|
||||||
style = self.html if parse_mode.lower() == "html" else self.markdown
|
return await self.edit_message_text(
|
||||||
|
chat_id=chat_id,
|
||||||
r = await self.send(
|
message_id=message_id,
|
||||||
functions.messages.EditMessage(
|
text=caption,
|
||||||
peer=await self.resolve_peer(chat_id),
|
parse_mode=parse_mode,
|
||||||
id=message_id,
|
reply_markup=reply_markup
|
||||||
reply_markup=reply_markup.write() if reply_markup else None,
|
|
||||||
**await style.parse(caption)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for i in r.updates:
|
|
||||||
if isinstance(i, (types.UpdateEditMessage, types.UpdateEditChannelMessage)):
|
|
||||||
return await pyrogram.Message._parse(
|
|
||||||
self, i.message,
|
|
||||||
{i.id: i for i in r.users},
|
|
||||||
{i.id: i for i in r.chats}
|
|
||||||
)
|
|
||||||
|
@ -37,12 +37,10 @@ class EditMessageMedia(BaseClient):
|
|||||||
media: InputMedia,
|
media: InputMedia,
|
||||||
reply_markup: "pyrogram.InlineKeyboardMarkup" = None
|
reply_markup: "pyrogram.InlineKeyboardMarkup" = None
|
||||||
) -> "pyrogram.Message":
|
) -> "pyrogram.Message":
|
||||||
"""Edit audio, document, photo, or video messages.
|
"""Edit animation, audio, document, photo or video messages.
|
||||||
|
|
||||||
If a message is a part of a message album, then it can be edited only to a photo or a video. Otherwise,
|
If a message is a part of a message album, then it can be edited only to a photo or a video. Otherwise, the
|
||||||
message type can be changed arbitrarily. When inline message is edited, new file can't be uploaded.
|
message type can be changed arbitrarily.
|
||||||
Use previously uploaded file via its file_id or specify a URL. On success, if the edited message was sent
|
|
||||||
by the bot, the edited Message is returned, otherwise True is returned.
|
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
chat_id (``int`` | ``str``):
|
chat_id (``int`` | ``str``):
|
||||||
@ -53,7 +51,7 @@ class EditMessageMedia(BaseClient):
|
|||||||
message_id (``int``):
|
message_id (``int``):
|
||||||
Message identifier in the chat specified in chat_id.
|
Message identifier in the chat specified in chat_id.
|
||||||
|
|
||||||
media (:obj:`InputMedia`)
|
media (:obj:`InputMedia`):
|
||||||
One of the InputMedia objects describing an animation, audio, document, photo or video.
|
One of the InputMedia objects describing an animation, audio, document, photo or video.
|
||||||
|
|
||||||
reply_markup (:obj:`InlineKeyboardMarkup`, *optional*):
|
reply_markup (:obj:`InlineKeyboardMarkup`, *optional*):
|
||||||
@ -92,8 +90,7 @@ class EditMessageMedia(BaseClient):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
media = utils.get_input_media_from_file_id(media.media, 2)
|
media = utils.get_input_media_from_file_id(media.media, 2)
|
||||||
|
elif isinstance(media, InputMediaVideo):
|
||||||
if isinstance(media, InputMediaVideo):
|
|
||||||
if os.path.exists(media.media):
|
if os.path.exists(media.media):
|
||||||
media = await self.send(
|
media = await self.send(
|
||||||
functions.messages.UploadMedia(
|
functions.messages.UploadMedia(
|
||||||
@ -130,8 +127,7 @@ class EditMessageMedia(BaseClient):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
media = utils.get_input_media_from_file_id(media.media, 4)
|
media = utils.get_input_media_from_file_id(media.media, 4)
|
||||||
|
elif isinstance(media, InputMediaAudio):
|
||||||
if isinstance(media, InputMediaAudio):
|
|
||||||
if os.path.exists(media.media):
|
if os.path.exists(media.media):
|
||||||
media = await self.send(
|
media = await self.send(
|
||||||
functions.messages.UploadMedia(
|
functions.messages.UploadMedia(
|
||||||
@ -167,8 +163,7 @@ class EditMessageMedia(BaseClient):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
media = utils.get_input_media_from_file_id(media.media, 9)
|
media = utils.get_input_media_from_file_id(media.media, 9)
|
||||||
|
elif isinstance(media, InputMediaAnimation):
|
||||||
if isinstance(media, InputMediaAnimation):
|
|
||||||
if os.path.exists(media.media):
|
if os.path.exists(media.media):
|
||||||
media = await self.send(
|
media = await self.send(
|
||||||
functions.messages.UploadMedia(
|
functions.messages.UploadMedia(
|
||||||
@ -206,8 +201,7 @@ class EditMessageMedia(BaseClient):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
media = utils.get_input_media_from_file_id(media.media, 10)
|
media = utils.get_input_media_from_file_id(media.media, 10)
|
||||||
|
elif isinstance(media, InputMediaDocument):
|
||||||
if isinstance(media, InputMediaDocument):
|
|
||||||
if os.path.exists(media.media):
|
if os.path.exists(media.media):
|
||||||
media = await self.send(
|
media = await self.send(
|
||||||
functions.messages.UploadMedia(
|
functions.messages.UploadMedia(
|
||||||
@ -243,8 +237,8 @@ class EditMessageMedia(BaseClient):
|
|||||||
functions.messages.EditMessage(
|
functions.messages.EditMessage(
|
||||||
peer=await self.resolve_peer(chat_id),
|
peer=await self.resolve_peer(chat_id),
|
||||||
id=message_id,
|
id=message_id,
|
||||||
reply_markup=reply_markup.write() if reply_markup else None,
|
|
||||||
media=media,
|
media=media,
|
||||||
|
reply_markup=reply_markup.write() if reply_markup else None,
|
||||||
**await style.parse(caption)
|
**await style.parse(caption)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -28,9 +28,9 @@ class EditMessageReplyMarkup(BaseClient):
|
|||||||
self,
|
self,
|
||||||
chat_id: Union[int, str],
|
chat_id: Union[int, str],
|
||||||
message_id: int,
|
message_id: int,
|
||||||
reply_markup: "pyrogram.InlineKeyboardMarkup" = None
|
reply_markup: "pyrogram.InlineKeyboardMarkup" = None,
|
||||||
) -> "pyrogram.Message":
|
) -> "pyrogram.Message":
|
||||||
"""Edit only the reply markup of messages sent by the bot or via the bot (for inline bots).
|
"""Edit only the reply markup of messages sent by the bot.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
chat_id (``int`` | ``str``):
|
chat_id (``int`` | ``str``):
|
||||||
@ -45,18 +45,16 @@ class EditMessageReplyMarkup(BaseClient):
|
|||||||
An InlineKeyboardMarkup object.
|
An InlineKeyboardMarkup object.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
:obj:`Message` | ``bool``: In case the edited message is sent by the bot, the edited message is returned,
|
:obj:`Message`: On success, the edited message is returned.
|
||||||
otherwise, True is returned in case the edited message is send by the user.
|
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
RPCError: In case of a Telegram RPC error.
|
RPCError: In case of a Telegram RPC error.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
r = await self.send(
|
r = await self.send(
|
||||||
functions.messages.EditMessage(
|
functions.messages.EditMessage(
|
||||||
peer=await self.resolve_peer(chat_id),
|
peer=await self.resolve_peer(chat_id),
|
||||||
id=message_id,
|
id=message_id,
|
||||||
reply_markup=reply_markup.write() if reply_markup else None
|
reply_markup=reply_markup.write() if reply_markup else None,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ class EditMessageText(BaseClient):
|
|||||||
disable_web_page_preview: bool = None,
|
disable_web_page_preview: bool = None,
|
||||||
reply_markup: "pyrogram.InlineKeyboardMarkup" = None
|
reply_markup: "pyrogram.InlineKeyboardMarkup" = None
|
||||||
) -> "pyrogram.Message":
|
) -> "pyrogram.Message":
|
||||||
"""Edit text messages.
|
"""Edit the text of messages.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
chat_id (``int`` | ``str``):
|
chat_id (``int`` | ``str``):
|
||||||
|
@ -16,10 +16,11 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from typing import Union, Iterable
|
from typing import Union, Iterable, List
|
||||||
|
|
||||||
import pyrogram
|
import pyrogram
|
||||||
from pyrogram.api import functions, types
|
from pyrogram.api import functions, types
|
||||||
|
|
||||||
from ...ext import BaseClient
|
from ...ext import BaseClient
|
||||||
|
|
||||||
|
|
||||||
@ -32,7 +33,7 @@ class ForwardMessages(BaseClient):
|
|||||||
disable_notification: bool = None,
|
disable_notification: bool = None,
|
||||||
as_copy: bool = False,
|
as_copy: bool = False,
|
||||||
remove_caption: bool = False
|
remove_caption: bool = False
|
||||||
) -> "pyrogram.Messages":
|
) -> List["pyrogram.Message"]:
|
||||||
"""Forward messages of any kind.
|
"""Forward messages of any kind.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
@ -64,9 +65,9 @@ class ForwardMessages(BaseClient):
|
|||||||
Defaults to False.
|
Defaults to False.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
:obj:`Message` | :obj:`Messages`: In case *message_ids* was an integer, the single forwarded message is
|
:obj:`Message` | List of :obj:`Message`: In case *message_ids* was an integer, the single forwarded message
|
||||||
returned, otherwise, in case *message_ids* was an iterable, the returned value will be an object containing
|
is returned, otherwise, in case *message_ids* was an iterable, the returned value will be a list of
|
||||||
a list of messages, even if such iterable contained just a single element.
|
messages, even if such iterable contained just a single element.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
RPCError: In case of a Telegram RPC error.
|
RPCError: In case of a Telegram RPC error.
|
||||||
@ -79,9 +80,9 @@ class ForwardMessages(BaseClient):
|
|||||||
forwarded_messages = []
|
forwarded_messages = []
|
||||||
|
|
||||||
for chunk in [message_ids[i:i + 200] for i in range(0, len(message_ids), 200)]:
|
for chunk in [message_ids[i:i + 200] for i in range(0, len(message_ids), 200)]:
|
||||||
messages = await self.get_messages(chat_id=from_chat_id, message_ids=chunk) # type: pyrogram.Messages
|
messages = await self.get_messages(chat_id=from_chat_id, message_ids=chunk)
|
||||||
|
|
||||||
for message in messages.messages:
|
for message in messages:
|
||||||
forwarded_messages.append(
|
forwarded_messages.append(
|
||||||
await message.forward(
|
await message.forward(
|
||||||
chat_id,
|
chat_id,
|
||||||
@ -91,11 +92,7 @@ class ForwardMessages(BaseClient):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return pyrogram.Messages(
|
return pyrogram.List(forwarded_messages) if is_iterable else forwarded_messages[0]
|
||||||
client=self,
|
|
||||||
total_count=len(forwarded_messages),
|
|
||||||
messages=forwarded_messages
|
|
||||||
) if is_iterable else forwarded_messages[0]
|
|
||||||
else:
|
else:
|
||||||
r = await self.send(
|
r = await self.send(
|
||||||
functions.messages.ForwardMessages(
|
functions.messages.ForwardMessages(
|
||||||
@ -116,13 +113,9 @@ class ForwardMessages(BaseClient):
|
|||||||
if isinstance(i, (types.UpdateNewMessage, types.UpdateNewChannelMessage)):
|
if isinstance(i, (types.UpdateNewMessage, types.UpdateNewChannelMessage)):
|
||||||
forwarded_messages.append(
|
forwarded_messages.append(
|
||||||
await pyrogram.Message._parse(
|
await pyrogram.Message._parse(
|
||||||
self, i.message,
|
self, i.message,
|
||||||
users, chats
|
users, chats
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
return pyrogram.Messages(
|
return pyrogram.List(forwarded_messages) if is_iterable else forwarded_messages[0]
|
||||||
client=self,
|
|
||||||
total_count=len(forwarded_messages),
|
|
||||||
messages=forwarded_messages
|
|
||||||
) if is_iterable else forwarded_messages[0]
|
|
||||||
|
@ -18,11 +18,13 @@
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from typing import Union
|
from typing import Union, List
|
||||||
|
|
||||||
import pyrogram
|
import pyrogram
|
||||||
from pyrogram.api import functions
|
from pyrogram.api import functions
|
||||||
|
from pyrogram.client.ext import utils
|
||||||
from pyrogram.errors import FloodWait
|
from pyrogram.errors import FloodWait
|
||||||
|
|
||||||
from ...ext import BaseClient
|
from ...ext import BaseClient
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -37,7 +39,7 @@ class GetHistory(BaseClient):
|
|||||||
offset_id: int = 0,
|
offset_id: int = 0,
|
||||||
offset_date: int = 0,
|
offset_date: int = 0,
|
||||||
reverse: bool = False
|
reverse: bool = False
|
||||||
) -> "pyrogram.Messages":
|
) -> List["pyrogram.Message"]:
|
||||||
"""Retrieve a chunk of the history of a chat.
|
"""Retrieve a chunk of the history of a chat.
|
||||||
|
|
||||||
You can get up to 100 messages at once.
|
You can get up to 100 messages at once.
|
||||||
@ -67,15 +69,17 @@ class GetHistory(BaseClient):
|
|||||||
Pass True to retrieve the messages in reversed order (from older to most recent).
|
Pass True to retrieve the messages in reversed order (from older to most recent).
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
:obj:`Messages` - On success, an object containing a list of the retrieved messages.
|
List of :obj:`Message` - On success, a list of the retrieved messages is returned.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
RPCError: In case of a Telegram RPC error.
|
RPCError: In case of a Telegram RPC error.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
offset_id = offset_id or (1 if reverse else 0)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
messages = await pyrogram.Messages._parse(
|
messages = await utils.parse_messages(
|
||||||
self,
|
self,
|
||||||
await self.send(
|
await self.send(
|
||||||
functions.messages.GetHistory(
|
functions.messages.GetHistory(
|
||||||
@ -97,6 +101,6 @@ class GetHistory(BaseClient):
|
|||||||
break
|
break
|
||||||
|
|
||||||
if reverse:
|
if reverse:
|
||||||
messages.messages.reverse()
|
messages.reverse()
|
||||||
|
|
||||||
return messages
|
return messages
|
||||||
|
@ -17,12 +17,10 @@
|
|||||||
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import time
|
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
from pyrogram.api import types, functions
|
from pyrogram.api import types, functions
|
||||||
from pyrogram.client.ext import BaseClient
|
from pyrogram.client.ext import BaseClient
|
||||||
from pyrogram.errors import FloodWait
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -51,34 +49,20 @@ class GetHistoryCount(BaseClient):
|
|||||||
RPCError: In case of a Telegram RPC error.
|
RPCError: In case of a Telegram RPC error.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
peer = await self.resolve_peer(chat_id)
|
r = await self.send(
|
||||||
|
functions.messages.GetHistory(
|
||||||
|
peer=await self.resolve_peer(chat_id),
|
||||||
|
offset_id=0,
|
||||||
|
offset_date=0,
|
||||||
|
add_offset=0,
|
||||||
|
limit=1,
|
||||||
|
max_id=0,
|
||||||
|
min_id=0,
|
||||||
|
hash=0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if not isinstance(peer, types.InputPeerChannel):
|
if isinstance(r, types.messages.Messages):
|
||||||
offset = 0
|
return len(r.messages)
|
||||||
limit = 100
|
else:
|
||||||
|
return r.count
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
r = await self.send(
|
|
||||||
functions.messages.GetHistory(
|
|
||||||
peer=peer,
|
|
||||||
offset_id=1,
|
|
||||||
offset_date=0,
|
|
||||||
add_offset=-offset - limit,
|
|
||||||
limit=limit,
|
|
||||||
max_id=0,
|
|
||||||
min_id=0,
|
|
||||||
hash=0
|
|
||||||
)
|
|
||||||
)
|
|
||||||
except FloodWait as e:
|
|
||||||
log.warning("Sleeping for {}s".format(e.x))
|
|
||||||
time.sleep(e.x)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not r.messages:
|
|
||||||
return offset
|
|
||||||
|
|
||||||
offset += len(r.messages)
|
|
||||||
|
|
||||||
return (await self.get_history(chat_id=chat_id, limit=1)).total_count
|
|
||||||
|
@ -18,12 +18,13 @@
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from typing import Union, Iterable
|
from typing import Union, Iterable, List
|
||||||
|
|
||||||
import pyrogram
|
import pyrogram
|
||||||
from pyrogram.api import functions, types
|
from pyrogram.api import functions, types
|
||||||
from pyrogram.errors import FloodWait
|
from pyrogram.errors import FloodWait
|
||||||
from ...ext import BaseClient
|
|
||||||
|
from ...ext import BaseClient, utils
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -35,7 +36,7 @@ class GetMessages(BaseClient):
|
|||||||
message_ids: Union[int, Iterable[int]] = None,
|
message_ids: Union[int, Iterable[int]] = None,
|
||||||
reply_to_message_ids: Union[int, Iterable[int]] = None,
|
reply_to_message_ids: Union[int, Iterable[int]] = None,
|
||||||
replies: int = 1
|
replies: int = 1
|
||||||
) -> Union["pyrogram.Message", "pyrogram.Messages"]:
|
) -> Union["pyrogram.Message", List["pyrogram.Message"]]:
|
||||||
"""Get one or more messages that belong to a specific chat.
|
"""Get one or more messages that belong to a specific chat.
|
||||||
You can retrieve up to 200 messages at once.
|
You can retrieve up to 200 messages at once.
|
||||||
|
|
||||||
@ -60,9 +61,9 @@ class GetMessages(BaseClient):
|
|||||||
Defaults to 1.
|
Defaults to 1.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
:obj:`Message` | :obj:`Messages`: In case *message_ids* was an integer, the single requested message is
|
:obj:`Message` | List of :obj:`Message`: In case *message_ids* was an integer, the single requested message is
|
||||||
returned, otherwise, in case *message_ids* was an iterable, the returned value will be an object containing
|
returned, otherwise, in case *message_ids* was an iterable, the returned value will be a list of messages,
|
||||||
a list of messages, even if such iterable contained just a single element.
|
even if such iterable contained just a single element.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
RPCError: In case of a Telegram RPC error.
|
RPCError: In case of a Telegram RPC error.
|
||||||
@ -99,6 +100,6 @@ class GetMessages(BaseClient):
|
|||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
messages = await pyrogram.Messages._parse(self, r, replies)
|
messages = await utils.parse_messages(self, r, replies=replies)
|
||||||
|
|
||||||
return messages if is_iterable else messages.messages[0]
|
return messages if is_iterable else messages[0]
|
||||||
|
@ -18,9 +18,9 @@
|
|||||||
|
|
||||||
from typing import Union, Optional, AsyncGenerator
|
from typing import Union, Optional, AsyncGenerator
|
||||||
|
|
||||||
|
import pyrogram
|
||||||
from async_generator import async_generator, yield_
|
from async_generator import async_generator, yield_
|
||||||
|
|
||||||
import pyrogram
|
|
||||||
from ...ext import BaseClient
|
from ...ext import BaseClient
|
||||||
|
|
||||||
|
|
||||||
@ -76,14 +76,14 @@ class IterHistory(BaseClient):
|
|||||||
limit = min(100, total)
|
limit = min(100, total)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
messages = (await self.get_history(
|
messages = await self.get_history(
|
||||||
chat_id=chat_id,
|
chat_id=chat_id,
|
||||||
limit=limit,
|
limit=limit,
|
||||||
offset=offset,
|
offset=offset,
|
||||||
offset_id=offset_id,
|
offset_id=offset_id,
|
||||||
offset_date=offset_date,
|
offset_date=offset_date,
|
||||||
reverse=reverse
|
reverse=reverse
|
||||||
)).messages
|
)
|
||||||
|
|
||||||
if not messages:
|
if not messages:
|
||||||
return
|
return
|
||||||
|
@ -38,7 +38,7 @@ class SendMediaGroup(BaseClient):
|
|||||||
media: List[Union["pyrogram.InputMediaPhoto", "pyrogram.InputMediaVideo"]],
|
media: List[Union["pyrogram.InputMediaPhoto", "pyrogram.InputMediaVideo"]],
|
||||||
disable_notification: bool = None,
|
disable_notification: bool = None,
|
||||||
reply_to_message_id: int = None
|
reply_to_message_id: int = None
|
||||||
):
|
) -> List["pyrogram.Message"]:
|
||||||
"""Send a group of photos or videos as an album.
|
"""Send a group of photos or videos as an album.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
@ -58,7 +58,7 @@ class SendMediaGroup(BaseClient):
|
|||||||
If the message is a reply, ID of the original message.
|
If the message is a reply, ID of the original message.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
:obj:`Messages`: On success, an object is returned containing all the single messages sent.
|
List of :obj:`Message`: On success, a list of the sent messages is returned.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
RPCError: In case of a Telegram RPC error.
|
RPCError: In case of a Telegram RPC error.
|
||||||
@ -158,7 +158,7 @@ class SendMediaGroup(BaseClient):
|
|||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
return await pyrogram.Messages._parse(
|
return await utils.parse_messages(
|
||||||
self,
|
self,
|
||||||
types.messages.Messages(
|
types.messages.Messages(
|
||||||
messages=[m.message for m in filter(
|
messages=[m.message for m in filter(
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from .block_user import BlockUser
|
||||||
from .delete_profile_photos import DeleteProfilePhotos
|
from .delete_profile_photos import DeleteProfilePhotos
|
||||||
from .get_me import GetMe
|
from .get_me import GetMe
|
||||||
from .get_profile_photos import GetProfilePhotos
|
from .get_profile_photos import GetProfilePhotos
|
||||||
@ -24,10 +25,12 @@ from .get_user_dc import GetUserDC
|
|||||||
from .get_users import GetUsers
|
from .get_users import GetUsers
|
||||||
from .iter_profile_photos import IterProfilePhotos
|
from .iter_profile_photos import IterProfilePhotos
|
||||||
from .set_profile_photo import SetProfilePhoto
|
from .set_profile_photo import SetProfilePhoto
|
||||||
|
from .unblock_user import UnblockUser
|
||||||
from .update_username import UpdateUsername
|
from .update_username import UpdateUsername
|
||||||
|
|
||||||
|
|
||||||
class Users(
|
class Users(
|
||||||
|
BlockUser,
|
||||||
GetProfilePhotos,
|
GetProfilePhotos,
|
||||||
SetProfilePhoto,
|
SetProfilePhoto,
|
||||||
DeleteProfilePhotos,
|
DeleteProfilePhotos,
|
||||||
@ -36,6 +39,7 @@ class Users(
|
|||||||
UpdateUsername,
|
UpdateUsername,
|
||||||
GetProfilePhotosCount,
|
GetProfilePhotosCount,
|
||||||
GetUserDC,
|
GetUserDC,
|
||||||
IterProfilePhotos
|
IterProfilePhotos,
|
||||||
|
UnblockUser
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
45
pyrogram/client/methods/users/block_user.py
Normal file
45
pyrogram/client/methods/users/block_user.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# Pyrogram - Telegram MTProto API Client Library for Python
|
||||||
|
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
|
||||||
|
#
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
from pyrogram.api import functions
|
||||||
|
|
||||||
|
from ...ext import BaseClient
|
||||||
|
|
||||||
|
|
||||||
|
class BlockUser(BaseClient):
|
||||||
|
async def block_user(
|
||||||
|
self,
|
||||||
|
user_id: Union[int, str]
|
||||||
|
) -> bool:
|
||||||
|
"""Block a user.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
``bool``: True on success
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RPCError: In case of Telegram RPC Error.
|
||||||
|
"""
|
||||||
|
return bool(
|
||||||
|
await self.send(
|
||||||
|
functions.contact.Block(
|
||||||
|
id=await self.resolve_peer(user_id)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
@ -16,10 +16,12 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from typing import Union
|
from typing import Union, List
|
||||||
|
|
||||||
import pyrogram
|
import pyrogram
|
||||||
from pyrogram.api import functions, types
|
from pyrogram.api import functions, types
|
||||||
|
from pyrogram.client.ext import utils
|
||||||
|
|
||||||
from ...ext import BaseClient
|
from ...ext import BaseClient
|
||||||
|
|
||||||
|
|
||||||
@ -29,7 +31,7 @@ class GetProfilePhotos(BaseClient):
|
|||||||
chat_id: Union[int, str],
|
chat_id: Union[int, str],
|
||||||
offset: int = 0,
|
offset: int = 0,
|
||||||
limit: int = 100
|
limit: int = 100
|
||||||
) -> "pyrogram.ProfilePhotos":
|
) -> List["pyrogram.Photo"]:
|
||||||
"""Get a list of profile pictures for a user or a chat.
|
"""Get a list of profile pictures for a user or a chat.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
@ -47,27 +49,15 @@ class GetProfilePhotos(BaseClient):
|
|||||||
Values between 1—100 are accepted. Defaults to 100.
|
Values between 1—100 are accepted. Defaults to 100.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
:obj:`ProfilePhotos`: On success, an object containing a list of the profile photos is returned.
|
List of :obj:`Photo`: On success, a list of profile photos is returned.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
RPCError: In case of a Telegram RPC error.
|
RPCError: In case of a Telegram RPC error.
|
||||||
"""
|
"""
|
||||||
peer_id = await self.resolve_peer(chat_id)
|
peer_id = await self.resolve_peer(chat_id)
|
||||||
|
|
||||||
if isinstance(peer_id, types.InputPeerUser):
|
if isinstance(peer_id, types.InputPeerChannel):
|
||||||
return pyrogram.ProfilePhotos._parse(
|
r = await utils.parse_messages(
|
||||||
self,
|
|
||||||
await self.send(
|
|
||||||
functions.photos.GetUserPhotos(
|
|
||||||
user_id=peer_id,
|
|
||||||
offset=offset,
|
|
||||||
max_id=0,
|
|
||||||
limit=limit
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
new_chat_photos = pyrogram.Messages._parse(
|
|
||||||
self,
|
self,
|
||||||
await self.send(
|
await self.send(
|
||||||
functions.messages.Search(
|
functions.messages.Search(
|
||||||
@ -86,7 +76,15 @@ class GetProfilePhotos(BaseClient):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return pyrogram.ProfilePhotos(
|
return pyrogram.List([message.new_chat_photo for message in r][:limit])
|
||||||
total_count=new_chat_photos.total_count,
|
else:
|
||||||
profile_photos=[m.new_chat_photo for m in new_chat_photos.messages][:limit]
|
r = await self.send(
|
||||||
|
functions.photos.GetUserPhotos(
|
||||||
|
user_id=peer_id,
|
||||||
|
offset=offset,
|
||||||
|
max_id=0,
|
||||||
|
limit=limit
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return pyrogram.List(pyrogram.Photo._parse(self, photo) for photo in r.photos)
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
|
from pyrogram.api import functions, types
|
||||||
|
|
||||||
from ...ext import BaseClient
|
from ...ext import BaseClient
|
||||||
|
|
||||||
|
|
||||||
@ -38,4 +40,28 @@ class GetProfilePhotosCount(BaseClient):
|
|||||||
RPCError: In case of a Telegram RPC error.
|
RPCError: In case of a Telegram RPC error.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return await self.get_profile_photos(chat_id, limit=1).total_count
|
peer_id = await self.resolve_peer(chat_id)
|
||||||
|
|
||||||
|
if isinstance(peer_id, types.InputPeerChannel):
|
||||||
|
r = await self.send(
|
||||||
|
functions.messages.GetSearchCounters(
|
||||||
|
peer=peer_id,
|
||||||
|
filters=[types.InputMessagesFilterChatPhotos()],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return r[0].count
|
||||||
|
else:
|
||||||
|
r = await self.send(
|
||||||
|
functions.photos.GetUserPhotos(
|
||||||
|
user_id=peer_id,
|
||||||
|
offset=0,
|
||||||
|
max_id=0,
|
||||||
|
limit=1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(r, types.photos.Photos):
|
||||||
|
return len(r.photos)
|
||||||
|
else:
|
||||||
|
return r.count
|
||||||
|
@ -24,7 +24,13 @@ from ...ext import BaseClient
|
|||||||
|
|
||||||
class GetUserDC(BaseClient):
|
class GetUserDC(BaseClient):
|
||||||
async def get_user_dc(self, user_id: Union[int, str]) -> Union[int, None]:
|
async def get_user_dc(self, user_id: Union[int, str]) -> Union[int, None]:
|
||||||
"""Get the assigned data center (DC) of a user.
|
"""Get the assigned DC (data center) of a user.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This information is approximate: it is based on where Telegram stores a user profile pictures and does not
|
||||||
|
by any means tell you the user location (i.e. a user might travel far away, but will still connect to its
|
||||||
|
assigned DC). More info at `FAQs <../faq#what-are-the-ip-addresses-of-telegram-data-centers>`_.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
user_id (``int`` | ``str``):
|
user_id (``int`` | ``str``):
|
||||||
@ -33,7 +39,8 @@ class GetUserDC(BaseClient):
|
|||||||
For a contact that exists in your Telegram address book you can use his phone number (str).
|
For a contact that exists in your Telegram address book you can use his phone number (str).
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
``int`` | ``None``: The DC identifier as integer, or None in case it wasn't possible to get it.
|
``int`` | ``None``: The DC identifier as integer, or None in case it wasn't possible to get it (i.e. the
|
||||||
|
user has no profile picture or has the privacy setting enabled).
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
RPCError: In case of a Telegram RPC error.
|
RPCError: In case of a Telegram RPC error.
|
||||||
|
@ -57,7 +57,7 @@ class GetUsers(BaseClient):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
users = []
|
users = pyrogram.List()
|
||||||
|
|
||||||
for i in r:
|
for i in r:
|
||||||
users.append(pyrogram.User._parse(self, i))
|
users.append(pyrogram.User._parse(self, i))
|
||||||
|
@ -16,11 +16,11 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from typing import Union, Generator
|
from typing import Union, AsyncGenerator, Optional
|
||||||
|
|
||||||
from async_generator import async_generator, yield_
|
|
||||||
|
|
||||||
import pyrogram
|
import pyrogram
|
||||||
|
from async_generator import async_generator, yield_
|
||||||
|
|
||||||
from ...ext import BaseClient
|
from ...ext import BaseClient
|
||||||
|
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ class IterProfilePhotos(BaseClient):
|
|||||||
chat_id: Union[int, str],
|
chat_id: Union[int, str],
|
||||||
offset: int = 0,
|
offset: int = 0,
|
||||||
limit: int = 0,
|
limit: int = 0,
|
||||||
) -> Generator["pyrogram.Photo", None, None]:
|
) -> Optional[AsyncGenerator["pyrogram.Message", None]]:
|
||||||
"""Iterate through a chat or a user profile photos sequentially.
|
"""Iterate through a chat or a user profile photos sequentially.
|
||||||
|
|
||||||
This convenience method does the same as repeatedly calling :meth:`~Client.get_profile_photos` in a loop, thus
|
This convenience method does the same as repeatedly calling :meth:`~Client.get_profile_photos` in a loop, thus
|
||||||
@ -62,11 +62,11 @@ class IterProfilePhotos(BaseClient):
|
|||||||
limit = min(100, total)
|
limit = min(100, total)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
photos = self.get_profile_photos(
|
photos = await self.get_profile_photos(
|
||||||
chat_id=chat_id,
|
chat_id=chat_id,
|
||||||
offset=offset,
|
offset=offset,
|
||||||
limit=limit
|
limit=limit
|
||||||
).photos
|
)
|
||||||
|
|
||||||
if not photos:
|
if not photos:
|
||||||
return
|
return
|
||||||
|
45
pyrogram/client/methods/users/unblock_user.py
Normal file
45
pyrogram/client/methods/users/unblock_user.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# Pyrogram - Telegram MTProto API Client Library for Python
|
||||||
|
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
|
||||||
|
#
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
from pyrogram.api import functions
|
||||||
|
|
||||||
|
from ...ext import BaseClient
|
||||||
|
|
||||||
|
|
||||||
|
class UnblockUser(BaseClient):
|
||||||
|
async def unblock_user(
|
||||||
|
self,
|
||||||
|
user_id: Union[int, str]
|
||||||
|
) -> bool:
|
||||||
|
"""Unblock a user.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
``bool``: True on success
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RPCError: In case of Telegram RPC Error.
|
||||||
|
"""
|
||||||
|
return bool(
|
||||||
|
await self.send(
|
||||||
|
functions.contact.Unblock(
|
||||||
|
id=await self.resolve_peer(user_id)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
21
pyrogram/client/storage/__init__.py
Normal file
21
pyrogram/client/storage/__init__.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Pyrogram - Telegram MTProto API Client Library for Python
|
||||||
|
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
|
||||||
|
#
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from .memory_storage import MemoryStorage
|
||||||
|
from .file_storage import FileStorage
|
||||||
|
from .storage import Storage
|
110
pyrogram/client/storage/file_storage.py
Normal file
110
pyrogram/client/storage/file_storage.py
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
# Pyrogram - Telegram MTProto API Client Library for Python
|
||||||
|
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
|
||||||
|
#
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import sqlite3
|
||||||
|
from pathlib import Path
|
||||||
|
from threading import Lock
|
||||||
|
|
||||||
|
from .memory_storage import MemoryStorage
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class FileStorage(MemoryStorage):
|
||||||
|
FILE_EXTENSION = ".session"
|
||||||
|
|
||||||
|
def __init__(self, name: str, workdir: Path):
|
||||||
|
super().__init__(name)
|
||||||
|
|
||||||
|
self.workdir = workdir
|
||||||
|
self.database = workdir / (self.name + self.FILE_EXTENSION)
|
||||||
|
self.conn = None # type: sqlite3.Connection
|
||||||
|
self.lock = Lock()
|
||||||
|
|
||||||
|
# noinspection PyAttributeOutsideInit
|
||||||
|
def migrate_from_json(self, session_json: dict):
|
||||||
|
self.open()
|
||||||
|
|
||||||
|
self.dc_id = session_json["dc_id"]
|
||||||
|
self.test_mode = session_json["test_mode"]
|
||||||
|
self.auth_key = base64.b64decode("".join(session_json["auth_key"]))
|
||||||
|
self.user_id = session_json["user_id"]
|
||||||
|
self.date = session_json.get("date", 0)
|
||||||
|
self.is_bot = session_json.get("is_bot", False)
|
||||||
|
|
||||||
|
peers_by_id = session_json.get("peers_by_id", {})
|
||||||
|
peers_by_phone = session_json.get("peers_by_phone", {})
|
||||||
|
|
||||||
|
peers = {}
|
||||||
|
|
||||||
|
for k, v in peers_by_id.items():
|
||||||
|
if v is None:
|
||||||
|
type_ = "group"
|
||||||
|
elif k.startswith("-100"):
|
||||||
|
type_ = "channel"
|
||||||
|
else:
|
||||||
|
type_ = "user"
|
||||||
|
|
||||||
|
peers[int(k)] = [int(k), int(v) if v is not None else None, type_, None, None]
|
||||||
|
|
||||||
|
for k, v in peers_by_phone.items():
|
||||||
|
peers[v][4] = k
|
||||||
|
|
||||||
|
# noinspection PyTypeChecker
|
||||||
|
self.update_peers(peers.values())
|
||||||
|
|
||||||
|
def open(self):
|
||||||
|
path = self.database
|
||||||
|
file_exists = path.is_file()
|
||||||
|
|
||||||
|
if file_exists:
|
||||||
|
try:
|
||||||
|
with open(path, encoding="utf-8") as f:
|
||||||
|
session_json = json.load(f)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
log.warning("JSON session storage detected! Converting it into an SQLite session storage...")
|
||||||
|
|
||||||
|
path.rename(path.name + ".OLD")
|
||||||
|
|
||||||
|
log.warning('The old session file has been renamed to "{}.OLD"'.format(path.name))
|
||||||
|
|
||||||
|
self.migrate_from_json(session_json)
|
||||||
|
|
||||||
|
log.warning("Done! The session has been successfully converted from JSON to SQLite storage")
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
if Path(path.name + ".OLD").is_file():
|
||||||
|
log.warning('Old session file detected: "{}.OLD". You can remove this file now'.format(path.name))
|
||||||
|
|
||||||
|
self.conn = sqlite3.connect(
|
||||||
|
path,
|
||||||
|
timeout=1,
|
||||||
|
check_same_thread=False
|
||||||
|
)
|
||||||
|
|
||||||
|
if not file_exists:
|
||||||
|
self.create()
|
||||||
|
|
||||||
|
with self.conn:
|
||||||
|
self.conn.execute("VACUUM")
|
241
pyrogram/client/storage/memory_storage.py
Normal file
241
pyrogram/client/storage/memory_storage.py
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
# Pyrogram - Telegram MTProto API Client Library for Python
|
||||||
|
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
|
||||||
|
#
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import inspect
|
||||||
|
import logging
|
||||||
|
import sqlite3
|
||||||
|
import struct
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
from threading import Lock
|
||||||
|
from typing import List, Tuple
|
||||||
|
|
||||||
|
from pyrogram.api import types
|
||||||
|
from pyrogram.client.storage.storage import Storage
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class MemoryStorage(Storage):
|
||||||
|
SCHEMA_VERSION = 1
|
||||||
|
USERNAME_TTL = 8 * 60 * 60
|
||||||
|
SESSION_STRING_FMT = ">B?256sI?"
|
||||||
|
SESSION_STRING_SIZE = 351
|
||||||
|
|
||||||
|
def __init__(self, name: str):
|
||||||
|
super().__init__(name)
|
||||||
|
|
||||||
|
self.conn = None # type: sqlite3.Connection
|
||||||
|
self.lock = Lock()
|
||||||
|
|
||||||
|
def create(self):
|
||||||
|
with self.lock, self.conn:
|
||||||
|
with open(Path(__file__).parent / "schema.sql", "r") as schema:
|
||||||
|
self.conn.executescript(schema.read())
|
||||||
|
|
||||||
|
self.conn.execute(
|
||||||
|
"INSERT INTO version VALUES (?)",
|
||||||
|
(self.SCHEMA_VERSION,)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.conn.execute(
|
||||||
|
"INSERT INTO sessions VALUES (?, ?, ?, ?, ?, ?)",
|
||||||
|
(1, None, None, 0, None, None)
|
||||||
|
)
|
||||||
|
|
||||||
|
def _import_session_string(self, string_session: str):
|
||||||
|
decoded = base64.urlsafe_b64decode(string_session + "=" * (-len(string_session) % 4))
|
||||||
|
return struct.unpack(self.SESSION_STRING_FMT, decoded)
|
||||||
|
|
||||||
|
def export_session_string(self):
|
||||||
|
packed = struct.pack(
|
||||||
|
self.SESSION_STRING_FMT,
|
||||||
|
self.dc_id,
|
||||||
|
self.test_mode,
|
||||||
|
self.auth_key,
|
||||||
|
self.user_id,
|
||||||
|
self.is_bot
|
||||||
|
)
|
||||||
|
|
||||||
|
return base64.urlsafe_b64encode(packed).decode().rstrip("=")
|
||||||
|
|
||||||
|
# noinspection PyAttributeOutsideInit
|
||||||
|
def open(self):
|
||||||
|
self.conn = sqlite3.connect(":memory:", check_same_thread=False)
|
||||||
|
self.create()
|
||||||
|
|
||||||
|
if self.name != ":memory:":
|
||||||
|
imported_session_string = self._import_session_string(self.name)
|
||||||
|
|
||||||
|
self.dc_id, self.test_mode, self.auth_key, self.user_id, self.is_bot = imported_session_string
|
||||||
|
self.date = 0
|
||||||
|
|
||||||
|
self.name = ":memory:" + str(self.user_id or "<unknown>")
|
||||||
|
|
||||||
|
# noinspection PyAttributeOutsideInit
|
||||||
|
def save(self):
|
||||||
|
self.date = int(time.time())
|
||||||
|
|
||||||
|
with self.lock:
|
||||||
|
self.conn.commit()
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
with self.lock:
|
||||||
|
self.conn.close()
|
||||||
|
|
||||||
|
def update_peers(self, peers: List[Tuple[int, int, str, str, str]]):
|
||||||
|
with self.lock:
|
||||||
|
self.conn.executemany(
|
||||||
|
"REPLACE INTO peers (id, access_hash, type, username, phone_number)"
|
||||||
|
"VALUES (?, ?, ?, ?, ?)",
|
||||||
|
peers
|
||||||
|
)
|
||||||
|
|
||||||
|
def clear_peers(self):
|
||||||
|
with self.lock, self.conn:
|
||||||
|
self.conn.execute(
|
||||||
|
"DELETE FROM peers"
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_input_peer(peer_id: int, access_hash: int, peer_type: str):
|
||||||
|
if peer_type in ["user", "bot"]:
|
||||||
|
return types.InputPeerUser(
|
||||||
|
user_id=peer_id,
|
||||||
|
access_hash=access_hash
|
||||||
|
)
|
||||||
|
|
||||||
|
if peer_type == "group":
|
||||||
|
return types.InputPeerChat(
|
||||||
|
chat_id=-peer_id
|
||||||
|
)
|
||||||
|
|
||||||
|
if peer_type in ["channel", "supergroup"]:
|
||||||
|
return types.InputPeerChannel(
|
||||||
|
channel_id=int(str(peer_id)[4:]),
|
||||||
|
access_hash=access_hash
|
||||||
|
)
|
||||||
|
|
||||||
|
raise ValueError("Invalid peer type")
|
||||||
|
|
||||||
|
def get_peer_by_id(self, peer_id: int):
|
||||||
|
r = self.conn.execute(
|
||||||
|
"SELECT id, access_hash, type FROM peers WHERE id = ?",
|
||||||
|
(peer_id,)
|
||||||
|
).fetchone()
|
||||||
|
|
||||||
|
if r is None:
|
||||||
|
raise KeyError("ID not found")
|
||||||
|
|
||||||
|
return self._get_input_peer(*r)
|
||||||
|
|
||||||
|
def get_peer_by_username(self, username: str):
|
||||||
|
r = self.conn.execute(
|
||||||
|
"SELECT id, access_hash, type, last_update_on FROM peers WHERE username = ?",
|
||||||
|
(username,)
|
||||||
|
).fetchone()
|
||||||
|
|
||||||
|
if r is None:
|
||||||
|
raise KeyError("Username not found")
|
||||||
|
|
||||||
|
if abs(time.time() - r[3]) > self.USERNAME_TTL:
|
||||||
|
raise KeyError("Username expired")
|
||||||
|
|
||||||
|
return self._get_input_peer(*r[:3])
|
||||||
|
|
||||||
|
def get_peer_by_phone_number(self, phone_number: str):
|
||||||
|
r = self.conn.execute(
|
||||||
|
"SELECT id, access_hash, type FROM peers WHERE phone_number = ?",
|
||||||
|
(phone_number,)
|
||||||
|
).fetchone()
|
||||||
|
|
||||||
|
if r is None:
|
||||||
|
raise KeyError("Phone number not found")
|
||||||
|
|
||||||
|
return self._get_input_peer(*r)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def peers_count(self):
|
||||||
|
return self.conn.execute(
|
||||||
|
"SELECT COUNT(*) FROM peers"
|
||||||
|
).fetchone()[0]
|
||||||
|
|
||||||
|
def _get(self):
|
||||||
|
attr = inspect.stack()[1].function
|
||||||
|
|
||||||
|
return self.conn.execute(
|
||||||
|
"SELECT {} FROM sessions".format(attr)
|
||||||
|
).fetchone()[0]
|
||||||
|
|
||||||
|
def _set(self, value):
|
||||||
|
attr = inspect.stack()[1].function
|
||||||
|
|
||||||
|
with self.lock, self.conn:
|
||||||
|
self.conn.execute(
|
||||||
|
"UPDATE sessions SET {} = ?".format(attr),
|
||||||
|
(value,)
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dc_id(self):
|
||||||
|
return self._get()
|
||||||
|
|
||||||
|
@dc_id.setter
|
||||||
|
def dc_id(self, value):
|
||||||
|
self._set(value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def test_mode(self):
|
||||||
|
return self._get()
|
||||||
|
|
||||||
|
@test_mode.setter
|
||||||
|
def test_mode(self, value):
|
||||||
|
self._set(value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def auth_key(self):
|
||||||
|
return self._get()
|
||||||
|
|
||||||
|
@auth_key.setter
|
||||||
|
def auth_key(self, value):
|
||||||
|
self._set(value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def date(self):
|
||||||
|
return self._get()
|
||||||
|
|
||||||
|
@date.setter
|
||||||
|
def date(self, value):
|
||||||
|
self._set(value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_id(self):
|
||||||
|
return self._get()
|
||||||
|
|
||||||
|
@user_id.setter
|
||||||
|
def user_id(self, value):
|
||||||
|
self._set(value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_bot(self):
|
||||||
|
return self._get()
|
||||||
|
|
||||||
|
@is_bot.setter
|
||||||
|
def is_bot(self, value):
|
||||||
|
self._set(value)
|
34
pyrogram/client/storage/schema.sql
Normal file
34
pyrogram/client/storage/schema.sql
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
CREATE TABLE sessions (
|
||||||
|
dc_id INTEGER PRIMARY KEY,
|
||||||
|
test_mode INTEGER,
|
||||||
|
auth_key BLOB,
|
||||||
|
date INTEGER NOT NULL,
|
||||||
|
user_id INTEGER,
|
||||||
|
is_bot INTEGER
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE peers (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
access_hash INTEGER,
|
||||||
|
type INTEGER NOT NULL,
|
||||||
|
username TEXT,
|
||||||
|
phone_number TEXT,
|
||||||
|
last_update_on INTEGER NOT NULL DEFAULT (CAST(STRFTIME('%s', 'now') AS INTEGER))
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE version (
|
||||||
|
number INTEGER PRIMARY KEY
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_peers_id ON peers (id);
|
||||||
|
CREATE INDEX idx_peers_username ON peers (username);
|
||||||
|
CREATE INDEX idx_peers_phone_number ON peers (phone_number);
|
||||||
|
|
||||||
|
CREATE TRIGGER trg_peers_last_update_on
|
||||||
|
AFTER UPDATE
|
||||||
|
ON peers
|
||||||
|
BEGIN
|
||||||
|
UPDATE peers
|
||||||
|
SET last_update_on = CAST(STRFTIME('%s', 'now') AS INTEGER)
|
||||||
|
WHERE id = NEW.id;
|
||||||
|
END;
|
98
pyrogram/client/storage/storage.py
Normal file
98
pyrogram/client/storage/storage.py
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
# Pyrogram - Telegram MTProto API Client Library for Python
|
||||||
|
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
|
||||||
|
#
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
class Storage:
|
||||||
|
def __init__(self, name: str):
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def open(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def update_peers(self, peers):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def get_peer_by_id(self, peer_id):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def get_peer_by_username(self, username):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def get_peer_by_phone_number(self, phone_number):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def export_session_string(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@property
|
||||||
|
def peers_count(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dc_id(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@dc_id.setter
|
||||||
|
def dc_id(self, value):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@property
|
||||||
|
def test_mode(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@test_mode.setter
|
||||||
|
def test_mode(self, value):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@property
|
||||||
|
def auth_key(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@auth_key.setter
|
||||||
|
def auth_key(self, value):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@property
|
||||||
|
def date(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@date.setter
|
||||||
|
def date(self, value):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_id(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@user_id.setter
|
||||||
|
def user_id(self, value):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_bot(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@is_bot.setter
|
||||||
|
def is_bot(self, value):
|
||||||
|
raise NotImplementedError
|
@ -16,10 +16,12 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from .keyboards import *
|
from .bots_and_keyboards import *
|
||||||
from .inline_mode import *
|
from .inline_mode import *
|
||||||
from .input_media import *
|
from .input_media import *
|
||||||
from .input_message_content import *
|
from .input_message_content import *
|
||||||
|
from .list import List
|
||||||
from .messages_and_media import *
|
from .messages_and_media import *
|
||||||
|
from .object import Object
|
||||||
from .update import *
|
from .update import *
|
||||||
from .user_and_chats import *
|
from .user_and_chats import *
|
||||||
|
@ -20,7 +20,6 @@ from .callback_game import CallbackGame
|
|||||||
from .callback_query import CallbackQuery
|
from .callback_query import CallbackQuery
|
||||||
from .force_reply import ForceReply
|
from .force_reply import ForceReply
|
||||||
from .game_high_score import GameHighScore
|
from .game_high_score import GameHighScore
|
||||||
from .game_high_scores import GameHighScores
|
|
||||||
from .inline_keyboard_button import InlineKeyboardButton
|
from .inline_keyboard_button import InlineKeyboardButton
|
||||||
from .inline_keyboard_markup import InlineKeyboardMarkup
|
from .inline_keyboard_markup import InlineKeyboardMarkup
|
||||||
from .keyboard_button import KeyboardButton
|
from .keyboard_button import KeyboardButton
|
||||||
@ -28,6 +27,6 @@ from .reply_keyboard_markup import ReplyKeyboardMarkup
|
|||||||
from .reply_keyboard_remove import ReplyKeyboardRemove
|
from .reply_keyboard_remove import ReplyKeyboardRemove
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"CallbackGame", "CallbackQuery", "ForceReply", "GameHighScore", "GameHighScores", "InlineKeyboardButton",
|
"CallbackGame", "CallbackQuery", "ForceReply", "GameHighScore", "InlineKeyboardButton", "InlineKeyboardMarkup",
|
||||||
"InlineKeyboardMarkup", "KeyboardButton", "ReplyKeyboardMarkup", "ReplyKeyboardRemove"
|
"KeyboardButton", "ReplyKeyboardMarkup", "ReplyKeyboardRemove"
|
||||||
]
|
]
|
@ -172,3 +172,151 @@ class CallbackQuery(Object, Update):
|
|||||||
url=url,
|
url=url,
|
||||||
cache_time=cache_time
|
cache_time=cache_time
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def edit_text(
|
||||||
|
self,
|
||||||
|
text: str,
|
||||||
|
parse_mode: str = "",
|
||||||
|
disable_web_page_preview: bool = None,
|
||||||
|
reply_markup: "pyrogram.InlineKeyboardMarkup" = None
|
||||||
|
) -> Union["pyrogram.Message", bool]:
|
||||||
|
"""Edit the text of messages attached to this callback query.
|
||||||
|
|
||||||
|
Bound method *edit_message_text* of :obj:`CallbackQuery`.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
text (``str``):
|
||||||
|
New text of the message.
|
||||||
|
|
||||||
|
parse_mode (``str``, *optional*):
|
||||||
|
Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline
|
||||||
|
URLs in your message. Defaults to "markdown".
|
||||||
|
|
||||||
|
disable_web_page_preview (``bool``, *optional*):
|
||||||
|
Disables link previews for links in this message.
|
||||||
|
|
||||||
|
reply_markup (:obj:`InlineKeyboardMarkup`, *optional*):
|
||||||
|
An InlineKeyboardMarkup object.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
:obj:`Message` | ``bool``: On success, if the edited message was sent by the bot, the edited message is
|
||||||
|
returned, otherwise True is returned (message sent via the bot, as inline query result).
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RPCError: In case of a Telegram RPC error.
|
||||||
|
"""
|
||||||
|
if self.inline_message_id is None:
|
||||||
|
return self._client.edit_message_text(
|
||||||
|
chat_id=self.message.chat.id,
|
||||||
|
message_id=self.message.message_id,
|
||||||
|
text=text,
|
||||||
|
parse_mode=parse_mode,
|
||||||
|
disable_web_page_preview=disable_web_page_preview,
|
||||||
|
reply_markup=reply_markup
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return self._client.edit_inline_text(
|
||||||
|
inline_message_id=self.inline_message_id,
|
||||||
|
text=text,
|
||||||
|
parse_mode=parse_mode,
|
||||||
|
disable_web_page_preview=disable_web_page_preview,
|
||||||
|
reply_markup=reply_markup
|
||||||
|
)
|
||||||
|
|
||||||
|
def edit_caption(
|
||||||
|
self,
|
||||||
|
caption: str,
|
||||||
|
parse_mode: str = "",
|
||||||
|
reply_markup: "pyrogram.InlineKeyboardMarkup" = None
|
||||||
|
) -> Union["pyrogram.Message", bool]:
|
||||||
|
"""Edit the caption of media messages attached to this callback query.
|
||||||
|
|
||||||
|
Bound method *edit_message_caption* of :obj:`CallbackQuery`.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
caption (``str``):
|
||||||
|
New caption of the message.
|
||||||
|
|
||||||
|
parse_mode (``str``, *optional*):
|
||||||
|
Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline
|
||||||
|
URLs in your message. Defaults to "markdown".
|
||||||
|
|
||||||
|
reply_markup (:obj:`InlineKeyboardMarkup`, *optional*):
|
||||||
|
An InlineKeyboardMarkup object.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
:obj:`Message` | ``bool``: On success, if the edited message was sent by the bot, the edited message is
|
||||||
|
returned, otherwise True is returned (message sent via the bot, as inline query result).
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RPCError: In case of a Telegram RPC error.
|
||||||
|
"""
|
||||||
|
return self.edit_text(caption, parse_mode, reply_markup)
|
||||||
|
|
||||||
|
def edit_media(
|
||||||
|
self,
|
||||||
|
media: "pyrogram.InputMedia",
|
||||||
|
reply_markup: "pyrogram.InlineKeyboardMarkup" = None
|
||||||
|
) -> Union["pyrogram.Message", bool]:
|
||||||
|
"""Edit animation, audio, document, photo or video messages attached to this callback query.
|
||||||
|
|
||||||
|
Bound method *edit_message_media* of :obj:`CallbackQuery`.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
media (:obj:`InputMedia`):
|
||||||
|
One of the InputMedia objects describing an animation, audio, document, photo or video.
|
||||||
|
|
||||||
|
reply_markup (:obj:`InlineKeyboardMarkup`, *optional*):
|
||||||
|
An InlineKeyboardMarkup object.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
:obj:`Message` | ``bool``: On success, if the edited message was sent by the bot, the edited message is
|
||||||
|
returned, otherwise True is returned (message sent via the bot, as inline query result).
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RPCError: In case of a Telegram RPC error.
|
||||||
|
"""
|
||||||
|
if self.inline_message_id is None:
|
||||||
|
return self._client.edit_message_media(
|
||||||
|
chat_id=self.message.chat.id,
|
||||||
|
message_id=self.message.message_id,
|
||||||
|
media=media,
|
||||||
|
reply_markup=reply_markup
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return self._client.edit_inline_media(
|
||||||
|
inline_message_id=self.inline_message_id,
|
||||||
|
media=media,
|
||||||
|
reply_markup=reply_markup
|
||||||
|
)
|
||||||
|
|
||||||
|
def edit_reply_markup(
|
||||||
|
self,
|
||||||
|
reply_markup: "pyrogram.InlineKeyboardMarkup" = None
|
||||||
|
) -> Union["pyrogram.Message", bool]:
|
||||||
|
"""Edit only the reply markup of messages attached to this callback query.
|
||||||
|
|
||||||
|
Bound method *edit_message_reply_markup* of :obj:`CallbackQuery`.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
reply_markup (:obj:`InlineKeyboardMarkup`):
|
||||||
|
An InlineKeyboardMarkup object.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
:obj:`Message` | ``bool``: On success, if the edited message was sent by the bot, the edited message is
|
||||||
|
returned, otherwise True is returned (message sent via the bot, as inline query result).
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RPCError: In case of a Telegram RPC error.
|
||||||
|
"""
|
||||||
|
if self.inline_message_id is None:
|
||||||
|
return self._client.edit_message_reply_markup(
|
||||||
|
chat_id=self.message.chat.id,
|
||||||
|
message_id=self.message.message_id,
|
||||||
|
reply_markup=reply_markup
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return self._client.edit_inline_reply_markup(
|
||||||
|
inline_message_id=self.inline_message_id,
|
||||||
|
reply_markup=reply_markup
|
||||||
|
)
|
@ -24,7 +24,7 @@ from ..object import Object
|
|||||||
|
|
||||||
class InputPhoneContact(Object):
|
class InputPhoneContact(Object):
|
||||||
"""A Phone Contact to be added in your Telegram address book.
|
"""A Phone Contact to be added in your Telegram address book.
|
||||||
It is intended to be used with :meth:`~Client.add_contacts() <pyrogram.Client.add_contacts>`
|
It is intended to be used with :meth:`~pyrogram.Client.add_contacts()`
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
phone (``str``):
|
phone (``str``):
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
# Pyrogram - Telegram MTProto API Client Library for Python
|
|
||||||
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
from typing import List
|
|
||||||
|
|
||||||
import pyrogram
|
|
||||||
from pyrogram.api import types
|
|
||||||
from pyrogram.client.types.object import Object
|
|
||||||
from .game_high_score import GameHighScore
|
|
||||||
|
|
||||||
|
|
||||||
class GameHighScores(Object):
|
|
||||||
"""The high scores table for a game.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
total_count (``int``):
|
|
||||||
Total number of scores the target game has.
|
|
||||||
|
|
||||||
game_high_scores (List of :obj:`GameHighScore`):
|
|
||||||
Game scores.
|
|
||||||
"""
|
|
||||||
|
|
||||||
__slots__ = ["total_count", "game_high_scores"]
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
client: "pyrogram.BaseClient" = None,
|
|
||||||
total_count: int,
|
|
||||||
game_high_scores: List[GameHighScore]
|
|
||||||
):
|
|
||||||
super().__init__(client)
|
|
||||||
|
|
||||||
self.total_count = total_count
|
|
||||||
self.game_high_scores = game_high_scores
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _parse(client, game_high_scores: types.messages.HighScores) -> "GameHighScores":
|
|
||||||
return GameHighScores(
|
|
||||||
total_count=len(game_high_scores.scores),
|
|
||||||
game_high_scores=[
|
|
||||||
GameHighScore._parse(client, score, game_high_scores.users)
|
|
||||||
for score in game_high_scores.scores],
|
|
||||||
client=client
|
|
||||||
)
|
|
@ -24,11 +24,9 @@ from .game import Game
|
|||||||
from .location import Location
|
from .location import Location
|
||||||
from .message import Message
|
from .message import Message
|
||||||
from .message_entity import MessageEntity
|
from .message_entity import MessageEntity
|
||||||
from .messages import Messages
|
|
||||||
from .photo import Photo
|
from .photo import Photo
|
||||||
from .poll import Poll
|
from .poll import Poll
|
||||||
from .poll_option import PollOption
|
from .poll_option import PollOption
|
||||||
from .profile_photos import ProfilePhotos
|
|
||||||
from .sticker import Sticker
|
from .sticker import Sticker
|
||||||
from .stripped_thumbnail import StrippedThumbnail
|
from .stripped_thumbnail import StrippedThumbnail
|
||||||
from .thumbnail import Thumbnail
|
from .thumbnail import Thumbnail
|
||||||
@ -38,7 +36,6 @@ from .video_note import VideoNote
|
|||||||
from .voice import Voice
|
from .voice import Voice
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"Animation", "Audio", "Contact", "Document", "Game", "Location", "Message", "MessageEntity", "Messages", "Photo",
|
"Animation", "Audio", "Contact", "Document", "Game", "Location", "Message", "MessageEntity", "Photo", "Thumbnail",
|
||||||
"Thumbnail", "StrippedThumbnail", "Poll", "PollOption", "Sticker", "ProfilePhotos", "Venue", "Video", "VideoNote",
|
"StrippedThumbnail", "Poll", "PollOption", "Sticker", "Venue", "Video", "VideoNote", "Voice"
|
||||||
"Voice"
|
|
||||||
]
|
]
|
||||||
|
@ -651,7 +651,7 @@ class Message(Object, Update):
|
|||||||
|
|
||||||
return parsed_message
|
return parsed_message
|
||||||
|
|
||||||
async def reply(
|
async def reply_text(
|
||||||
self,
|
self,
|
||||||
text: str,
|
text: str,
|
||||||
quote: bool = None,
|
quote: bool = None,
|
||||||
@ -661,7 +661,7 @@ class Message(Object, Update):
|
|||||||
reply_to_message_id: int = None,
|
reply_to_message_id: int = None,
|
||||||
reply_markup=None
|
reply_markup=None
|
||||||
) -> "Message":
|
) -> "Message":
|
||||||
"""Bound method *reply* :obj:`Message <pyrogram.Message>`.
|
"""Bound method *reply_text* of :obj:`Message`.
|
||||||
|
|
||||||
Use as a shortcut for:
|
Use as a shortcut for:
|
||||||
|
|
||||||
@ -676,7 +676,7 @@ class Message(Object, Update):
|
|||||||
Example:
|
Example:
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
message.reply("hello", quote=True)
|
message.reply_text("hello", quote=True)
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
text (``str``):
|
text (``str``):
|
||||||
@ -748,7 +748,7 @@ class Message(Object, Update):
|
|||||||
progress: callable = None,
|
progress: callable = None,
|
||||||
progress_args: tuple = ()
|
progress_args: tuple = ()
|
||||||
) -> "Message":
|
) -> "Message":
|
||||||
"""Bound method *reply_animation* :obj:`Message <pyrogram.Message>`.
|
"""Bound method *reply_animation* :obj:`Message`.
|
||||||
|
|
||||||
Use as a shortcut for:
|
Use as a shortcut for:
|
||||||
|
|
||||||
@ -882,7 +882,7 @@ class Message(Object, Update):
|
|||||||
progress: callable = None,
|
progress: callable = None,
|
||||||
progress_args: tuple = ()
|
progress_args: tuple = ()
|
||||||
) -> "Message":
|
) -> "Message":
|
||||||
"""Bound method *reply_audio* :obj:`Message <pyrogram.Message>`.
|
"""Bound method *reply_audio* of :obj:`Message`.
|
||||||
|
|
||||||
Use as a shortcut for:
|
Use as a shortcut for:
|
||||||
|
|
||||||
@ -1010,7 +1010,7 @@ class Message(Object, Update):
|
|||||||
"pyrogram.ForceReply"
|
"pyrogram.ForceReply"
|
||||||
] = None
|
] = None
|
||||||
) -> "Message":
|
) -> "Message":
|
||||||
"""Bound method *reply_cached_media* :obj:`Message <pyrogram.Message>`.
|
"""Bound method *reply_cached_media* of :obj:`Message`.
|
||||||
|
|
||||||
Use as a shortcut for:
|
Use as a shortcut for:
|
||||||
|
|
||||||
@ -1077,7 +1077,7 @@ class Message(Object, Update):
|
|||||||
)
|
)
|
||||||
|
|
||||||
async def reply_chat_action(self, action: str) -> bool:
|
async def reply_chat_action(self, action: str) -> bool:
|
||||||
"""Bound method *reply_chat_action* :obj:`Message <pyrogram.Message>`.
|
"""Bound method *reply_chat_action* of :obj:`Message`.
|
||||||
|
|
||||||
Use as a shortcut for:
|
Use as a shortcut for:
|
||||||
|
|
||||||
@ -1130,7 +1130,7 @@ class Message(Object, Update):
|
|||||||
"pyrogram.ForceReply"
|
"pyrogram.ForceReply"
|
||||||
] = None
|
] = None
|
||||||
) -> "Message":
|
) -> "Message":
|
||||||
"""Bound method *reply_contact* :obj:`Message <pyrogram.Message>`.
|
"""Bound method *reply_contact* of :obj:`Message`.
|
||||||
|
|
||||||
Use as a shortcut for:
|
Use as a shortcut for:
|
||||||
|
|
||||||
@ -1217,7 +1217,7 @@ class Message(Object, Update):
|
|||||||
progress: callable = None,
|
progress: callable = None,
|
||||||
progress_args: tuple = ()
|
progress_args: tuple = ()
|
||||||
) -> "Message":
|
) -> "Message":
|
||||||
"""Bound method *reply_document* :obj:`Message <pyrogram.Message>`.
|
"""Bound method *reply_document* of :obj:`Message`.
|
||||||
|
|
||||||
Use as a shortcut for:
|
Use as a shortcut for:
|
||||||
|
|
||||||
@ -1331,7 +1331,7 @@ class Message(Object, Update):
|
|||||||
"pyrogram.ForceReply"
|
"pyrogram.ForceReply"
|
||||||
] = None
|
] = None
|
||||||
) -> "Message":
|
) -> "Message":
|
||||||
"""Bound method *reply_game* :obj:`Message <pyrogram.Message>`.
|
"""Bound method *reply_game* of :obj:`Message`.
|
||||||
|
|
||||||
Use as a shortcut for:
|
Use as a shortcut for:
|
||||||
|
|
||||||
@ -1396,7 +1396,7 @@ class Message(Object, Update):
|
|||||||
reply_to_message_id: int = None,
|
reply_to_message_id: int = None,
|
||||||
hide_via: bool = None
|
hide_via: bool = None
|
||||||
) -> "Message":
|
) -> "Message":
|
||||||
"""Bound method *reply_inline_bot_result* :obj:`Message <pyrogram.Message>`.
|
"""Bound method *reply_inline_bot_result* of :obj:`Message`.
|
||||||
|
|
||||||
Use as a shortcut for:
|
Use as a shortcut for:
|
||||||
|
|
||||||
@ -1470,7 +1470,7 @@ class Message(Object, Update):
|
|||||||
"pyrogram.ForceReply"
|
"pyrogram.ForceReply"
|
||||||
] = None
|
] = None
|
||||||
) -> "Message":
|
) -> "Message":
|
||||||
"""Bound method *reply_location* :obj:`Message <pyrogram.Message>`.
|
"""Bound method *reply_location* of :obj:`Message`.
|
||||||
|
|
||||||
Use as a shortcut for:
|
Use as a shortcut for:
|
||||||
|
|
||||||
@ -1538,7 +1538,7 @@ class Message(Object, Update):
|
|||||||
disable_notification: bool = None,
|
disable_notification: bool = None,
|
||||||
reply_to_message_id: int = None
|
reply_to_message_id: int = None
|
||||||
) -> "Message":
|
) -> "Message":
|
||||||
"""Bound method *reply_media_group* :obj:`Message <pyrogram.Message>`.
|
"""Bound method *reply_media_group* of :obj:`Message`.
|
||||||
|
|
||||||
Use as a shortcut for:
|
Use as a shortcut for:
|
||||||
|
|
||||||
@ -1610,7 +1610,7 @@ class Message(Object, Update):
|
|||||||
progress: callable = None,
|
progress: callable = None,
|
||||||
progress_args: tuple = ()
|
progress_args: tuple = ()
|
||||||
) -> "Message":
|
) -> "Message":
|
||||||
"""Bound method *reply_photo* :obj:`Message <pyrogram.Message>`.
|
"""Bound method *reply_photo* of :obj:`Message`.
|
||||||
|
|
||||||
Use as a shortcut for:
|
Use as a shortcut for:
|
||||||
|
|
||||||
@ -1724,7 +1724,7 @@ class Message(Object, Update):
|
|||||||
"pyrogram.ForceReply"
|
"pyrogram.ForceReply"
|
||||||
] = None
|
] = None
|
||||||
) -> "Message":
|
) -> "Message":
|
||||||
"""Bound method *reply_poll* :obj:`Message <pyrogram.Message>`.
|
"""Bound method *reply_poll* of :obj:`Message`.
|
||||||
|
|
||||||
Use as a shortcut for:
|
Use as a shortcut for:
|
||||||
|
|
||||||
@ -1800,7 +1800,7 @@ class Message(Object, Update):
|
|||||||
progress: callable = None,
|
progress: callable = None,
|
||||||
progress_args: tuple = ()
|
progress_args: tuple = ()
|
||||||
) -> "Message":
|
) -> "Message":
|
||||||
"""Bound method *reply_sticker* :obj:`Message <pyrogram.Message>`.
|
"""Bound method *reply_sticker* of :obj:`Message`.
|
||||||
|
|
||||||
Use as a shortcut for:
|
Use as a shortcut for:
|
||||||
|
|
||||||
@ -1903,7 +1903,7 @@ class Message(Object, Update):
|
|||||||
"pyrogram.ForceReply"
|
"pyrogram.ForceReply"
|
||||||
] = None
|
] = None
|
||||||
) -> "Message":
|
) -> "Message":
|
||||||
"""Bound method *reply_venue* :obj:`Message <pyrogram.Message>`.
|
"""Bound method *reply_venue* of :obj:`Message`.
|
||||||
|
|
||||||
Use as a shortcut for:
|
Use as a shortcut for:
|
||||||
|
|
||||||
@ -2005,7 +2005,7 @@ class Message(Object, Update):
|
|||||||
progress: callable = None,
|
progress: callable = None,
|
||||||
progress_args: tuple = ()
|
progress_args: tuple = ()
|
||||||
) -> "Message":
|
) -> "Message":
|
||||||
"""Bound method *reply_video* :obj:`Message <pyrogram.Message>`.
|
"""Bound method *reply_video* of :obj:`Message`.
|
||||||
|
|
||||||
Use as a shortcut for:
|
Use as a shortcut for:
|
||||||
|
|
||||||
@ -2140,7 +2140,7 @@ class Message(Object, Update):
|
|||||||
progress: callable = None,
|
progress: callable = None,
|
||||||
progress_args: tuple = ()
|
progress_args: tuple = ()
|
||||||
) -> "Message":
|
) -> "Message":
|
||||||
"""Bound method *reply_video_note* :obj:`Message <pyrogram.Message>`.
|
"""Bound method *reply_video_note* of :obj:`Message`.
|
||||||
|
|
||||||
Use as a shortcut for:
|
Use as a shortcut for:
|
||||||
|
|
||||||
@ -2258,7 +2258,7 @@ class Message(Object, Update):
|
|||||||
progress: callable = None,
|
progress: callable = None,
|
||||||
progress_args: tuple = ()
|
progress_args: tuple = ()
|
||||||
) -> "Message":
|
) -> "Message":
|
||||||
"""Bound method *reply_voice* :obj:`Message <pyrogram.Message>`.
|
"""Bound method *reply_voice* of :obj:`Message`.
|
||||||
|
|
||||||
Use as a shortcut for:
|
Use as a shortcut for:
|
||||||
|
|
||||||
@ -2356,19 +2356,14 @@ class Message(Object, Update):
|
|||||||
progress_args=progress_args
|
progress_args=progress_args
|
||||||
)
|
)
|
||||||
|
|
||||||
async def edit(
|
async def edit_text(
|
||||||
self,
|
self,
|
||||||
text: str,
|
text: str,
|
||||||
parse_mode: str = "",
|
parse_mode: str = "",
|
||||||
disable_web_page_preview: bool = None,
|
disable_web_page_preview: bool = None,
|
||||||
reply_markup: Union[
|
reply_markup: "pyrogram.InlineKeyboardMarkup" = None
|
||||||
"pyrogram.InlineKeyboardMarkup",
|
|
||||||
"pyrogram.ReplyKeyboardMarkup",
|
|
||||||
"pyrogram.ReplyKeyboardRemove",
|
|
||||||
"pyrogram.ForceReply"
|
|
||||||
] = None
|
|
||||||
) -> "Message":
|
) -> "Message":
|
||||||
"""Bound method *edit* :obj:`Message <pyrogram.Message>`.
|
"""Bound method *edit_text* of :obj:`Message`.
|
||||||
|
|
||||||
Use as a shortcut for:
|
Use as a shortcut for:
|
||||||
|
|
||||||
@ -2383,7 +2378,7 @@ class Message(Object, Update):
|
|||||||
Example:
|
Example:
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
message.edit("hello")
|
message.edit_text("hello")
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
text (``str``):
|
text (``str``):
|
||||||
@ -2418,14 +2413,9 @@ class Message(Object, Update):
|
|||||||
self,
|
self,
|
||||||
caption: str,
|
caption: str,
|
||||||
parse_mode: str = "",
|
parse_mode: str = "",
|
||||||
reply_markup: Union[
|
reply_markup: "pyrogram.InlineKeyboardMarkup" = None
|
||||||
"pyrogram.InlineKeyboardMarkup",
|
|
||||||
"pyrogram.ReplyKeyboardMarkup",
|
|
||||||
"pyrogram.ReplyKeyboardRemove",
|
|
||||||
"pyrogram.ForceReply"
|
|
||||||
] = None
|
|
||||||
) -> "Message":
|
) -> "Message":
|
||||||
"""Bound method *edit_caption* :obj:`Message <pyrogram.Message>`.
|
"""Bound method *edit_caption* of :obj:`Message`.
|
||||||
|
|
||||||
Use as a shortcut for:
|
Use as a shortcut for:
|
||||||
|
|
||||||
@ -2468,7 +2458,7 @@ class Message(Object, Update):
|
|||||||
)
|
)
|
||||||
|
|
||||||
async def edit_media(self, media: InputMedia, reply_markup: "pyrogram.InlineKeyboardMarkup" = None) -> "Message":
|
async def edit_media(self, media: InputMedia, reply_markup: "pyrogram.InlineKeyboardMarkup" = None) -> "Message":
|
||||||
"""Bound method *edit_media* :obj:`Message <pyrogram.Message>`.
|
"""Bound method *edit_media* of :obj:`Message`.
|
||||||
|
|
||||||
Use as a shortcut for:
|
Use as a shortcut for:
|
||||||
|
|
||||||
@ -2486,7 +2476,7 @@ class Message(Object, Update):
|
|||||||
message.edit_media(media)
|
message.edit_media(media)
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
media (:obj:`InputMediaAnimation` | :obj:`InputMediaAudio` | :obj:`InputMediaDocument` | :obj:`InputMediaPhoto` | :obj:`InputMediaVideo`)
|
media (:obj:`InputMedia`):
|
||||||
One of the InputMedia objects describing an animation, audio, document, photo or video.
|
One of the InputMedia objects describing an animation, audio, document, photo or video.
|
||||||
|
|
||||||
reply_markup (:obj:`InlineKeyboardMarkup`, *optional*):
|
reply_markup (:obj:`InlineKeyboardMarkup`, *optional*):
|
||||||
@ -2506,7 +2496,7 @@ class Message(Object, Update):
|
|||||||
)
|
)
|
||||||
|
|
||||||
async def edit_reply_markup(self, reply_markup: "pyrogram.InlineKeyboardMarkup" = None) -> "Message":
|
async def edit_reply_markup(self, reply_markup: "pyrogram.InlineKeyboardMarkup" = None) -> "Message":
|
||||||
"""Bound method *edit_reply_markup* :obj:`Message <pyrogram.Message>`.
|
"""Bound method *edit_reply_markup* of :obj:`Message`.
|
||||||
|
|
||||||
Use as a shortcut for:
|
Use as a shortcut for:
|
||||||
|
|
||||||
@ -2547,7 +2537,7 @@ class Message(Object, Update):
|
|||||||
as_copy: bool = False,
|
as_copy: bool = False,
|
||||||
remove_caption: bool = False
|
remove_caption: bool = False
|
||||||
) -> "Message":
|
) -> "Message":
|
||||||
"""Bound method *forward* :obj:`Message <pyrogram.Message>`.
|
"""Bound method *forward* of :obj:`Message`.
|
||||||
|
|
||||||
Use as a shortcut for:
|
Use as a shortcut for:
|
||||||
|
|
||||||
@ -2617,7 +2607,7 @@ class Message(Object, Update):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if self.photo:
|
if self.photo:
|
||||||
file_id = self.photo.sizes[-1].file_id
|
file_id = self.photo.file_id
|
||||||
elif self.audio:
|
elif self.audio:
|
||||||
file_id = self.audio.file_id
|
file_id = self.audio.file_id
|
||||||
elif self.document:
|
elif self.document:
|
||||||
@ -2690,7 +2680,7 @@ class Message(Object, Update):
|
|||||||
)
|
)
|
||||||
|
|
||||||
async def delete(self, revoke: bool = True):
|
async def delete(self, revoke: bool = True):
|
||||||
"""Bound method *delete* :obj:`Message <pyrogram.Message>`.
|
"""Bound method *delete* of :obj:`Message`.
|
||||||
|
|
||||||
Use as a shortcut for:
|
Use as a shortcut for:
|
||||||
|
|
||||||
@ -2726,7 +2716,7 @@ class Message(Object, Update):
|
|||||||
)
|
)
|
||||||
|
|
||||||
async def click(self, x: int or str, y: int = 0, quote: bool = None, timeout: int = 10):
|
async def click(self, x: int or str, y: int = 0, quote: bool = None, timeout: int = 10):
|
||||||
"""Bound method *click* :obj:`Message <pyrogram.Message>`.
|
"""Bound method *click* of :obj:`Message`.
|
||||||
|
|
||||||
Use as a shortcut for clicking a button attached to the message instead of:
|
Use as a shortcut for clicking a button attached to the message instead of:
|
||||||
|
|
||||||
@ -2853,7 +2843,7 @@ class Message(Object, Update):
|
|||||||
progress: callable = None,
|
progress: callable = None,
|
||||||
progress_args: tuple = ()
|
progress_args: tuple = ()
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Bound method *download* :obj:`Message <pyrogram.Message>`.
|
"""Bound method *download* of :obj:`Message`.
|
||||||
|
|
||||||
Use as a shortcut for:
|
Use as a shortcut for:
|
||||||
|
|
||||||
@ -2902,7 +2892,7 @@ class Message(Object, Update):
|
|||||||
)
|
)
|
||||||
|
|
||||||
async def pin(self, disable_notification: bool = None) -> "Message":
|
async def pin(self, disable_notification: bool = None) -> "Message":
|
||||||
"""Bound method *pin* :obj:`Message <pyrogram.Message>`.
|
"""Bound method *pin* of :obj:`Message`.
|
||||||
|
|
||||||
Use as a shortcut for:
|
Use as a shortcut for:
|
||||||
|
|
||||||
|
@ -31,8 +31,8 @@ class MessageEntity(Object):
|
|||||||
type (``str``):
|
type (``str``):
|
||||||
Type of the entity.
|
Type of the entity.
|
||||||
Can be "mention" (@username), "hashtag", "cashtag", "bot_command", "url", "email", "phone_number", "bold"
|
Can be "mention" (@username), "hashtag", "cashtag", "bot_command", "url", "email", "phone_number", "bold"
|
||||||
(bold text), italic (italic text), "code" (monowidth string), "pre" (monowidth block), "text_link"
|
(bold text), "italic" (italic text), "code" (monowidth string), "pre" (monowidth block), "text_link"
|
||||||
(for clickable text URLs), "text_mention" (for users without usernames).
|
(for clickable text URLs), "text_mention" (for custom text mentions based on users' identifiers).
|
||||||
|
|
||||||
offset (``int``):
|
offset (``int``):
|
||||||
Offset in UTF-16 code units to the start of the entity.
|
Offset in UTF-16 code units to the start of the entity.
|
||||||
|
@ -1,174 +0,0 @@
|
|||||||
# Pyrogram - Telegram MTProto API Client Library for Python
|
|
||||||
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
from typing import List, Union
|
|
||||||
|
|
||||||
import pyrogram
|
|
||||||
from pyrogram.api import types
|
|
||||||
from .message import Message
|
|
||||||
from ..object import Object
|
|
||||||
from ..update import Update
|
|
||||||
from ..user_and_chats import Chat
|
|
||||||
|
|
||||||
|
|
||||||
class Messages(Object, Update):
|
|
||||||
"""Contains a chat's messages.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
total_count (``int``):
|
|
||||||
Total number of messages the target chat has.
|
|
||||||
|
|
||||||
messages (List of :obj:`Message`):
|
|
||||||
Requested messages.
|
|
||||||
"""
|
|
||||||
|
|
||||||
__slots__ = ["total_count", "messages"]
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
client: "pyrogram.BaseClient" = None,
|
|
||||||
total_count: int,
|
|
||||||
messages: List[Message]
|
|
||||||
):
|
|
||||||
super().__init__(client)
|
|
||||||
|
|
||||||
self.total_count = total_count
|
|
||||||
self.messages = messages
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
async def _parse(client, messages: types.messages.Messages, replies: int = 1) -> "Messages":
|
|
||||||
users = {i.id: i for i in messages.users}
|
|
||||||
chats = {i.id: i for i in messages.chats}
|
|
||||||
|
|
||||||
total_count = getattr(messages, "count", len(messages.messages))
|
|
||||||
|
|
||||||
if not messages.messages:
|
|
||||||
return Messages(
|
|
||||||
total_count=total_count,
|
|
||||||
messages=[],
|
|
||||||
client=client
|
|
||||||
)
|
|
||||||
|
|
||||||
# TODO: WTF! Py 3.5 doesn't support await inside comprehensions
|
|
||||||
parsed_messages = []
|
|
||||||
|
|
||||||
for message in messages.messages:
|
|
||||||
parsed_messages.append(await Message._parse(client, message, users, chats, replies=0))
|
|
||||||
|
|
||||||
if replies:
|
|
||||||
messages_with_replies = {i.id: getattr(i, "reply_to_msg_id", None) for i in messages.messages}
|
|
||||||
reply_message_ids = [i[0] for i in filter(lambda x: x[1] is not None, messages_with_replies.items())]
|
|
||||||
|
|
||||||
if reply_message_ids:
|
|
||||||
reply_messages = (await client.get_messages(
|
|
||||||
parsed_messages[0].chat.id,
|
|
||||||
reply_to_message_ids=reply_message_ids,
|
|
||||||
replies=replies - 1
|
|
||||||
)).messages
|
|
||||||
|
|
||||||
for message in parsed_messages:
|
|
||||||
reply_id = messages_with_replies[message.message_id]
|
|
||||||
|
|
||||||
for reply in reply_messages:
|
|
||||||
if reply.message_id == reply_id:
|
|
||||||
message.reply_to_message = reply
|
|
||||||
|
|
||||||
return Messages(
|
|
||||||
total_count=total_count,
|
|
||||||
messages=parsed_messages,
|
|
||||||
client=client
|
|
||||||
)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _parse_deleted(client, update) -> "Messages":
|
|
||||||
messages = update.messages
|
|
||||||
channel_id = getattr(update, "channel_id", None)
|
|
||||||
|
|
||||||
parsed_messages = []
|
|
||||||
|
|
||||||
for message in messages:
|
|
||||||
parsed_messages.append(
|
|
||||||
Message(
|
|
||||||
message_id=message,
|
|
||||||
chat=Chat(
|
|
||||||
id=int("-100" + str(channel_id)),
|
|
||||||
type="channel",
|
|
||||||
client=client
|
|
||||||
) if channel_id is not None else None,
|
|
||||||
client=client
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return Messages(
|
|
||||||
total_count=len(parsed_messages),
|
|
||||||
messages=parsed_messages,
|
|
||||||
client=client
|
|
||||||
)
|
|
||||||
|
|
||||||
def forward(
|
|
||||||
self,
|
|
||||||
chat_id: Union[int, str],
|
|
||||||
disable_notification: bool = None,
|
|
||||||
as_copy: bool = False,
|
|
||||||
remove_caption: bool = False
|
|
||||||
):
|
|
||||||
"""Bound method *forward* of :obj:`Message`.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
chat_id (``int`` | ``str``):
|
|
||||||
Unique identifier (int) or username (str) of the target chat.
|
|
||||||
For your personal cloud (Saved Messages) you can simply use "me" or "self".
|
|
||||||
For a contact that exists in your Telegram address book you can use his phone number (str).
|
|
||||||
|
|
||||||
disable_notification (``bool``, *optional*):
|
|
||||||
Sends messages silently.
|
|
||||||
Users will receive a notification with no sound.
|
|
||||||
|
|
||||||
as_copy (``bool``, *optional*):
|
|
||||||
Pass True to forward messages without the forward header (i.e.: send a copy of the message content).
|
|
||||||
Defaults to False.
|
|
||||||
|
|
||||||
remove_caption (``bool``, *optional*):
|
|
||||||
If set to True and *as_copy* is enabled as well, media captions are not preserved when copying the
|
|
||||||
message. Has no effect if *as_copy* is not enabled.
|
|
||||||
Defaults to False.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
On success, a :obj:`Messages` containing forwarded messages is returned.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
RPCError: In case of a Telegram RPC error.
|
|
||||||
"""
|
|
||||||
forwarded_messages = []
|
|
||||||
|
|
||||||
for message in self.messages:
|
|
||||||
forwarded_messages.append(
|
|
||||||
message.forward(
|
|
||||||
chat_id=chat_id,
|
|
||||||
as_copy=as_copy,
|
|
||||||
disable_notification=disable_notification,
|
|
||||||
remove_caption=remove_caption
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return Messages(
|
|
||||||
total_count=len(forwarded_messages),
|
|
||||||
messages=forwarded_messages,
|
|
||||||
client=self._client
|
|
||||||
)
|
|
@ -1,57 +0,0 @@
|
|||||||
# Pyrogram - Telegram MTProto API Client Library for Python
|
|
||||||
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
from typing import List
|
|
||||||
|
|
||||||
import pyrogram
|
|
||||||
from .photo import Photo
|
|
||||||
from ..object import Object
|
|
||||||
|
|
||||||
|
|
||||||
class ProfilePhotos(Object):
|
|
||||||
"""Contains a user's profile pictures.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
total_count (``int``):
|
|
||||||
Total number of profile pictures the target user has.
|
|
||||||
|
|
||||||
profile_photos (List of :obj:`Photo`):
|
|
||||||
Requested profile pictures.
|
|
||||||
"""
|
|
||||||
|
|
||||||
__slots__ = ["total_count", "profile_photos"]
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
client: "pyrogram.BaseClient" = None,
|
|
||||||
total_count: int,
|
|
||||||
profile_photos: List[Photo]
|
|
||||||
):
|
|
||||||
super().__init__(client)
|
|
||||||
|
|
||||||
self.total_count = total_count
|
|
||||||
self.profile_photos = profile_photos
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _parse(client, photos) -> "ProfilePhotos":
|
|
||||||
return ProfilePhotos(
|
|
||||||
total_count=getattr(photos, "count", len(photos.photos)),
|
|
||||||
profile_photos=[Photo._parse(client, photo) for photo in photos.photos],
|
|
||||||
client=client
|
|
||||||
)
|
|
@ -95,7 +95,7 @@ class Sticker(Object):
|
|||||||
self.width = width
|
self.width = width
|
||||||
self.height = height
|
self.height = height
|
||||||
self.emoji = emoji
|
self.emoji = emoji
|
||||||
self.set_name = set_name,
|
self.set_name = set_name
|
||||||
self.thumbs = thumbs
|
self.thumbs = thumbs
|
||||||
# self.mask_position = mask_position
|
# self.mask_position = mask_position
|
||||||
|
|
||||||
|
@ -18,16 +18,13 @@
|
|||||||
|
|
||||||
from .chat import Chat
|
from .chat import Chat
|
||||||
from .chat_member import ChatMember
|
from .chat_member import ChatMember
|
||||||
from .chat_members import ChatMembers
|
|
||||||
from .chat_permissions import ChatPermissions
|
from .chat_permissions import ChatPermissions
|
||||||
from .chat_photo import ChatPhoto
|
from .chat_photo import ChatPhoto
|
||||||
from .chat_preview import ChatPreview
|
from .chat_preview import ChatPreview
|
||||||
from .dialog import Dialog
|
from .dialog import Dialog
|
||||||
from .dialogs import Dialogs
|
|
||||||
from .user import User
|
from .user import User
|
||||||
from .user_status import UserStatus
|
from .user_status import UserStatus
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"Chat", "ChatMember", "ChatMembers", "ChatPermissions", "ChatPhoto", "ChatPreview", "Dialog", "Dialogs", "User",
|
"Chat", "ChatMember", "ChatPermissions", "ChatPhoto", "ChatPreview", "Dialog", "User", "UserStatus"
|
||||||
"UserStatus"
|
|
||||||
]
|
]
|
||||||
|
@ -20,6 +20,7 @@ from typing import Union
|
|||||||
|
|
||||||
import pyrogram
|
import pyrogram
|
||||||
from pyrogram.api import types
|
from pyrogram.api import types
|
||||||
|
|
||||||
from .chat_permissions import ChatPermissions
|
from .chat_permissions import ChatPermissions
|
||||||
from .chat_photo import ChatPhoto
|
from .chat_photo import ChatPhoto
|
||||||
from ..object import Object
|
from ..object import Object
|
||||||
@ -36,14 +37,17 @@ class Chat(Object):
|
|||||||
Type of chat, can be either "private", "bot", "group", "supergroup" or "channel".
|
Type of chat, can be either "private", "bot", "group", "supergroup" or "channel".
|
||||||
|
|
||||||
is_verified (``bool``, *optional*):
|
is_verified (``bool``, *optional*):
|
||||||
True, if this chat has been verified by Telegram. Supergroups and channels only.
|
True, if this chat has been verified by Telegram. Supergroups, channels and bots only.
|
||||||
|
|
||||||
is_restricted (``bool``, *optional*):
|
is_restricted (``bool``, *optional*):
|
||||||
True, if this chat has been restricted. Supergroups and channels only.
|
True, if this chat has been restricted. Supergroups, channels and bots only.
|
||||||
See *restriction_reason* for details.
|
See *restriction_reason* for details.
|
||||||
|
|
||||||
is_scam (``bool``, *optional*):
|
is_scam (``bool``, *optional*):
|
||||||
True, if this chat has been flagged for scam. Supergroups and channels only.
|
True, if this chat has been flagged for scam. Supergroups, channels and bots only.
|
||||||
|
|
||||||
|
is_support (``bool``):
|
||||||
|
True, if this chat is part of the Telegram support team. Users and bots only.
|
||||||
|
|
||||||
title (``str``, *optional*):
|
title (``str``, *optional*):
|
||||||
Title, for supergroups, channels and basic group chats.
|
Title, for supergroups, channels and basic group chats.
|
||||||
@ -92,8 +96,8 @@ class Chat(Object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = [
|
__slots__ = [
|
||||||
"id", "type", "is_verified", "is_restricted", "is_scam", "title", "username", "first_name", "last_name",
|
"id", "type", "is_verified", "is_restricted", "is_scam", "is_support", "title", "username", "first_name",
|
||||||
"photo", "description", "invite_link", "pinned_message", "sticker_set_name", "can_set_sticker_set",
|
"last_name", "photo", "description", "invite_link", "pinned_message", "sticker_set_name", "can_set_sticker_set",
|
||||||
"members_count", "restriction_reason", "permissions"
|
"members_count", "restriction_reason", "permissions"
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -106,6 +110,7 @@ class Chat(Object):
|
|||||||
is_verified: bool = None,
|
is_verified: bool = None,
|
||||||
is_restricted: bool = None,
|
is_restricted: bool = None,
|
||||||
is_scam: bool = None,
|
is_scam: bool = None,
|
||||||
|
is_support: bool = None,
|
||||||
title: str = None,
|
title: str = None,
|
||||||
username: str = None,
|
username: str = None,
|
||||||
first_name: str = None,
|
first_name: str = None,
|
||||||
@ -127,6 +132,7 @@ class Chat(Object):
|
|||||||
self.is_verified = is_verified
|
self.is_verified = is_verified
|
||||||
self.is_restricted = is_restricted
|
self.is_restricted = is_restricted
|
||||||
self.is_scam = is_scam
|
self.is_scam = is_scam
|
||||||
|
self.is_support = is_support
|
||||||
self.title = title
|
self.title = title
|
||||||
self.username = username
|
self.username = username
|
||||||
self.first_name = first_name
|
self.first_name = first_name
|
||||||
@ -148,6 +154,10 @@ class Chat(Object):
|
|||||||
return Chat(
|
return Chat(
|
||||||
id=peer_id,
|
id=peer_id,
|
||||||
type="bot" if user.bot else "private",
|
type="bot" if user.bot else "private",
|
||||||
|
is_verified=getattr(user, "verified", None),
|
||||||
|
is_restricted=getattr(user, "restricted", None),
|
||||||
|
is_scam=getattr(user, "scam", None),
|
||||||
|
is_support=getattr(user, "support", None),
|
||||||
username=user.username,
|
username=user.username,
|
||||||
first_name=user.first_name,
|
first_name=user.first_name,
|
||||||
last_name=user.last_name,
|
last_name=user.last_name,
|
||||||
@ -257,3 +267,49 @@ class Chat(Object):
|
|||||||
return Chat._parse_user_chat(client, chat)
|
return Chat._parse_user_chat(client, chat)
|
||||||
else:
|
else:
|
||||||
return Chat._parse_channel_chat(client, chat)
|
return Chat._parse_channel_chat(client, chat)
|
||||||
|
|
||||||
|
async def archive(self):
|
||||||
|
"""Bound method *archive* of :obj:`Chat`.
|
||||||
|
|
||||||
|
Use as a shortcut for:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
client.archive_chats(-100123456789)
|
||||||
|
|
||||||
|
Example:
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
chat.archive()
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True on success.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RPCError: In case of a Telegram RPC error.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return await self._client.archive_chats(self.id)
|
||||||
|
|
||||||
|
async def unarchive(self):
|
||||||
|
"""Bound method *unarchive* of :obj:`Chat`.
|
||||||
|
|
||||||
|
Use as a shortcut for:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
client.unarchive_chats(-100123456789)
|
||||||
|
|
||||||
|
Example:
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
chat.unarchive()
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True on success.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RPCError: In case of a Telegram RPC error.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return await self._client.unarchive_chats(self.id)
|
||||||
|
@ -1,71 +0,0 @@
|
|||||||
# Pyrogram - Telegram MTProto API Client Library for Python
|
|
||||||
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
from typing import List
|
|
||||||
|
|
||||||
import pyrogram
|
|
||||||
from pyrogram.api import types
|
|
||||||
from .chat_member import ChatMember
|
|
||||||
from ..object import Object
|
|
||||||
|
|
||||||
|
|
||||||
class ChatMembers(Object):
|
|
||||||
"""Contains information about the members list of a chat.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
total_count (``int``):
|
|
||||||
Total number of members the chat has.
|
|
||||||
|
|
||||||
chat_members (List of :obj:`ChatMember <pyrogram.ChatMember>`):
|
|
||||||
Requested chat members.
|
|
||||||
"""
|
|
||||||
|
|
||||||
__slots__ = ["total_count", "chat_members"]
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
client: "pyrogram.BaseClient" = None,
|
|
||||||
total_count: int,
|
|
||||||
chat_members: List[ChatMember]
|
|
||||||
):
|
|
||||||
super().__init__(client)
|
|
||||||
|
|
||||||
self.total_count = total_count
|
|
||||||
self.chat_members = chat_members
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _parse(client, members):
|
|
||||||
users = {i.id: i for i in members.users}
|
|
||||||
chat_members = []
|
|
||||||
|
|
||||||
if isinstance(members, types.channels.ChannelParticipants):
|
|
||||||
total_count = members.count
|
|
||||||
members = members.participants
|
|
||||||
else:
|
|
||||||
members = members.full_chat.participants.participants
|
|
||||||
total_count = len(members)
|
|
||||||
|
|
||||||
for member in members:
|
|
||||||
chat_members.append(ChatMember._parse(client, member, users))
|
|
||||||
|
|
||||||
return ChatMembers(
|
|
||||||
total_count=total_count,
|
|
||||||
chat_members=chat_members,
|
|
||||||
client=client
|
|
||||||
)
|
|
@ -1,87 +0,0 @@
|
|||||||
# Pyrogram - Telegram MTProto API Client Library for Python
|
|
||||||
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
|
|
||||||
#
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
from typing import List
|
|
||||||
|
|
||||||
import pyrogram
|
|
||||||
from pyrogram.api import types
|
|
||||||
from .dialog import Dialog
|
|
||||||
from ..messages_and_media import Message
|
|
||||||
from ..object import Object
|
|
||||||
|
|
||||||
|
|
||||||
class Dialogs(Object):
|
|
||||||
"""Contains a user's dialogs chunk.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
total_count (``int``):
|
|
||||||
Total number of dialogs the user has.
|
|
||||||
|
|
||||||
dialogs (List of :obj:`Dialog`):
|
|
||||||
Requested dialogs.
|
|
||||||
"""
|
|
||||||
|
|
||||||
__slots__ = ["total_count", "dialogs"]
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
client: "pyrogram.BaseClient" = None,
|
|
||||||
total_count: int,
|
|
||||||
dialogs: List[Dialog]
|
|
||||||
):
|
|
||||||
super().__init__(client)
|
|
||||||
|
|
||||||
self.total_count = total_count
|
|
||||||
self.dialogs = dialogs
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
async def _parse(client, dialogs: types.messages.Dialogs) -> "Dialogs":
|
|
||||||
users = {i.id: i for i in dialogs.users}
|
|
||||||
chats = {i.id: i for i in dialogs.chats}
|
|
||||||
|
|
||||||
messages = {}
|
|
||||||
|
|
||||||
for message in dialogs.messages:
|
|
||||||
to_id = message.to_id
|
|
||||||
|
|
||||||
if isinstance(to_id, types.PeerUser):
|
|
||||||
if message.out:
|
|
||||||
chat_id = to_id.user_id
|
|
||||||
else:
|
|
||||||
chat_id = message.from_id
|
|
||||||
elif isinstance(to_id, types.PeerChat):
|
|
||||||
chat_id = -to_id.chat_id
|
|
||||||
else:
|
|
||||||
chat_id = int("-100" + str(to_id.channel_id))
|
|
||||||
|
|
||||||
messages[chat_id] = await Message._parse(client, message, users, chats)
|
|
||||||
|
|
||||||
parsed_dialogs = []
|
|
||||||
|
|
||||||
for dialog in dialogs.dialogs:
|
|
||||||
if not isinstance(dialog, types.Dialog):
|
|
||||||
continue
|
|
||||||
|
|
||||||
parsed_dialogs.append(Dialog._parse(client, dialog, messages, users, chats))
|
|
||||||
|
|
||||||
return Dialogs(
|
|
||||||
total_count=getattr(dialogs, "count", len(dialogs.dialogs)),
|
|
||||||
dialogs=parsed_dialogs,
|
|
||||||
client=client
|
|
||||||
)
|
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
import pyrogram
|
import pyrogram
|
||||||
from pyrogram.api import types
|
from pyrogram.api import types
|
||||||
|
|
||||||
from .chat_photo import ChatPhoto
|
from .chat_photo import ChatPhoto
|
||||||
from .user_status import UserStatus
|
from .user_status import UserStatus
|
||||||
from ..object import Object
|
from ..object import Object
|
||||||
@ -52,12 +53,12 @@ class User(Object):
|
|||||||
True, if this user has been restricted. Bots only.
|
True, if this user has been restricted. Bots only.
|
||||||
See *restriction_reason* for details.
|
See *restriction_reason* for details.
|
||||||
|
|
||||||
is_support (``bool``):
|
|
||||||
True, if this user is part of the Telegram support team.
|
|
||||||
|
|
||||||
is_scam (``bool``):
|
is_scam (``bool``):
|
||||||
True, if this user has been flagged for scam.
|
True, if this user has been flagged for scam.
|
||||||
|
|
||||||
|
is_support (``bool``):
|
||||||
|
True, if this user is part of the Telegram support team.
|
||||||
|
|
||||||
first_name (``str``):
|
first_name (``str``):
|
||||||
User's or bot's first name.
|
User's or bot's first name.
|
||||||
|
|
||||||
@ -86,7 +87,7 @@ class User(Object):
|
|||||||
|
|
||||||
__slots__ = [
|
__slots__ = [
|
||||||
"id", "is_self", "is_contact", "is_mutual_contact", "is_deleted", "is_bot", "is_verified", "is_restricted",
|
"id", "is_self", "is_contact", "is_mutual_contact", "is_deleted", "is_bot", "is_verified", "is_restricted",
|
||||||
"is_support", "is_scam", "first_name", "last_name", "status", "username", "language_code", "phone_number",
|
"is_scam", "is_support", "first_name", "last_name", "status", "username", "language_code", "phone_number",
|
||||||
"photo", "restriction_reason"
|
"photo", "restriction_reason"
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -102,8 +103,8 @@ class User(Object):
|
|||||||
is_bot: bool,
|
is_bot: bool,
|
||||||
is_verified: bool,
|
is_verified: bool,
|
||||||
is_restricted: bool,
|
is_restricted: bool,
|
||||||
is_support: bool,
|
|
||||||
is_scam: bool,
|
is_scam: bool,
|
||||||
|
is_support: bool,
|
||||||
first_name: str,
|
first_name: str,
|
||||||
last_name: str = None,
|
last_name: str = None,
|
||||||
status: UserStatus = None,
|
status: UserStatus = None,
|
||||||
@ -123,8 +124,8 @@ class User(Object):
|
|||||||
self.is_bot = is_bot
|
self.is_bot = is_bot
|
||||||
self.is_verified = is_verified
|
self.is_verified = is_verified
|
||||||
self.is_restricted = is_restricted
|
self.is_restricted = is_restricted
|
||||||
self.is_support = is_support
|
|
||||||
self.is_scam = is_scam
|
self.is_scam = is_scam
|
||||||
|
self.is_support = is_support
|
||||||
self.first_name = first_name
|
self.first_name = first_name
|
||||||
self.last_name = last_name
|
self.last_name = last_name
|
||||||
self.status = status
|
self.status = status
|
||||||
@ -148,8 +149,8 @@ class User(Object):
|
|||||||
is_bot=user.bot,
|
is_bot=user.bot,
|
||||||
is_verified=user.verified,
|
is_verified=user.verified,
|
||||||
is_restricted=user.restricted,
|
is_restricted=user.restricted,
|
||||||
is_support=user.support,
|
|
||||||
is_scam=user.scam,
|
is_scam=user.scam,
|
||||||
|
is_support=user.support,
|
||||||
first_name=user.first_name,
|
first_name=user.first_name,
|
||||||
last_name=user.last_name,
|
last_name=user.last_name,
|
||||||
status=UserStatus._parse(client, user.status, user.id, user.bot),
|
status=UserStatus._parse(client, user.status, user.id, user.bot),
|
||||||
@ -160,3 +161,49 @@ class User(Object):
|
|||||||
restriction_reason=user.restriction_reason,
|
restriction_reason=user.restriction_reason,
|
||||||
client=client
|
client=client
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def archive(self):
|
||||||
|
"""Bound method *archive* of :obj:`User`.
|
||||||
|
|
||||||
|
Use as a shortcut for:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
client.archive_chats(123456789)
|
||||||
|
|
||||||
|
Example:
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
user.archive()
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True on success.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RPCError: In case of a Telegram RPC error.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return await self._client.archive_chats(self.id)
|
||||||
|
|
||||||
|
async def unarchive(self):
|
||||||
|
"""Bound method *unarchive* of :obj:`User`.
|
||||||
|
|
||||||
|
Use as a shortcut for:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
client.unarchive_chats(123456789)
|
||||||
|
|
||||||
|
Example:
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
user.unarchive()
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True on success.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RPCError: In case of a Telegram RPC error.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return await self._client.unarchive_chats(self.id)
|
||||||
|
@ -23,10 +23,12 @@ from hashlib import sha1
|
|||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from os import urandom
|
from os import urandom
|
||||||
|
|
||||||
|
import pyrogram
|
||||||
from pyrogram.api import functions, types
|
from pyrogram.api import functions, types
|
||||||
from pyrogram.api.core import TLObject, Long, Int
|
from pyrogram.api.core import TLObject, Long, Int
|
||||||
from pyrogram.connection import Connection
|
from pyrogram.connection import Connection
|
||||||
from pyrogram.crypto import AES, RSA, Prime
|
from pyrogram.crypto import AES, RSA, Prime
|
||||||
|
|
||||||
from .internals import MsgId
|
from .internals import MsgId
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -35,11 +37,11 @@ log = logging.getLogger(__name__)
|
|||||||
class Auth:
|
class Auth:
|
||||||
MAX_RETRIES = 5
|
MAX_RETRIES = 5
|
||||||
|
|
||||||
def __init__(self, dc_id: int, test_mode: bool, ipv6: bool, proxy: dict):
|
def __init__(self, client: "pyrogram.Client", dc_id: int):
|
||||||
self.dc_id = dc_id
|
self.dc_id = dc_id
|
||||||
self.test_mode = test_mode
|
self.test_mode = client.storage.test_mode
|
||||||
self.ipv6 = ipv6
|
self.ipv6 = client.ipv6
|
||||||
self.proxy = proxy
|
self.proxy = client.proxy
|
||||||
|
|
||||||
self.connection = None
|
self.connection = None
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ from pyrogram.api.core import TLObject, MsgContainer, Int, Long, FutureSalt, Fut
|
|||||||
from pyrogram.connection import Connection
|
from pyrogram.connection import Connection
|
||||||
from pyrogram.crypto import MTProto
|
from pyrogram.crypto import MTProto
|
||||||
from pyrogram.errors import RPCError, InternalServerError, AuthKeyDuplicated
|
from pyrogram.errors import RPCError, InternalServerError, AuthKeyDuplicated
|
||||||
|
|
||||||
from .internals import MsgId, MsgFactory
|
from .internals import MsgId, MsgFactory
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -66,12 +67,14 @@ class Session:
|
|||||||
64: "[64] invalid container"
|
64: "[64] invalid container"
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(
|
||||||
client: pyrogram,
|
self,
|
||||||
dc_id: int,
|
client: pyrogram,
|
||||||
auth_key: bytes,
|
dc_id: int,
|
||||||
is_media: bool = False,
|
auth_key: bytes,
|
||||||
is_cdn: bool = False):
|
is_media: bool = False,
|
||||||
|
is_cdn: bool = False
|
||||||
|
):
|
||||||
if not Session.notice_displayed:
|
if not Session.notice_displayed:
|
||||||
print("Pyrogram v{}, {}".format(__version__, __copyright__))
|
print("Pyrogram v{}, {}".format(__version__, __copyright__))
|
||||||
print("Licensed under the terms of the " + __license__, end="\n\n")
|
print("Licensed under the terms of the " + __license__, end="\n\n")
|
||||||
@ -110,7 +113,12 @@ class Session:
|
|||||||
|
|
||||||
async def start(self):
|
async def start(self):
|
||||||
while True:
|
while True:
|
||||||
self.connection = Connection(self.dc_id, self.client.test_mode, self.client.ipv6, self.client.proxy)
|
self.connection = Connection(
|
||||||
|
self.dc_id,
|
||||||
|
self.client.storage.test_mode,
|
||||||
|
self.client.ipv6,
|
||||||
|
self.client.proxy
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await self.connection.connect()
|
await self.connection.connect()
|
||||||
|
5
setup.py
5
setup.py
@ -168,12 +168,13 @@ setup(
|
|||||||
python_requires="~=3.4",
|
python_requires="~=3.4",
|
||||||
packages=find_packages(exclude=["compiler*"]),
|
packages=find_packages(exclude=["compiler*"]),
|
||||||
package_data={
|
package_data={
|
||||||
"pyrogram.client.ext": ["mime.types"]
|
"pyrogram.client.ext": ["mime.types"],
|
||||||
|
"pyrogram.client.storage": ["schema.sql"]
|
||||||
},
|
},
|
||||||
zip_safe=False,
|
zip_safe=False,
|
||||||
install_requires=requires,
|
install_requires=requires,
|
||||||
extras_require={
|
extras_require={
|
||||||
"fast": ["tgcrypto==1.1.1"]
|
"fast": ["tgcrypto==1.2.0"]
|
||||||
},
|
},
|
||||||
cmdclass={
|
cmdclass={
|
||||||
"clean": Clean,
|
"clean": Clean,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user