Telethon/telethon/tl/custom/inlinebuilder.py

333 lines
12 KiB
Python
Raw Normal View History

2018-07-15 12:31:14 +03:00
import hashlib
from .. import functions, types
from ... import utils
2018-07-15 12:31:14 +03:00
class InlineBuilder:
"""
2018-10-12 13:38:46 +03:00
Helper class to allow defining `InlineQuery
<telethon.events.inlinequery.InlineQuery>` ``results``.
2018-07-15 12:31:14 +03:00
Common arguments to all methods are
explained here to avoid repetition:
text (`str`, optional):
If present, the user will send a text
message with this text upon being clicked.
link_preview (`bool`, optional):
Whether to show a link preview in the sent
text message or not.
2018-10-12 13:38:46 +03:00
geo (:tl:`InputGeoPoint`, :tl:`GeoPoint`, :tl:`InputMediaVenue`, :tl:`MessageMediaVenue`, optional):
2018-07-15 12:31:14 +03:00
If present, it may either be a geo point or a venue.
period (int, optional):
The period in seconds to be used for geo points.
2018-10-12 13:38:46 +03:00
contact (:tl:`InputMediaContact`, :tl:`MessageMediaContact`, optional):
2018-07-15 12:31:14 +03:00
If present, it must be the contact information to send.
game (`bool`, optional):
May be ``True`` to indicate that the game will be sent.
2018-10-06 21:20:11 +03:00
buttons (`list`, `custom.Button <telethon.tl.custom.button.Button>`, :tl:`KeyboardButton`, optional):
2018-07-15 12:31:14 +03:00
Same as ``buttons`` for `client.send_message
<telethon.client.messages.MessageMethods.send_message>`.
parse_mode (`str`, optional):
Same as ``parse_mode`` for `client.send_message
<telethon.client.messageparse.MessageParseMethods.parse_mode>`.
id (`str`, optional):
The string ID to use for this result. If not present, it
will be the SHA256 hexadecimal digest of converting the
created :tl:`InputBotInlineResult` with empty ID to ``bytes()``,
so that the ID will be deterministic for the same input.
.. note::
If two inputs are exactly the same, their IDs will be the same
too. If you send two articles with the same ID, it will raise
``ResultIdDuplicateError``. Consider giving them an explicit
ID if you need to send two results that are the same.
2018-07-15 12:31:14 +03:00
"""
def __init__(self, client):
self._client = client
async def article(
self, title, description=None,
*, url=None, thumb=None, content=None,
id=None, text=None, parse_mode=(), link_preview=True,
geo=None, period=60, contact=None, game=False, buttons=None
2018-07-15 12:31:14 +03:00
):
"""
Creates new inline result of article type.
Args:
title (`str`):
The title to be shown for this result.
description (`str`, optional):
Further explanation of what this result means.
url (`str`, optional):
The URL to be shown for this result.
thumb (:tl:`InputWebDocument`, optional):
The thumbnail to be shown for this result.
For now it has to be a :tl:`InputWebDocument` if present.
content (:tl:`InputWebDocument`, optional):
The content to be shown for this result.
For now it has to be a :tl:`InputWebDocument` if present.
"""
# TODO Does 'article' work always?
# article, photo, gif, mpeg4_gif, video, audio,
# voice, document, location, venue, contact, game
result = types.InputBotInlineResult(
id=id or '',
type='article',
send_message=await self._message(
text=text, parse_mode=parse_mode, link_preview=link_preview,
geo=geo, period=period,
contact=contact,
game=game,
buttons=buttons
),
title=title,
description=description,
url=url,
thumb=thumb,
content=content
)
if id is None:
result.id = hashlib.sha256(bytes(result)).hexdigest()
return result
async def photo(
self, file, *, id=None,
text=None, parse_mode=(), link_preview=True,
2018-07-15 12:31:14 +03:00
geo=None, period=60, contact=None, game=False, buttons=None
):
"""
Creates a new inline result of photo type.
Args:
file (`obj`, optional):
Same as ``file`` for `client.send_file
<telethon.client.uploads.UploadMethods.send_file>`.
"""
try:
fh = utils.get_input_photo(file)
except TypeError:
fh = await self._client.upload_file(file, use_cache=types.InputPhoto)
2018-07-15 12:31:14 +03:00
if not isinstance(fh, types.InputPhoto):
r = await self._client(functions.messages.UploadMediaRequest(
types.InputPeerSelf(), media=types.InputMediaUploadedPhoto(fh)
2018-07-15 12:31:14 +03:00
))
fh = utils.get_input_photo(r.photo)
result = types.InputBotInlineResultPhoto(
id=id or '',
type='photo',
photo=fh,
send_message=await self._message(
text=text or '',
parse_mode=parse_mode,
link_preview=link_preview,
geo=geo,
period=period,
2018-07-15 12:31:14 +03:00
contact=contact,
game=game,
buttons=buttons
)
)
if id is None:
result.id = hashlib.sha256(bytes(result)).hexdigest()
return result
async def document(
self, file, title=None, *, description=None, type=None,
mime_type=None, attributes=None, force_document=False,
voice_note=False, video_note=False, use_cache=True, id=None,
text=None, parse_mode=(), link_preview=True,
2018-07-15 12:31:14 +03:00
geo=None, period=60, contact=None, game=False, buttons=None
):
"""
Creates a new inline result of document type.
`use_cache`, `mime_type`, `attributes`, `force_document`,
2018-07-15 12:31:14 +03:00
`voice_note` and `video_note` are described in `client.send_file
<telethon.client.uploads.UploadMethods.send_file>`.
Args:
file (`obj`):
2018-10-12 13:38:46 +03:00
Same as ``file`` for `client.send_file
<telethon.client.uploads.UploadMethods.send_file>`.
2018-07-15 12:31:14 +03:00
title (`str`, optional):
The title to be shown for this result.
description (`str`, optional):
Further explanation of what this result means.
type (`str`, optional):
The type of the document. May be one of: photo, gif,
mpeg4_gif, video, audio, voice, document, sticker.
See "Type of the result" in https://core.telegram.org/bots/api.
"""
if type is None:
if voice_note:
type = 'voice'
else:
type = 'document'
try:
fh = utils.get_input_document(file)
except TypeError:
use_cache = types.InputDocument if use_cache else None
fh = await self._client.upload_file(file, use_cache=use_cache)
2018-07-15 12:31:14 +03:00
if not isinstance(fh, types.InputDocument):
attributes, mime_type = utils.get_attributes(
2018-07-15 12:31:14 +03:00
file,
mime_type=mime_type,
attributes=attributes,
force_document=force_document,
voice_note=voice_note,
video_note=video_note
)
r = await self._client(functions.messages.UploadMediaRequest(
types.InputPeerSelf(), media=types.InputMediaUploadedDocument(
2018-07-15 12:31:14 +03:00
fh,
mime_type=mime_type,
attributes=attributes,
nosound_video=None,
thumb=None
)))
fh = utils.get_input_document(r.document)
result = types.InputBotInlineResultDocument(
id=id or '',
type=type,
document=fh,
send_message=await self._message(
# Empty string for text if there's media but text is None.
# We may want to display a document but send text; however
# default to sending the media (without text, i.e. stickers).
text=text or '',
parse_mode=parse_mode,
link_preview=link_preview,
geo=geo,
period=period,
2018-07-15 12:31:14 +03:00
contact=contact,
game=game,
buttons=buttons
),
title=title,
description=description
)
if id is None:
result.id = hashlib.sha256(bytes(result)).hexdigest()
return result
async def game(
self, short_name, *, id=None,
text=None, parse_mode=(), link_preview=True,
2018-07-15 12:31:14 +03:00
geo=None, period=60, contact=None, game=False, buttons=None
):
"""
Creates a new inline result of game type.
Args:
short_name (`str`):
The short name of the game to use.
"""
result = types.InputBotInlineResultGame(
id=id or '',
short_name=short_name,
send_message=await self._message(
text=text, parse_mode=parse_mode, link_preview=link_preview,
geo=geo, period=period,
contact=contact,
game=game,
buttons=buttons
)
)
if id is None:
result.id = hashlib.sha256(bytes(result)).hexdigest()
return result
async def _message(
self, *,
text=None, parse_mode=(), link_preview=True,
2018-07-15 12:31:14 +03:00
geo=None, period=60, contact=None, game=False, buttons=None
):
# Empty strings are valid but false-y; if they're empty use dummy '\0'
args = ('\0' if text == '' else text, geo, contact, game)
if sum(1 for x in args if x is not None and x is not False) != 1:
raise ValueError(
'Must set exactly one of text, geo, contact or game (set {})'
.format(', '.join(x[0] for x in zip(
'text geo contact game'.split(), args) if x[1]) or 'none')
)
2018-07-15 12:31:14 +03:00
2018-07-21 14:54:36 +03:00
markup = self._client.build_reply_markup(buttons, inline_only=True)
if text is not None:
if not text: # Automatic media on empty string, like stickers
return types.InputBotInlineMessageMediaAuto('')
2018-07-15 12:31:14 +03:00
text, msg_entities = await self._client._parse_message_text(
text, parse_mode
)
return types.InputBotInlineMessageText(
message=text,
no_webpage=not link_preview,
entities=msg_entities,
reply_markup=markup
)
elif isinstance(geo, (types.InputGeoPoint, types.GeoPoint)):
return types.InputBotInlineMessageMediaGeo(
geo_point=utils.get_input_geo(geo),
period=period,
reply_markup=markup
)
elif isinstance(geo, (types.InputMediaVenue, types.MessageMediaVenue)):
if isinstance(geo, types.InputMediaVenue):
geo_point = geo.geo_point
else:
geo_point = geo.geo
return types.InputBotInlineMessageMediaVenue(
geo_point=geo_point,
title=geo.title,
address=geo.address,
provider=geo.provider,
venue_id=geo.venue_id,
venue_type=geo.venue_type,
reply_markup=markup
)
elif isinstance(contact, (
types.InputMediaContact, types.MessageMediaContact)):
return types.InputBotInlineMessageMediaContact(
phone_number=contact.phone_number,
first_name=contact.first_name,
last_name=contact.last_name,
vcard=contact.vcard,
reply_markup=markup
)
elif game:
return types.InputBotInlineMessageGame(
reply_markup=markup
)
else:
raise ValueError('No text, game or valid geo or contact given')