File Handling
Telegram bots can not only send and receive text messages, but also many other kinds of messages, such as photos and videos. This involves handling the files that are attached to the messages.
How Files Work for Telegram Bots
This section explains how files work for Telegram bots. If you want to know how you can work with files in grammY, scroll down for downloading and uploading files.
Files are stored separately from messages. A file on the Telegram servers is identified by a file
, which is just a long string of characters. For example, it could look like AgADBAADZRAx
.
Identifiers for Receiving Files
Bots only receive file identifiers. If they want to obtain file contents, they have to request them explicitly.
Whenever your bot receives a message with a file, it will in fact not directly receive the complete file data, but only the file
instead. If your bot actually wants to download the file, then it can do so by calling the get
method (Telegram Bot API reference). This method enables you to download the file by constructing a special, temporary, URL. Note that this URL is only guaranteed to be valid for 60 minutes, after which it may expire. In this case, you can simply call get
again.
Files can be received like this.
Identifiers for Sending Files
Sending files gives you a file identifier, too.
Whenever your bot sends a message with a file, it will receive information about the sent message, including the file
of the sent file. This means that all files the bot sees, both via sending or receiving, will make a file
available to the bot. If you want to work with a file after your bot sees it, you should always store its file
.
Use file identifiers whenever you can. They are very efficient.
When a bot sends a message, it can specify a file
that it has seen before. This will allow it to send the identified file, without needing to upload the data for it.
You can reuse the same file
as often as you want, so you could send the same file to five different chats, using the same file
. However, you must make sure to use the correct method—for example, you cannot use a file
that identifies a photo when calling send
.
Files can be sent like this.
Identifiers May Surprise You
File identifiers only work for your bot. If another bot uses your file identifiers, it may randomly work and randomly crash and randomly kill innocent kittens. 🐱 → 💀
Every bot has its own set of file
s for the files that it can access. You cannot reliably use a file
from your friend’s bot, to access a file with your bot. Each bot will use different identifiers for the same file. This implies that you cannot simply guess a file
and access some random person’s file, because Telegram keeps track of which file
s are valid for your bot.
Using Foreign file
s
Note that in some cases it is technically possible that a file
from another bot seems to work correctly. However, using a foreign file
like this is dangerous as it can stop working at any time, without warning. So, always ensure that any file
s you use were originally for your bot.
A file can have several file identifiers.
On the other hand, it is possible that a bot eventually sees the same file identified by different file
s. This means that you cannot rely on comparing file
s to check if two files are the same. If you need to identify the same file over time (or across multiple bots), you should use the file
value that your bot receives along with every file
.
The file
cannot be used to download files, but will be the same for any given file, across every bot.
Receiving Files
You can handle files just like any other message. For example, if you want to listen for voice messages, you can do this:
bot.on("message:voice", async (ctx) => {
const voice = ctx.msg.voice;
const duration = voice.duration; // in seconds
await ctx.reply(`Your voice message is ${duration} seconds long.`);
const fileId = voice.file_id;
await ctx.reply("The file identifier of your voice message is: " + fileId);
const file = await ctx.getFile(); // valid for at least 1 hour
const path = file.file_path; // file path on Bot API server
await ctx.reply("Download your own file again: " + path);
});
2
3
4
5
6
7
8
9
10
11
12
13
Passing a Custom file_id to getFile
On the context object, get
is a shortcut, and will fetch information for a file on the current message. If you want to get a different file while handling a message, use ctx
instead.
Check out the
:
andmedia :
shortcuts for filter queries if you want to receive any kind of file.file
Once you have called get
, you can use the returned file
to download the file using this URL https://
, where <token>
must be replaced by your bot token.
If you run your own Bot API server, the file
will instead be an absolutely file path that points to a file on your local disk. In that case, you do not need to download anything anymore, as the Bot API server downloads the file for you when calling get
.
Files Plugin
grammY does not come bundled with its own file downloader, but you can install the official files plugin. This allows you to download files via await file
, and to obtain a download URL for them via file
.
Sending Files
Telegram bots have three ways to send files:
- Via
file
, i.e. by sending a file by an identifier that is already known to the bot._id - Via URL, i.e. by passing a public file URL, which Telegram downloads and sends for you.
- Via uploading your own file.
In all cases, the methods you need to call are named the same. Depending on which of the three ways you pick to send your file, the parameters to these functions will vary. For example, to send a photo, you can use ctx
(or send
if you use ctx
or bot
).
You can send other types of files by simply renaming the method and changing the type of the data you pass to it. In order to send a video, you can use ctx
. It’s the same case for a document: ctx
. You get the idea.
Let’s dive into what the three ways of sending a file are.
Via file_id
or URL
The first two methods are simple: you just pass the respective value as a string
, and you’re done.
// Send via file_id.
await ctx.replyWithPhoto(existingFileId);
// Send via URL.
await ctx.replyWithPhoto("https://grammy.dev/images/grammY.png");
// Alternatively, you use bot.api.sendPhoto() or ctx.api.sendPhoto().
2
3
4
5
6
7
Uploading Your Own Files
grammY has good support for uploading your own files. You can do this by importing and using the Input
class (grammY API Reference).
// Send a file via local path
await ctx.replyWithPhoto(new InputFile("/tmp/picture.jpg"));
// alternatively, use bot.api.sendPhoto() or ctx.api.sendPhoto()
2
3
4
The Input
constructor not only takes file paths, but also streams, Buffer
objects, async iterators, and—depending on your platform—more, or a function that creates any of these things. All you need to remember is: create an instance of Input
and pass it to any method to send a file. Instances of Input
can be passed to all methods that accept sending files by upload.
Here are some examples on how you can construct Input
s.
Uploading a File From Disk
If you already have a file stored on your machine, you can let grammY upload this file.
import { createReadStream } from "fs";
// Send a local file.
new InputFile("/path/to/file");
// Send from a read stream.
new InputFile(createReadStream("/path/to/file"));
2
3
4
5
6
7
// Send a local file.
new InputFile("/path/to/file");
// Send a `Deno.FsFile` instance.
new InputFile(await Deno.open("/path/to/file"));
2
3
4
5
Uploading Raw Binary Data
You can also send a Buffer
object, or an iterator that yields Buffer
objects. On Deno, you can send Blob
objects, too.
// Send a buffer or a byte array.
const buffer = Uint8Array.from([65, 66, 67]);
new InputFile(buffer); // "ABC"
// Send an iterable.
new InputFile(function* () {
// "ABCABCABCABC"
for (let i = 0; i < 4; i++) yield buffer;
});
2
3
4
5
6
7
8
// Send a blob.
const blob = new Blob(["ABC"], { type: "text/plain" });
new InputFile(blob);
// Send a buffer or a byte array.
const buffer = Uint8Array.from([65, 66, 67]);
new InputFile(buffer); // "ABC"
// Send an iterable.
new InputFile(function* () {
// "ABCABCABCABC"
for (let i = 0; i < 4; i++) yield buffer;
});
2
3
4
5
6
7
8
9
10
11
Downloading and Reuploading a File
You can even make grammY download a file from the internet. This will not actually save the file on your disk. Instead, grammY will only pipe through the data, and only keep a small chunk of it in memory. This is very efficient.
Note that Telegram supports downloading the file for you in many methods. If possible, you should prefer to send the file via URL, instead of using
Input
to stream the file contents through your server.File
// Download a file, and stream the response to Telegram.
new InputFile(new URL("https://grammy.dev/images/grammY.png"));
new InputFile({ url: "https://grammy.dev/images/grammY.png" }); // equivalent
2
3
Adding a Caption
When sending files, you can specify further options in an options object of type Other
, exactly as explained earlier. For example, this lets you send captions.
// Send a photo from a local file to user 12345 with the caption "photo.jpg".
await bot.api.sendPhoto(12345, new InputFile("/path/to/photo.jpg"), {
caption: "photo.jpg",
});
2
3
4
As always, just like with all other API methods, you can send files via ctx
(easiest), ctx
, or bot
.
File Size Limits
grammY itself can send files without any size limits, however, Telegram restricts file sizes as documented here. This means that your bot cannot download files larger than 20 MB, or upload files larger than 50 MB. Some combinations have even stricter limits, such as photos sent by URL (5 MB).
As mentioned in an earlier section, your bot is able to work with large files with some extra effort. If you want to support uploading files up to 2000 MB (maximum file size on Telegram) and downloading files of any size (4000 MB with Telegram Premium), you must host your own Bot API server in addition to hosting your bot.
Hosting your own Bot API server has, in and of itself, nothing to do with grammY. However, grammY supports all of the methods that are needed to configure your bot to use your own Bot API Server.