Merge branch 'master' into docs

This commit is contained in:
Dan 2018-02-27 14:57:21 +01:00
commit e44fd408f2
12 changed files with 407 additions and 13 deletions

View File

@ -42,8 +42,10 @@ CDN_METHOD_INVALID The method can't be used on CDN DCs
VOLUME_LOC_NOT_FOUND The volume location can't be found VOLUME_LOC_NOT_FOUND The volume location can't be found
FILE_ID_INVALID The file id is invalid FILE_ID_INVALID The file id is invalid
LOCATION_INVALID The file location is invalid LOCATION_INVALID The file location is invalid
CHAT_ADMIN_REQUIRED The method requires admin privileges CHAT_ADMIN_REQUIRED The method requires chat admin privileges
PHONE_NUMBER_BANNED The phone number is banned PHONE_NUMBER_BANNED The phone number is banned
ABOUT_TOO_LONG The about text is too long ABOUT_TOO_LONG The about text is too long
MULTI_MEDIA_TOO_LONG The album contains more than 10 items MULTI_MEDIA_TOO_LONG The album contains more than 10 items
USERNAME_OCCUPIED The username is already in use USERNAME_OCCUPIED The username is already in use
BOT_INLINE_DISABLED The inline feature of the bot is disabled
INLINE_RESULT_EXPIRED The inline bot query expired
1 id message
42 VOLUME_LOC_NOT_FOUND The volume location can't be found
43 FILE_ID_INVALID The file id is invalid
44 LOCATION_INVALID The file location is invalid
45 CHAT_ADMIN_REQUIRED The method requires admin privileges The method requires chat admin privileges
46 PHONE_NUMBER_BANNED The phone number is banned
47 ABOUT_TOO_LONG The about text is too long
48 MULTI_MEDIA_TOO_LONG The album contains more than 10 items
49 USERNAME_OCCUPIED The username is already in use
50 BOT_INLINE_DISABLED The inline feature of the bot is disabled
51 INLINE_RESULT_EXPIRED The inline bot query expired

14
examples/README.md Normal file
View File

@ -0,0 +1,14 @@
# Examples
This folder contains example scripts to show you how **Pyrogram** looks like.
You can start with [hello_world.py](https://github.com/pyrogram/pyrogram/blob/master/examples/hello_world.py) and continue
with the more advanced examples. Every script is working right away, meaning you can simply copy-paste and run, the only things
you have to change are the target chats (username, id) and file paths for sending media (photo, video, ...).
- [**hello_world.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/hello_world.py)
- [**get_history.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/get_history.py)
- [**get_participants.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/get_participants.py)
- [**updates.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/updates.py)
- [**simple_echo.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/simple_echo.py)
- [**advanced_echo.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/advanced_echo.py)
- [**advanced_echo2.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/advanced_echo2.py)

64
examples/advanced_echo.py Normal file
View File

@ -0,0 +1,64 @@
from pyrogram import Client
from pyrogram.api import types
"""This is a more advanced example bot that will reply to all private and basic groups text messages
by also mentioning the Users.
Beware! This script will make you reply to ALL new messages in private chats and in every basic group you are in.
Make sure you add an extra check to filter them:
# Filter Groups by ID
if message.to_id.chat_id == MY_GROUP_ID:
...
"""
def update_handler(client, update, users, chats):
if isinstance(update, types.UpdateNewMessage): # Filter by UpdateNewMessage (PM and Chats)
message = update.message
if isinstance(message, types.Message): # Filter by Message to exclude MessageService and MessageEmpty
if isinstance(message.to_id, types.PeerUser): # Private Messages
text = '[{}](tg://user?id={}) said "{}" to me ([{}](tg://user?id={}))'.format(
users[message.from_id].first_name,
users[message.from_id].id,
message.message,
users[message.to_id.user_id].first_name,
users[message.to_id.user_id].id
)
client.send_message(
message.from_id, # Send the message to the private chat (from_id)
text,
reply_to_message_id=message.id
)
else: # Group chats
text = '[{}](tg://user?id={}) said "{}" in **{}** group'.format(
users[message.from_id].first_name,
users[message.from_id].id,
message.message,
chats[message.to_id.chat_id].title
)
client.send_message(
message.to_id, # Send the message to the group chat (to_id)
text,
reply_to_message_id=message.id
)
def main():
# Pyrogram setup
client = Client("example")
# Set the update_handler callback function
client.set_update_handler(update_handler)
client.start()
# Blocks the program execution until you press CTRL+C then
# automatically stops the Client by closing the underlying connection
client.idle()
if __name__ == "__main__":
main()

View File

@ -0,0 +1,55 @@
from pyrogram import Client
from pyrogram.api import types
"""This example is similar to advanced_echo.py, except for the fact that it will reply to Supergroup text messages only.
Beware! This script will make you reply to ALL new messages in every single supergroup you are in.
Make sure you add an extra check to filter them:
# Filter Supergroups by ID
if message.to_id.channel_id == MY_SUPERGROUP_ID:
...
# Filter Supergroups by Username
if chats[message.to_id.channel_id].username == MY_SUPERGROUP_USERNAME:
...
"""
def update_handler(client, update, users, chats):
# Channels and Supergroups share the same type (Channel). The .megagroup field is used to tell them apart, and is
# True for Supegroups, False for Channels.
if isinstance(update, types.UpdateNewChannelMessage): # Filter by UpdateNewChannelMessage (Channels/Supergroups)
message = update.message
if isinstance(message, types.Message): # Filter by Message to exclude MessageService and MessageEmpty
if chats[message.to_id.channel_id].megagroup: # Only handle messages from Supergroups not Channels
text = '[{}](tg://user?id={}) said "{}" in **{}** supergroup'.format(
users[message.from_id].first_name,
users[message.from_id].id,
message.message,
chats[message.to_id.channel_id].title
)
client.send_message(
message.to_id,
text,
reply_to_message_id=message.id
)
def main():
# Pyrogram setup
client = Client("example")
# Set the update_handler callback function
client.set_update_handler(update_handler)
client.start()
# Blocks the program execution until you press CTRL+C then
# automatically stops the Client by closing the underlying connection
client.idle()
if __name__ == "__main__":
main()

BIN
examples/data/pyrogram.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

37
examples/get_history.py Normal file
View File

@ -0,0 +1,37 @@
import time
from pyrogram import Client
from pyrogram.api import functions
from pyrogram.api.errors import FloodWait
client = Client("example")
client.start()
target = "me" # "me" refers to your own chat (Saved Messages)
history = [] # List that will contain all the messages of the target chat
limit = 100 # Amount of messages to retrieve for each API call
offset = 0 # Offset starts at 0
while True:
try:
messages = client.send(
functions.messages.GetHistory(
client.resolve_peer(target),
0, 0, offset, limit, 0, 0, 0
)
)
except FloodWait as e:
# For very large chats the method call can raise a FloodWait
time.sleep(e.x) # Sleep X seconds before continuing
continue
if not messages.messages:
break # No more messages left
history.extend(messages.messages)
offset += limit
client.stop()
# Now the "history" list contains all the messages sorted by date in
# descending order (from the most recent to the oldest one)

View File

@ -0,0 +1,40 @@
import time
from pyrogram import Client
from pyrogram.api import functions, types
from pyrogram.api.errors import FloodWait
client = Client("example")
client.start()
target = "username" # Target channel/supergroup
users = [] # List that will contain all the users of the target chat
limit = 200 # Amount of users to retrieve for each API call
offset = 0 # Offset starts at 0
while True:
try:
participants = client.send(
functions.channels.GetParticipants(
channel=client.resolve_peer(target),
filter=types.ChannelParticipantsSearch(""), # Filter by empty string (search for all)
offset=offset,
limit=limit,
hash=0
)
)
except FloodWait as e:
# Very large channels will trigger FloodWait.
# When happens, wait X seconds before continuing
time.sleep(e.x)
continue
if not participants.participants:
break # No more participants left
users.extend(participants.users)
offset += limit
client.stop()
# Now the "users" list contains all the members of the target chat

19
examples/hello_world.py Normal file
View File

@ -0,0 +1,19 @@
from pyrogram import Client
# Create a new Client
client = Client("example")
# Start the Client
client.start()
# Send a message to yourself, Markdown is enabled by default
client.send_message("me", "Hi there! I'm using **Pyrogram**")
# Send a photo with a formatted caption to yourself
client.send_photo("me", "data/pyrogram.png", "__This is a formatted__ **caption**")
# Send a location to yourself
client.send_location("me", 51.500729, -0.124583)
# Stop the client
client.stop()

15
examples/inline_bots.py Normal file
View File

@ -0,0 +1,15 @@
from pyrogram import Client
# Create a new Client
client = Client("example")
# Start the Client
client.start()
# Get bot results for "Fuzz Universe" from the inline bot @vid
bot_results = client.get_inline_bot_results("vid", "Fuzz Universe")
# Send the first result (bot_results.results[0]) to your own chat (Saved Messages)
client.send_inline_bot_result("me", bot_results.query_id, bot_results.results[0].id)
# Stop the client
client.stop()

34
examples/simple_echo.py Normal file
View File

@ -0,0 +1,34 @@
from pyrogram import Client
from pyrogram.api import types
"""This simple example bot will reply to all private text messages"""
def update_handler(client, update, users, chats):
if isinstance(update, types.UpdateNewMessage): # Filter by UpdateNewMessage (Private Messages)
message = update.message # type: types.Message
if isinstance(message, types.Message): # Filter by Message to exclude MessageService and MessageEmpty
if isinstance(message.to_id, types.PeerUser): # Private Messages (Message from user)
client.send_message(
chat_id=message.from_id,
text=message.message,
reply_to_message_id=message.id
)
def main():
# Pyrogram setup
client = Client("example")
# Set the update_handler callback function
client.set_update_handler(update_handler)
client.start()
# Blocks the program execution until you press CTRL+C then
# automatically stops the Client by closing the underlying connection
client.idle()
if __name__ == "__main__":
main()

25
examples/updates.py Normal file
View File

@ -0,0 +1,25 @@
from pyrogram import Client
# This function will be called every time a new Update is received from Telegram
def update_handler(client, update, users, chats):
# Send EVERY update that arrives to your own chat (Saved Messages)
# Use triple backticks to make the text look nicer.
client.send_message("me", "```\n" + str(update) + "```")
def main():
# Pyrogram setup
client = Client("example")
# Set the update_handler callback function
client.set_update_handler(update_handler)
client.start()
# Blocks the program execution until you press CTRL+C then
# automatically stops the Client by closing the underlying connection
client.idle()
if __name__ == "__main__":
main()

View File

@ -159,7 +159,7 @@ class Client:
self.session = None self.session = None
self.is_idle = Event() self.is_idle = None
self.updates_queue = Queue() self.updates_queue = Queue()
self.update_queue = Queue() self.update_queue = Queue()
@ -311,7 +311,8 @@ class Client:
if not file_name: if not file_name:
file_name = "doc_{}{}".format( file_name = "doc_{}{}".format(
datetime.fromtimestamp(document.date).strftime("%Y-%m-%d_%H-%M-%S"), datetime.fromtimestamp(document.date).strftime("%Y-%m-%d_%H-%M-%S"),
mimetypes.guess_extension(document.mime_type) or ".unknown" ".txt" if document.mime_type == "text/plain" else
mimetypes.guess_extension(document.mime_type) if document.mime_type else ".unknown"
) )
for i in document.attributes: for i in document.attributes:
@ -370,7 +371,6 @@ class Client:
except Exception as e: except Exception as e:
log.error(e, exc_info=True) log.error(e, exc_info=True)
finally: finally:
print(done)
done.set() done.set()
try: try:
@ -472,7 +472,7 @@ class Client:
def signal_handler(self, *args): def signal_handler(self, *args):
self.stop() self.stop()
self.is_idle.set() self.is_idle = False
def idle(self, stop_signals: tuple = (SIGINT, SIGTERM, SIGABRT)): def idle(self, stop_signals: tuple = (SIGINT, SIGTERM, SIGABRT)):
"""Blocks the program execution until one of the signals are received, """Blocks the program execution until one of the signals are received,
@ -486,7 +486,10 @@ class Client:
for s in stop_signals: for s in stop_signals:
signal(s, self.signal_handler) signal(s, self.signal_handler)
self.is_idle.wait() self.is_idle = True
while self.is_idle:
time.sleep(1)
def set_update_handler(self, callback: callable): def set_update_handler(self, callback: callable):
"""Use this method to set the update handler. """Use this method to set the update handler.
@ -543,7 +546,12 @@ class Client:
Raises: Raises:
:class:`pyrogram.Error` :class:`pyrogram.Error`
""" """
return self.session.send(data) r = self.session.send(data)
self.fetch_peers(getattr(r, "users", []))
self.fetch_peers(getattr(r, "chats", []))
return r
def authorize(self): def authorize(self):
phone_number_invalid_raises = self.phone_number is not None phone_number_invalid_raises = self.phone_number is not None
@ -769,9 +777,6 @@ class Client:
def get_dialogs(self): def get_dialogs(self):
def parse_dialogs(d): def parse_dialogs(d):
self.fetch_peers(d.chats)
self.fetch_peers(d.users)
for m in reversed(d.messages): for m in reversed(d.messages):
if isinstance(m, types.MessageEmpty): if isinstance(m, types.MessageEmpty):
continue continue
@ -2510,8 +2515,6 @@ class Client:
) )
) )
self.fetch_peers(imported_contacts.users)
return imported_contacts return imported_contacts
def delete_contacts(self, ids: list): def delete_contacts(self, ids: list):
@ -2556,6 +2559,92 @@ class Client:
else: else:
if isinstance(contacts, types.contacts.Contacts): if isinstance(contacts, types.contacts.Contacts):
log.info("Contacts count: {}".format(len(contacts.users))) log.info("Contacts count: {}".format(len(contacts.users)))
self.fetch_peers(contacts.users)
return contacts return contacts
def get_inline_bot_results(self,
bot: int or str,
query: str,
offset: str = "",
location: tuple = None):
"""Use this method to get bot results via inline queries.
You can then send a result using :obj:`send_inline_bot_result <pyrogram.Client.send_inline_bot_result>`
Args:
bot (:obj:`int` | :obj:`str`):
Unique identifier of the inline bot you want to get results from. You can specify
a @username (str) or a bot ID (int).
query (:obj:`str`):
Text of the query (up to 512 characters).
offset (:obj:`str`):
Offset of the results to be returned.
location (:obj:`tuple`, optional):
Your location in tuple format (latitude, longitude), e.g.: (51.500729, -0.124583).
Useful for location-based results only.
Returns:
On Success, `BotResults <pyrogram.api.types.messages.BotResults>`_ is returned.
Raises:
:class:`pyrogram.Error`
"""
return self.send(
functions.messages.GetInlineBotResults(
bot=self.resolve_peer(bot),
peer=types.InputPeerSelf(),
query=query,
offset=offset,
geo_point=types.InputGeoPoint(
lat=location[0],
long=location[1]
) if location else None
)
)
def send_inline_bot_result(self,
chat_id: int or str,
query_id: int,
result_id: str,
disable_notification: bool = None,
reply_to_message_id: int = None):
"""Use this method to send an inline bot result.
Bot results can be retrieved using :obj:`get_inline_bot_results <pyrogram.Client.get_inline_bot_results>`
Args:
chat_id (:obj:`int` | :obj:`str`):
Unique identifier for the target chat or username of the target channel/supergroup
(in the format @username). For your personal cloud storage (Saved Messages) you can
simply use "me" or "self". Phone numbers that exist in your Telegram address book are also supported.
query_id (:obj:`int`):
Unique identifier for the answered query.
result_id (:obj:`str`):
Unique identifier for the result that was chosen.
disable_notification (:obj:`bool`, optional):
Sends the message silently.
Users will receive a notification with no sound.
reply_to_message_id (:obj:`bool`, optional):
If the message is a reply, ID of the original message.
Returns:
On success, the sent Message is returned.
Raises:
:class:`pyrogram.Error`
"""
return self.send(
functions.messages.SendInlineBotResult(
peer=self.resolve_peer(chat_id),
query_id=query_id,
id=result_id,
random_id=self.rnd_id(),
silent=disable_notification or None,
reply_to_msg_id=reply_to_message_id
)
)