From 0c5f5738a54cf2a44b9dfda9e7e4afe5fd880d3c Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sat, 13 Apr 2019 13:32:16 +0200 Subject: [PATCH 1/7] Add ACCESS_TOKEN_INVALID error --- compiler/error/source/400_BAD_REQUEST.tsv | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/error/source/400_BAD_REQUEST.tsv b/compiler/error/source/400_BAD_REQUEST.tsv index a71976b0..8325040d 100644 --- a/compiler/error/source/400_BAD_REQUEST.tsv +++ b/compiler/error/source/400_BAD_REQUEST.tsv @@ -95,4 +95,5 @@ PHOTO_EXT_INVALID The photo extension is invalid EXTERNAL_URL_INVALID The external media URL is invalid CHAT_NOT_MODIFIED The chat settings were not modified RESULTS_TOO_MUCH The result contains too many items -RESULT_ID_DUPLICATE The result contains items with duplicated identifiers \ No newline at end of file +RESULT_ID_DUPLICATE The result contains items with duplicated identifiers +ACCESS_TOKEN_INVALID The bot access token is invalid \ No newline at end of file From 88078d45738f9c265e7852b98f966dec571ff955 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sat, 13 Apr 2019 14:03:01 +0200 Subject: [PATCH 2/7] Simplify user and chat filters implementation --- pyrogram/client/filters/filters.py | 45 +++++++++++++++--------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/pyrogram/client/filters/filters.py b/pyrogram/client/filters/filters.py index f5bcd5b5..31edb181 100644 --- a/pyrogram/client/filters/filters.py +++ b/pyrogram/client/filters/filters.py @@ -311,21 +311,20 @@ class Filters: def __init__(self, users: int or str or list = None): users = [] if users is None else users if type(users) is list else [users] + super().__init__( - {"me" if i in ["me", "self"] else i.lower().strip("@") if type(i) is str else i for i in users} - if type(users) is list else - {"me" if users in ["me", "self"] else users.lower().strip("@") if type(users) is str else users} + "me" if u in ["me", "self"] + else u.lower().strip("@") if type(u) is str + else u for u in users ) def __call__(self, message): - return bool( - message.from_user - and (message.from_user.id in self - or (message.from_user.username - and message.from_user.username.lower() in self) - or ("me" in self - and message.from_user.is_self)) - ) + return (message.from_user + and (message.from_user.id in self + or (message.from_user.username + and message.from_user.username.lower() in self) + or ("me" in self + and message.from_user.is_self))) # noinspection PyPep8Naming class chat(Filter, set): @@ -343,21 +342,21 @@ class Filters: def __init__(self, chats: int or str or list = None): chats = [] if chats is None else chats if type(chats) is list else [chats] + super().__init__( - {"me" if i in ["me", "self"] else i.lower().strip("@") if type(i) is str else i for i in chats} - if type(chats) is list else - {"me" if chats in ["me", "self"] else chats.lower().strip("@") if type(chats) is str else chats} + "me" if c in ["me", "self"] + else c.lower().strip("@") if type(c) is str + else c for c in chats ) def __call__(self, message): - return bool( - message.chat - and (message.chat.id in self - or (message.chat.username - and message.chat.username.lower() in self) - or ("me" in self and message.from_user - and message.from_user.is_self - and not message.outgoing)) - ) + return (message.chat + and (message.chat.id in self + or (message.chat.username + and message.chat.username.lower() in self) + or ("me" in self + and message.from_user + and message.from_user.is_self + and not message.outgoing))) dan = create("Dan", lambda _, m: bool(m.from_user and m.from_user.id == 23122162)) From 292a6ea7bf3bf60787219ff403bec8229ec1eb76 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sat, 13 Apr 2019 14:21:53 +0200 Subject: [PATCH 3/7] Refactor the command filter. Also allow it to work on media captions as well --- pyrogram/client/filters/filters.py | 50 ++++++++++++++---------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/pyrogram/client/filters/filters.py b/pyrogram/client/filters/filters.py index 31edb181..9aecb79a 100644 --- a/pyrogram/client/filters/filters.py +++ b/pyrogram/client/filters/filters.py @@ -222,14 +222,16 @@ class Filters: - poll""" @staticmethod - def command(command: str or list, - prefix: str or list = "/", - separator: str = " ", - case_sensitive: bool = False): + def command( + commands: str or list, + prefix: str or list = "/", + separator: str = " ", + case_sensitive: bool = False + ): """Filter commands, i.e.: text messages starting with "/" or any other custom prefix. Args: - command (``str`` | ``list``): + commands (``str`` | ``list``): The command or list of commands as string the filter should look for. Examples: "start", ["start", "help", "settings"]. When a message text containing a command arrives, the command itself and its arguments will be stored in the *command* @@ -249,31 +251,25 @@ class Filters: Examples: when True, command="Start" would trigger /Start but not /start. """ - def f(_, m): - if m.text: - for i in _.p: - if m.text.startswith(i): - t = m.text.split(_.s) - c, a = t[0][len(i):], t[1:] - c = c if _.cs else c.lower() - m.command = ([c] + a) if c in _.c else None + def func(flt, message): + text = message.text or message.caption + + if text: + for p in flt.p: + if text.startswith(p): + s = text.split(flt.s) + c, a = s[0][len(p):], s[1:] + c = c if flt.cs else c.lower() + message.command = ([c] + a) if c in flt.c else None break - return bool(m.command) + return bool(message.command) - return create( - "Command", - f, - c={command if case_sensitive - else command.lower()} - if not isinstance(command, list) - else {c if case_sensitive - else c.lower() - for c in command}, - p=set(prefix) if prefix else {""}, - s=separator, - cs=case_sensitive - ) + commands = commands if type(commands) is list else [commands] + commands = {c if case_sensitive else c.lower() for c in commands} + prefixes = set(prefix) if prefix else {""} + + return create("Command", func=func, c=commands, p=prefixes, s=separator, cs=case_sensitive) @staticmethod def regex(pattern, flags: int = 0): From 4d1abbbb79ce9f30377b216c98353dc8547f0aec Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sat, 13 Apr 2019 14:41:14 +0200 Subject: [PATCH 4/7] Make the deprecation warning actually work --- pyrogram/client/client.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py index d2d3cec5..44aef5df 100644 --- a/pyrogram/client/client.py +++ b/pyrogram/client/client.py @@ -29,7 +29,6 @@ import struct import tempfile import threading import time -import warnings from configparser import ConfigParser from datetime import datetime from hashlib import sha256, md5 @@ -41,6 +40,10 @@ from typing import Union, List from pyrogram.api import functions, types from pyrogram.api.core import Object +from pyrogram.client.handlers import DisconnectHandler +from pyrogram.client.handlers.handler import Handler +from pyrogram.client.methods.password.utils import compute_check +from pyrogram.crypto import AES from pyrogram.errors import ( PhoneMigrate, NetworkMigrate, PhoneNumberInvalid, PhoneNumberUnoccupied, PhoneCodeInvalid, PhoneCodeHashEmpty, @@ -49,10 +52,6 @@ from pyrogram.errors import ( VolumeLocNotFound, UserMigrate, FileIdInvalid, ChannelPrivate, PhoneNumberOccupied, PasswordRecoveryNa, PasswordEmpty ) -from pyrogram.client.handlers import DisconnectHandler -from pyrogram.client.handlers.handler import Handler -from pyrogram.client.methods.password.utils import compute_check -from pyrogram.crypto import AES from pyrogram.session import Auth, Session from .ext import utils, Syncer, BaseClient, Dispatcher from .methods import Methods @@ -276,10 +275,10 @@ class Client(Methods, BaseClient): self.is_bot = True self.bot_token = self.session_name self.session_name = self.session_name.split(":")[0] - warnings.warn('\nYou are using a bot token as session name.\n' - 'It will be deprecated in next update, please use session file name to load ' - 'existing sessions and bot_token argument to create new sessions.', - DeprecationWarning, stacklevel=2) + 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.ml/start/Setup#bot-authorization\n') self.load_config() self.load_session() From 6ad9caa7c629ffe6e69c2d653d5543d4451e5c43 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sat, 13 Apr 2019 15:54:19 +0200 Subject: [PATCH 5/7] Automatically cast inline result ids to string --- .../client/types/inline_mode/inline_query_result_article.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pyrogram/client/types/inline_mode/inline_query_result_article.py b/pyrogram/client/types/inline_mode/inline_query_result_article.py index 7916dba5..8d0089c3 100644 --- a/pyrogram/client/types/inline_mode/inline_query_result_article.py +++ b/pyrogram/client/types/inline_mode/inline_query_result_article.py @@ -16,6 +16,8 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . +from typing import Any + from pyrogram.api import types from .inline_query_result import InlineQueryResult @@ -61,7 +63,7 @@ class InlineQueryResultArticle(InlineQueryResult): def __init__( self, - id: str, + id: Any, title: str, input_message_content, reply_markup=None, @@ -84,7 +86,7 @@ class InlineQueryResultArticle(InlineQueryResult): def write(self): return types.InputBotInlineResult( - id=self.id, + id=str(self.id), type=self.type, send_message=self.input_message_content.write(self.reply_markup), title=self.title, From 0d5724164cd77cd5f2e63d3ce3d79332eced6d0b Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sat, 13 Apr 2019 16:02:31 +0200 Subject: [PATCH 6/7] Update examples --- examples/README.md | 7 +-- examples/callback_queries.py | 4 +- examples/chat_members.py | 2 +- examples/{echo.py => echobot.py} | 0 examples/{hello.py => hello_world.py} | 0 examples/inline_queries.py | 54 +++++++++++++++++++ examples/keyboards.py | 10 ++-- .../{inline_bots.py => using_inline_bots.py} | 0 8 files changed, 65 insertions(+), 12 deletions(-) rename examples/{echo.py => echobot.py} (100%) rename examples/{hello.py => hello_world.py} (100%) create mode 100644 examples/inline_queries.py rename examples/{inline_bots.py => using_inline_bots.py} (100%) diff --git a/examples/README.md b/examples/README.md index 6f56ab89..643fe56d 100644 --- a/examples/README.md +++ b/examples/README.md @@ -10,13 +10,14 @@ can be freely used as basic building blocks for your own applications without wo Example | Description ---: | :--- -[**hello**](hello.py) | Demonstration of basic API usage -[**echo**](echo.py) | Reply to every private text message +[**hello_world**](hello_world.py) | Demonstration of basic API usage +[**echobot**](echobot.py) | Echo every private text message [**welcome**](welcome.py) | The Welcome Bot in [@PyrogramChat](https://t.me/pyrogramchat) [**history**](history.py) | Get the full message history of a chat [**chat_members**](chat_members.py) | Get all the members of a chat [**dialogs**](dialogs.py) | Get all of your dialog chats -[**inline_bots**](inline_bots.py) | Query an inline bot and send a result to a chat +[**using_inline_bots**](using_inline_bots.py) | Query an inline bot (as user) and send a result to a chat [**keyboards**](keyboards.py) | Send normal and inline keyboards using regular bots [**callback_queries**](callback_queries.py) | Handle queries coming from inline button presses +[**inline_queries**](inline_queries.py) | Handle inline queries [**raw_updates**](raw_updates.py) | Handle raw updates (old, should be avoided) diff --git a/examples/callback_queries.py b/examples/callback_queries.py index 71538eae..f4a87b00 100644 --- a/examples/callback_queries.py +++ b/examples/callback_queries.py @@ -5,12 +5,12 @@ It uses the @on_callback_query decorator to register a CallbackQueryHandler. from pyrogram import Client -app = Client("123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11") +app = Client("my_bot", bot_token="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11") @app.on_callback_query() def answer(client, callback_query): - callback_query.answer('Button contains: "{}"'.format(callback_query.data), show_alert=True) + callback_query.answer("Button contains: '{}'".format(callback_query.data), show_alert=True) app.run() # Automatically start() and idle() diff --git a/examples/chat_members.py b/examples/chat_members.py index 87f8613d..468ac7de 100644 --- a/examples/chat_members.py +++ b/examples/chat_members.py @@ -2,7 +2,7 @@ from pyrogram import Client -app = Client("my_count") +app = Client("my_account") target = "pyrogramchat" # Target channel/supergroup with app: diff --git a/examples/echo.py b/examples/echobot.py similarity index 100% rename from examples/echo.py rename to examples/echobot.py diff --git a/examples/hello.py b/examples/hello_world.py similarity index 100% rename from examples/hello.py rename to examples/hello_world.py diff --git a/examples/inline_queries.py b/examples/inline_queries.py new file mode 100644 index 00000000..c1727fe6 --- /dev/null +++ b/examples/inline_queries.py @@ -0,0 +1,54 @@ +"""This example shows how to handle inline queries. +Two results are generated when users invoke the bot inline mode, e.g.: @pyrogrambot hi. +It uses the @on_inline_query decorator to register an InlineQueryHandler. +""" + +from uuid import uuid4 + +from pyrogram import ( + Client, InlineQueryResultArticle, InputTextMessageContent, InlineKeyboardMarkup, InlineKeyboardButton +) + +app = Client("my_bot", bot_token="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11") + + +@app.on_inline_query() +def answer(client, inline_query): + inline_query.answer( + results=[ + InlineQueryResultArticle( + id=uuid4(), + title="Installation", + input_message_content=InputTextMessageContent( + "Here's how to install **Pyrogram**" + ), + url="https://docs.pyrogram.ml/start/Installation", + description="How to install Pyrogram", + thumb_url="https://i.imgur.com/JyxrStE.png", + reply_markup=InlineKeyboardMarkup( + [ + [InlineKeyboardButton("Open website", url="https://docs.pyrogram.ml/start/Installation")] + ] + ) + ), + InlineQueryResultArticle( + id=uuid4(), + title="Usage", + input_message_content=InputTextMessageContent( + "Here's how to use **Pyrogram**" + ), + url="https://docs.pyrogram.ml/start/Usage", + description="How to use Pyrogram", + thumb_url="https://i.imgur.com/JyxrStE.png", + reply_markup=InlineKeyboardMarkup( + [ + [InlineKeyboardButton("Open website", url="https://docs.pyrogram.ml/start/Usage")] + ] + ) + ) + ], + cache_time=1 + ) + + +app.run() # Automatically start() and idle() diff --git a/examples/keyboards.py b/examples/keyboards.py index 147154a3..1a1140b6 100644 --- a/examples/keyboards.py +++ b/examples/keyboards.py @@ -10,7 +10,7 @@ like send_audio(), send_document(), send_location(), etc... from pyrogram import Client, ReplyKeyboardMarkup, InlineKeyboardMarkup, InlineKeyboardButton # Create a client using your bot token -app = Client("123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11") +app = Client("my_bot", bot_token="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11") with app: app.send_message( @@ -33,19 +33,17 @@ with app: reply_markup=InlineKeyboardMarkup( [ [ # First row - InlineKeyboardButton( # Generates a callback query when pressed "Button", - callback_data=b"data" - ), # Note how callback_data must be bytes + callback_data=b"data" # Note how callback_data must be bytes + ), InlineKeyboardButton( # Opens a web URL "URL", url="https://docs.pyrogram.ml" ), ], [ # Second row - # Opens the inline interface - InlineKeyboardButton( + InlineKeyboardButton( # Opens the inline interface "Choose chat", switch_inline_query="pyrogram" ), diff --git a/examples/inline_bots.py b/examples/using_inline_bots.py similarity index 100% rename from examples/inline_bots.py rename to examples/using_inline_bots.py From 1e635f00ea95d10c9b28e7abfc488f1cfaef29cf Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sat, 13 Apr 2019 16:10:15 +0200 Subject: [PATCH 7/7] Fix set_chat_description not working anymore with the new Layer --- pyrogram/client/methods/chats/set_chat_description.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pyrogram/client/methods/chats/set_chat_description.py b/pyrogram/client/methods/chats/set_chat_description.py index ebe15bf2..9d4e130b 100644 --- a/pyrogram/client/methods/chats/set_chat_description.py +++ b/pyrogram/client/methods/chats/set_chat_description.py @@ -47,15 +47,13 @@ class SetChatDescription(BaseClient): """ peer = self.resolve_peer(chat_id) - if isinstance(peer, types.InputPeerChannel): + if isinstance(peer, (types.InputPeerChannel, types.InputPeerChat)): self.send( - functions.channels.EditAbout( - channel=peer, + functions.messages.EditChatAbout( + peer=peer, about=description ) ) - elif isinstance(peer, types.InputPeerChat): - raise ValueError("The chat_id \"{}\" belongs to a basic group".format(chat_id)) else: raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id))