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 diff --git a/telethon/tl/custom/inlineresult.py b/telethon/tl/custom/inlineresult.py new file mode 100644 index 00000000..67ac0131 --- /dev/null +++ b/telethon/tl/custom/inlineresult.py @@ -0,0 +1,142 @@ +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): + """ + 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): + return self.result.photo + + @property + def document(self): + """ + 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): + 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 = 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 self._client(req), entity) + + async def download_media(self, *args, **kwargs): + """ + Downloads the media in this result (if there is a document, the + document will be downloaded; otherwise, the photo will if present). + + This is a wrapper around `client.download_media + `. + """ + if self.document or self.photo: + return await self._client.download_media( + self.document or self.photo, *args, **kwargs)