Guides

Working with Messages

Main Walkthrough 7 of 25

Message Updates

There are three updates that are directly related to messages:

Here are some examples on adding listeners for each of them:

client.on("message", (ctx) => {
  // called when a message is received or sent
});

client.on("editedMessage", (ctx) => {
  // called when a message is edited
});

client.on("deletedMessages", (ctx) => {
  // called when one or more messages are deleted
});

To see how the context object would look like for each update, you can refer to their specific documentation pages linked above.

Filtering Message Types

There is a significant number of Message variants, which makes processing all of them in a single handler a little harder.

Fortunately, you can easily filter out messages by their types when assigning your handler. Here are some examples:

client.on("message:text", (ctx) => {
  // This handler is called only when text messages are received.
  // So ctx.msg.text is always set.
});

client.on("editedMessage:photo", (ctx) => {
  // This handler is called only when photo messages are edited.
  // So ctx.msg.photo is always set.
});

Accessing the Message in Handlers

You can access the received message through ctx.msg or ctx.update.message.

client.on("message", (ctx) => {
  // Both ctx.msg and ctx.update.message are referring to the received message.
});

Edited messages are accessed through ctx.msg and ctx.update.editedMessage.

client.on("editedMessage", (ctx) => {
  // Both ctx.msg and ctx.update.editedMessage are referring to the edited message.
});

ctx.msg is just a shortcut that resolves to ctx.update.message ?? ctx.update.editedMessage. See Message.

Updates for deleted messages don’t include full message objects, only references to them (see MessageReference).

client.on("deletedMessages", (ctx) => {
  // ctx.update.deletedMessages is an array of MessageReference.
});

NotesBOT-ONLY

  • UpdateMessagesDeleted is not always sent to bots, so it is recommended that you don’t depend on it for bots.
  • Outgoing messages are not sent as high-level updates by default. Enable the outgoingMessages option to receive them:
const client = new Client({
  outgoingMessages: true,
  /* ... */
});

Sending Messages

There are multiple methods that can be used to send messages. Each of them is used for sending a specific type of message.

Here are some example calls:

const chat = /* ... */; // ID
const file = /* ... */; // FileSource

await client.sendMessage(
  chat,
  "Hey!",
  { isSilent: true, /* other optional options */ }
);

await client.sendRichText(chat, {
  type: "blocks",
  blocks: [
    {
      type: "paragraph",
      text: { type: "plain", text: "Hello!" },
    },
  ],
});

await client.sendPhoto(chat, file, { caption: "Optional Caption", /* other optional options */ });

await client.sendDocument(chat, file, { caption: "Optional Caption", /* other optional options */ });

await client.sendVideo(chat, file, { caption: "Optional Caption", /* other optional options */ });

await client.sendLivePhoto(chat, "./photo.jpg", "./video.mp4");

await client.sendMediaGroup(chat, [
  { type: "photo", photo: "./first.jpg" },
  { type: "photo", photo: "./second.jpg" },
]);

await client.sendAnimation(chat, file, { caption: "Optional Caption", /* other optional options */ });

await client.sendAudio(chat, file, { caption: "Optional Caption", /* other optional options */ });

await client.sendVoice(chat, file, { caption: "Optional Caption", /* other optional options */ });

await client.sendVideoNote(chat, file);

await client.sendSticker(chat, "./sticker.webp");

await client.sendDice(chat); // defaults to 🎲
await client.sendDice(chat, { emoji: "🏀" }); // but you can send any valid dice

await client.sendLocation(chat, 25.0953, 55.1562);

await client.sendVenue(chat, 25.0953, 55.1562, "Dubai Media City", "Dubai, UAE");

await client.sendPoll(chat, "Which runtime do you use?", [
  { text: "Deno" },
  { text: "Node.js" },
  { text: "Bun" },
]);

await client.sendContact(chat, "Alice", "+1234567890");

await client.sendChecklist(chat, "Release checklist", [
  { text: "Run the tests" },
  { text: "Publish the release" },
]);

To use the above example calls, chat must be replaced with a valid ID, and file must be replaced with a valid FileSource.

As previously said, the last parameters are optional and can always be omitted, so for example you can do just await client.sendMessage(chat, "Hey!"); if you don’t specify any optional parameter. Optional parameters are those parameters marked with ? in the method documentation.

Inside handlers, you can call the respective reply* shortcuts to easily reply the context message:

client.on("message", async (ctx) => {
  await ctx.reply(text); // same as client.sendMessage(ctx.chat.id, text, { replyTo: { type: "message", messageId: ctx.msg.id } });
  await ctx.replyPhoto(file); // same as client.sendPhoto(ctx.chat.id, file, { replyTo: { type: "message", messageId: ctx.msg.id } });
});

Sending Stickers

sendSticker accepts a sticker file source or file identifier.

await client.sendSticker(chatId, sticker);

Sending Media Groups

With sendMediaGroup, you can send several photos or videos as one album.

await client.sendMediaGroup(chatId, media);

Sending Live Photos

sendLivePhoto accepts the photo and video parts.

await client.sendLivePhoto(chatId, photo, video);

Getting Messages

getMessage retrieves one message, while getMessages retrieves several.

const message = await client.getMessage(chatId, messageId);
const messages = await client.getMessages(chatId, messageIds);

To resolve a Telegram message link, call resolveMessageLink.

const message = await client.resolveMessageLink(link);

With getLinkPreview, you can preview a link before sending it.

Screenshot NotificationsUSER-ONLY

Users can send a screenshot notification with sendScreenshotNotification.

await client.sendScreenshotNotification(chatId, messageId);

Editing Messages

You can edit messages that have already been sent. Each method targets a specific part of the message, and the referenced message must already be of a matching type.

Editing Text

editMessageText changes the text of a text message.

await client.editMessageText(chatId, messageId, "Updated text");

Like sendMessage, it accepts formatting options.

await client.editMessageText(chatId, messageId, "*Updated* text", {
  parseMode: "Markdown",
});

Editing Captions

editMessageCaption lets you change the caption of a media message.

await client.editMessageCaption(chatId, messageId, {
  caption: "New caption",
});

Replacing Media

Replace a media message’s content through editMessageMedia. Pass an InputMedia describing the new media.

await client.editMessageMedia(chatId, messageId, {
  type: "photo",
  photo: new URL("https://example.com/photo.jpg"),
  caption: "New caption",
});

Editing Reply Markup

With editMessageReplyMarkup, you can update the buttons attached to a message without changing its content.

await client.editMessageReplyMarkup(chatId, messageId, {
  replyMarkup: {/* ... */},
});

Live locations can be updated with editMessageLiveLocation, and rich text messages with editMessageRichText.

Deleting Messages

You can delete messages by calling either deleteMessage or deleteMessages.

await ctx.deleteMessage(messageId);
await ctx.deleteMessages([...messageIds]);

You can delete the context message with delete:

client.on("message", async (ctx) => {
  await ctx.delete(); // This deletes the received message.
});

Forwarding Messages

You can forward messages by calling either forwardMessage or forwardMessages.

await ctx.forwardMessage(toChat, messageId);
await ctx.forwardMessages(toChat, messageIds);

You can forward the context message with forward:

client.on("message", async (ctx) => {
  await ctx.forward(toChat); // This forwards the received message.
});

Pinned Messages

Pinning a message keeps it at the top of a chat so members can find it easily. Both users and bots can pin messages, provided they have the rights to do so in the chat.

Pinning a Message

pinMessage pins a message.

await client.pinMessage(chatId, messageId);

In private chats, the pin is visible to both participants by default. Pass isForBothSides as false to pin it only for the current account.

await client.pinMessage(chatId, messageId, {
  isForBothSides: false,
});

The isSilent option prevents a pin notification.

await client.pinMessage(chatId, messageId, {
  isSilent: true,
});

Unpinning Messages

unpinMessage lets you unpin a single message.

await client.unpinMessage(chatId, messageId);

To unpin every pinned message in a chat at once, call unpinMessages.

await client.unpinMessages(chatId);

In a forum, pass a topicId to unpin only the messages in that topic.

await client.unpinMessages(chatId, {
  topicId,
});

Receiving Pin Notifications

When a message is pinned in a group, a service message of type pinnedMessage is added to the chat. Listen for it like any other message, and read the pinned message through ctx.msg.pinnedMessage.

client.on("message:pinnedMessage", (ctx) => {
  const pinned = ctx.msg.pinnedMessage;
  console.log("Pinned:", pinned.id);
});