From 682e65018707b5a07290e21a0ae8d80d462d5d9c Mon Sep 17 00:00:00 2001 From: Lonami Exo Date: Sun, 29 Jul 2018 12:56:11 +0200 Subject: [PATCH 1/4] Create a basic InlineResult class --- telethon/tl/custom/inlineresult.py | 135 +++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 telethon/tl/custom/inlineresult.py diff --git a/telethon/tl/custom/inlineresult.py b/telethon/tl/custom/inlineresult.py new file mode 100644 index 00000000..f0b1bb9a --- /dev/null +++ b/telethon/tl/custom/inlineresult.py @@ -0,0 +1,135 @@ +from .. import types, functions +from ... import utils + + +class InlineResult: + """ + Custom class that encapsulates a bot inline result providing + an abstraction to easily access some commonly needed features + (such as clicking a result to select it). + + Attributes: + + result (:tl:`BotInlineResult`): + The original :tl:`BotInlineResult` object. + """ + ARTICLE = 'article' + PHOTO = 'photo' + GIF = 'gif' + VIDEO = 'video' + VIDEO_GIF = 'mpeg4_gif' + AUDIO = 'audio' + DOCUMENT = 'document' + LOCATION = 'location' + VENUE = 'venue' + CONTACT = 'contact' + GAME = 'game' + + def __init__(self, client, original, query_id=None): + self._client = client + self.result = original + self._query_id = query_id + + @property + def type(self): + """ + The always-present type of this result. It will be one of: + ``'article'``, ``'photo'``, ``'gif'``, ``'mpeg4_gif'``, ``'video'``, + ``'audio'``, ``'voice'``, ``'document'``, ``'location'``, ``'venue'``, + ``'contact'``, ``'game'``. + + You can access all of these constants through `InlineResult`, + such as `InlineResult.ARTICLE`, `InlineResult.VIDEO_GIF`, etc. + """ + return self.result.type + + @property + def message(self): + """ + The always-present :tl:`BotInlineMessage` that + will be sent if `click` is called on this result. + """ + return self.result.send_message + + @property + def title(self): + """ + The title for this inline result. It may be ``None``. + """ + return self.result.title + + @property + def description(self): + """ + The description for this inline result. It may be ``None``. + """ + return self.result.description + + @property + def url(self): + """ + The URL present in this inline results. If you want to "click" + this URL to open it in your browser, you should use Python's + `webbrowser.open(url)` for such task. + """ + if isinstance(self.result, types.BotInlineResult): + return self.result.url + + @property + def photo(self): + # TODO Document - how to deal with web media vs. normal? + if isinstance(self.result, types.BotInlineResult): + return self.result.thumb + elif isinstance(self.result, types.BotInlineMediaResult): + return self.result.photo + + @property + def document(self): + # TODO Document - how to deal with web media vs. normal? + if isinstance(self.result, types.BotInlineResult): + return self.result.content + elif isinstance(self.result, types.BotInlineMediaResult): + return self.result.document + + async def click(self, entity, reply_to=None, + silent=False, clear_draft=False): + """ + Clicks this result and sends the associated `message`. + + Args: + entity (`entity`): + The entity to which the message of this result should be sent. + + reply_to (`int` | :tl:`Message`, optional): + If present, the sent message will reply to this ID or message. + + silent (`bool`, optional): + If ``True``, the sent message will not notify the user(s). + + clear_draft (`bool`, optional): + Whether the draft should be removed after sending the + message from this result or not. Defaults to ``False``. + """ + entity = await self._client.get_input_entity(entity) + reply_id = None if reply_to is None else utils.get_message_id(reply_to) + req = self._client(functions.messages.SendInlineBotResultRequest( + peer=entity, + query_id=self._query_id, + id=self.result.id, + silent=silent, + clear_draft=clear_draft, + reply_to_msg_id=reply_id + )) + return self._client._get_response_message(req, await req, entity) + + async def download_photo(self): + """ + Downloads the media in `photo` if any and returns the download path. + """ + pass + + async def download_document(self): + """ + Downloads the media in `document` if any and returns the download path. + """ + pass From 7d880a856e314ac8530bc188a64e7b98fcce7bf6 Mon Sep 17 00:00:00 2001 From: Lonami Exo Date: Wed, 1 Aug 2018 00:15:23 +0200 Subject: [PATCH 2/4] Implement InlineResult.download_media --- telethon/tl/custom/inlineresult.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/telethon/tl/custom/inlineresult.py b/telethon/tl/custom/inlineresult.py index f0b1bb9a..491970e8 100644 --- a/telethon/tl/custom/inlineresult.py +++ b/telethon/tl/custom/inlineresult.py @@ -77,7 +77,10 @@ class InlineResult: @property def photo(self): - # TODO Document - how to deal with web media vs. normal? + """ + Returns either the :tl:`WebDocument` thumbnail for + normal results or the :tl:`Photo` for media results. + """ if isinstance(self.result, types.BotInlineResult): return self.result.thumb elif isinstance(self.result, types.BotInlineMediaResult): @@ -85,7 +88,10 @@ class InlineResult: @property def document(self): - # TODO Document - how to deal with web media vs. normal? + """ + Returns either the :tl:`WebDocument` content for + normal results or the :tl:`Document` for media results. + """ if isinstance(self.result, types.BotInlineResult): return self.result.content elif isinstance(self.result, types.BotInlineMediaResult): @@ -122,14 +128,14 @@ class InlineResult: )) return self._client._get_response_message(req, await req, entity) - async def download_photo(self): + async def download_media(self, *args, **kwargs): """ - Downloads the media in `photo` if any and returns the download path. - """ - pass + Downloads the media in this result (if there is a document, the + document will be downloaded; otherwise, the photo will if present). - async def download_document(self): + This is a wrapper around `client.download_media + `. """ - Downloads the media in `document` if any and returns the download path. - """ - pass + if self.document or self.photo: + return await self._client.download_media( + self.document or self.photo, *args, **kwargs) From 49a6cb4ef8cc803d89b318c1bc20772d12f18113 Mon Sep 17 00:00:00 2001 From: Lonami Exo Date: Wed, 1 Aug 2018 01:06:08 +0200 Subject: [PATCH 3/4] Fix InlineResult.click() --- telethon/tl/custom/inlineresult.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/telethon/tl/custom/inlineresult.py b/telethon/tl/custom/inlineresult.py index 491970e8..67ac0131 100644 --- a/telethon/tl/custom/inlineresult.py +++ b/telethon/tl/custom/inlineresult.py @@ -118,15 +118,16 @@ class InlineResult: """ entity = await self._client.get_input_entity(entity) reply_id = None if reply_to is None else utils.get_message_id(reply_to) - req = self._client(functions.messages.SendInlineBotResultRequest( + req = functions.messages.SendInlineBotResultRequest( peer=entity, query_id=self._query_id, id=self.result.id, silent=silent, clear_draft=clear_draft, reply_to_msg_id=reply_id - )) - return self._client._get_response_message(req, await req, entity) + ) + return self._client._get_response_message( + req, await self._client(req), entity) async def download_media(self, *args, **kwargs): """ From 7a2d7d98ade3e762193e002b423ade8536060bf5 Mon Sep 17 00:00:00 2001 From: Lonami Exo Date: Wed, 1 Aug 2018 01:06:47 +0200 Subject: [PATCH 4/4] Implement client.inline_query() --- readthedocs/telethon.tl.custom.rst | 8 ++++++ telethon/client/__init__.py | 1 + telethon/client/bots.py | 45 ++++++++++++++++++++++++++++++ telethon/client/telegramclient.py | 4 +-- telethon/sync.py | 6 ++-- telethon/tl/custom/__init__.py | 1 + 6 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 telethon/client/bots.py diff --git a/readthedocs/telethon.tl.custom.rst b/readthedocs/telethon.tl.custom.rst index 1099fa5e..ccc3f14c 100644 --- a/readthedocs/telethon.tl.custom.rst +++ b/readthedocs/telethon.tl.custom.rst @@ -54,6 +54,14 @@ telethon\.tl\.custom\.button module :undoc-members: :show-inheritance: +telethon\.tl\.custom\.inlineresult module +----------------------------------------- + +.. automodule:: telethon.tl.custom.inlineresult + :members: + :undoc-members: + :show-inheritance: + telethon\.tl\.custom\.chatgetter module --------------------------------------- diff --git a/telethon/client/__init__.py b/telethon/client/__init__.py index 0e8d2377..2ea98295 100644 --- a/telethon/client/__init__.py +++ b/telethon/client/__init__.py @@ -20,4 +20,5 @@ from .chats import ChatMethods from .dialogs import DialogMethods from .downloads import DownloadMethods from .auth import AuthMethods +from .bots import BotMethods from .telegramclient import TelegramClient diff --git a/telethon/client/bots.py b/telethon/client/bots.py new file mode 100644 index 00000000..8bef4c25 --- /dev/null +++ b/telethon/client/bots.py @@ -0,0 +1,45 @@ +from .users import UserMethods +from ..tl import types, functions, custom + + +class BotMethods(UserMethods): + async def inline_query(self, bot, query, *, offset=None, geo_point=None): + """ + Makes the given inline query to the specified bot + i.e. ``@vote My New Poll`` would be as follows: + + >>> client = ... + >>> client.inline_query('vote', 'My New Poll') + + Args: + bot (`entity`): + The bot entity to which the inline query should be made. + + query (`str`): + The query that should be made to the bot. + + offset (`str`, optional): + The string offset to use for the bot. + + geo_point (:tl:`GeoPoint`, optional) + The geo point location information to send to the bot + for localised results. Available under some bots. + + Returns: + A list of `custom.InlineResult + `. + """ + bot = await self.get_input_entity(bot) + result = await self(functions.messages.GetInlineBotResultsRequest( + bot=bot, + peer=types.InputPeerEmpty(), + query=query, + offset=offset or '', + geo_point=geo_point + )) + + # TODO Custom InlineResults(UserList) class with more information + return [ + custom.InlineResult(self, x, query_id=result.query_id) + for x in result.results + ] diff --git a/telethon/client/telegramclient.py b/telethon/client/telegramclient.py index e10dc43f..955765fb 100644 --- a/telethon/client/telegramclient.py +++ b/telethon/client/telegramclient.py @@ -1,12 +1,12 @@ from . import ( - AuthMethods, DownloadMethods, DialogMethods, ChatMethods, + AuthMethods, DownloadMethods, DialogMethods, ChatMethods, BotMethods, MessageMethods, ButtonMethods, UpdateMethods, UploadMethods, MessageParseMethods, UserMethods ) class TelegramClient( - AuthMethods, DownloadMethods, DialogMethods, ChatMethods, + AuthMethods, DownloadMethods, DialogMethods, ChatMethods, BotMethods, MessageMethods, UploadMethods, ButtonMethods, UpdateMethods, MessageParseMethods, UserMethods ): diff --git a/telethon/sync.py b/telethon/sync.py index 9e79a112..5395f8ad 100644 --- a/telethon/sync.py +++ b/telethon/sync.py @@ -17,7 +17,9 @@ import inspect from async_generator import isasyncgenfunction from .client.telegramclient import TelegramClient -from .tl.custom import Draft, Dialog, MessageButton, Forward, Message +from .tl.custom import ( + Draft, Dialog, MessageButton, Forward, Message, InlineResult +) from .tl.custom.chatgetter import ChatGetter from .tl.custom.sendergetter import SenderGetter @@ -81,4 +83,4 @@ def syncify(*types): syncify(TelegramClient, Draft, Dialog, MessageButton, - ChatGetter, SenderGetter, Forward, Message) + ChatGetter, SenderGetter, Forward, Message, InlineResult) diff --git a/telethon/tl/custom/__init__.py b/telethon/tl/custom/__init__.py index 72f8e95e..e3b6f39b 100644 --- a/telethon/tl/custom/__init__.py +++ b/telethon/tl/custom/__init__.py @@ -6,3 +6,4 @@ from .forward import Forward from .message import Message from .button import Button from .inline import InlineBuilder +from .inlineresult import InlineResult