Skip to content

Sending more than just messages

Lonami edited this page Jun 21, 2024 · 11 revisions

The current version of the library, v1, does not yet implement some features in the friendly methods, such as spoilers in client.send_message. And it never might, as it increases the maintenance burden, but are not so commonly used. However, that doesn't mean the library can't do those things.

Sending stickers

Stickers are nothing else than files, and when you successfully retrieve the stickers for a certain sticker set, all you will have are handles to these files. Remember, the files Telegram holds on their servers can be referenced through this pair of ID/hash (unique per user), and you need to use this handle when sending a "document" message. This working example will send yourself the very first sticker you have:

# Get all the sticker sets this user has
from telethon.tl.functions.messages import GetAllStickersRequest
sticker_sets = await client(GetAllStickersRequest(0))

# Choose a sticker set
from telethon.tl.functions.messages import GetStickerSetRequest
from telethon.tl.types import InputStickerSetID
sticker_set = sticker_sets.sets[0]

# Get the stickers for this sticker set
stickers = await client(GetStickerSetRequest(
    stickerset=InputStickerSetID(
        id=sticker_set.id, access_hash=sticker_set.access_hash
    ),
    hash=0
))

# Stickers are nothing more than files, so send that
await client.send_file('me', stickers.documents[0])

Sending reactions

It works very similar to replying to a message. You need to specify the chat, message ID you wish to react to, and reaction, using :tl:SendReaction:

from telethon.tl.functions.messages import SendReactionRequest
await client(SendReactionRequest(
    peer=chat,
    msg_id=42,
    reaction=[types.ReactionEmoji(
        emoticon='❤️'
    )]
))

Note that you cannot use strings like :heart: for the reaction. You must use the desired emoji directly. You can most easily achieve this by copy-pasting the emoji from an official application such as Telegram Desktop.

If for some reason you cannot embed emoji directly into the code, you can also use its unicode escape (which you can find using websites like symbl.cc), or install a different package, like emoji:

    # All of these work exactly the same (you only need one):
    import emoji
    reaction = emoji.emojize(':red_heart:')
    reaction = '❤️'
    reaction = '\u2764'

    from telethon.tl.functions.messages import SendReactionRequest
    await client(SendReactionRequest(
        peer=chat,
        msg_id=42,
        reaction=[types.ReactionEmoji(
            emoticon=reaction
        )]
    ))

Please make sure to check the help pages of the respective websites you use if you need a more in-depth explanation on how they work. Telethon only needs you to provide the emoji in some form. Some packages or websites can make this easier.

Sending spoilers and custom emoji

Telethon's v1 markdown parser does not offer a way to send spoiler (hidden text) or custom emoji. However, it's easy to add support for them.

Telethon's parse_mode supports using a custom object with parse and unparse functions to work. This means it's possible to leverage the current markdown implementation and extend it with custom functionality.

There are third-party parsers for HTML that support more of the tags allowed by the HTTP bot API, including spoilers and custom emoji. Alternatively, you can copy the (less powerful) following code into your own:

from telethon.extensions import markdown
from telethon import types

class CustomMarkdown:
    @staticmethod
    def parse(text):
        text, entities = markdown.parse(text)
        for i, e in enumerate(entities):
            if isinstance(e, types.MessageEntityTextUrl):
                if e.url == 'spoiler':
                    entities[i] = types.MessageEntitySpoiler(e.offset, e.length)
                elif e.url.startswith('emoji/'):
                    entities[i] = types.MessageEntityCustomEmoji(e.offset, e.length, int(e.url.split('/')[1]))
        return text, entities
    @staticmethod
    def unparse(text, entities):
        for i, e in enumerate(entities or []):
            if isinstance(e, types.MessageEntityCustomEmoji):
                entities[i] = types.MessageEntityTextUrl(e.offset, e.length, f'emoji/{e.document_id}')
            if isinstance(e, types.MessageEntitySpoiler):
                entities[i] = types.MessageEntityTextUrl(e.offset, e.length, 'spoiler')
        return markdown.unparse(text, entities)

This creates a custom class with parse and unparse. CustomMarkdown.parse uses markdown.parse (so it works just like the default markdown), but before returning, it scans the parsed text for the following inline URLs:

message = 'this is a [link text](spoiler) and [❤️](emoji/10002345) too'

Here, the message contains a link text with URL spoiler. The above code will replace the URL with MessageEntitySpoiler. It also contains a URL with emoji/10002345, which will be replaced with MessageEntityCustomEmoji. Effectively sending those instead of the URL.

To use the class, you must change your client.parse_mode to it (be sure to use an instance, because the type is callable and the library would attempt to create an instance to parse it):

client.parse_mode = CustomMarkdown()

Now, in your message text, you can use inline links which become spoilers and custom emoji! (Note that for custom emoji to work, the inline link text must be a normal emoji):

client.send_message('me', 'hello this is a [hidden text](spoiler), with custom emoji [❤️](emoji/10002345) !')

You may have noticed the emoji URL is followed by a number. This number is a document_id. To find it, the easiest way is to send a message to your own chat with the premium emoji you want to use using an official client, and then use Telethon to print the message.entities. It will contain the document_id you need to use.