403 lines
10 KiB
Markdown
403 lines
10 KiB
Markdown
|
# Handlers
|
||
|
|
||
|
Dispatcher can process a lot of different update types,
|
||
|
and for each of them you can register as many handlers
|
||
|
as you want.
|
||
|
|
||
|
For each of the handler types, there are 2 ways you can add
|
||
|
a handler - either using a specialized method, or
|
||
|
with `addUpdateHandler` method, as [described below](#addupdatehandler)
|
||
|
|
||
|
See also: [Reference](https://ref.mtcute.dev/classes/_mtcute_dispatcher.Dispatcher.html)
|
||
|
|
||
|
::: warning
|
||
|
Do not add or remove handlers inside of another handler,
|
||
|
this may lead to undefined behaviour.
|
||
|
:::
|
||
|
|
||
|
## New message
|
||
|
|
||
|
Whenever a new message is received by the bot, or a message is sent
|
||
|
by another client (mostly used for users), `new_message` handlers are
|
||
|
dispatched:
|
||
|
|
||
|
```ts
|
||
|
dp.onNewMessage(async (upd) => {
|
||
|
await upd.answerText('Hey!')
|
||
|
})
|
||
|
```
|
||
|
|
||
|
## Edit message
|
||
|
|
||
|
Whenever a message is edited (and client receives an update about that*),
|
||
|
`edit_message` handlers are dispatched:
|
||
|
|
||
|
```ts
|
||
|
dp.onEditMessage(async (upd) => {
|
||
|
await upd.replyText('Yes.')
|
||
|
})
|
||
|
```
|
||
|
|
||
|
<small>* Telegram might decide not to send these updates
|
||
|
in case this message is old enough.</small>
|
||
|
|
||
|
## Message group
|
||
|
|
||
|
When message grouping is enabled (see [here](/guide/intro/updates.md#message-grouping)),
|
||
|
`message_group` handlers are dispatched when a media group (aka album) is received:
|
||
|
|
||
|
```ts
|
||
|
dp.onMessageGroup(async (upd) => {
|
||
|
await upd.replyText('Thanks for the media!')
|
||
|
})
|
||
|
```
|
||
|
|
||
|
## Delete message
|
||
|
|
||
|
Whenever a message is deleted (and client receives an update about that*),
|
||
|
`delete_message` handlers are dispatched. Note that these updates
|
||
|
only contain message ID(s), and not its contents, and it is up to you
|
||
|
to check what that ID corresponds to.
|
||
|
|
||
|
```ts
|
||
|
dp.onDeleteMessage(async (upd) => {
|
||
|
if (upd.messageIds.includes(42)) {
|
||
|
console.log('Magic message deleted :c')
|
||
|
}
|
||
|
})
|
||
|
```
|
||
|
|
||
|
Note that for private chats, this does not include
|
||
|
user's ID, so you may want to implement some caching
|
||
|
if you need that info.
|
||
|
|
||
|
<small>* Telegram might decide not to send these updates
|
||
|
in case this message is old enough.</small>
|
||
|
|
||
|
## Chat member
|
||
|
|
||
|
Whenever chat member status is changed in a channel/supergroup where the bot
|
||
|
is an administrator, `chat_member` handlers are dispatched.
|
||
|
|
||
|
```ts
|
||
|
dp.onChatMemberUpdate(async (upd) => {
|
||
|
console.log(`${upd.user.displayName} ${upd.type} by ${upd.actor.displayName}`)
|
||
|
})
|
||
|
```
|
||
|
|
||
|
::: tip
|
||
|
You can filter by update type using `filters.chatMember`:
|
||
|
|
||
|
```ts
|
||
|
dp.onChatMemberUpdate(
|
||
|
filters.chatMember('joined'),
|
||
|
async (upd) => {
|
||
|
await upd.client.sendText(upd.chat, `${upd.user.mention()}, welcome to the chat!`)
|
||
|
}
|
||
|
)
|
||
|
```
|
||
|
:::
|
||
|
|
||
|
## Inline query
|
||
|
|
||
|
Whenever an inline query is sent by user to your bot, `inline_query`
|
||
|
handlers are dispatched:
|
||
|
|
||
|
```ts
|
||
|
dp.onInlineQuery(async (upd) => {
|
||
|
await upd.answer([], {
|
||
|
switchPm: {
|
||
|
text: 'Hello!',
|
||
|
parameter: 'inline_hello'
|
||
|
}
|
||
|
})
|
||
|
})
|
||
|
```
|
||
|
|
||
|
You can learn more about inline queries in [Inline Mode](./inline-mode.html) section.
|
||
|
|
||
|
## Chosen inline result
|
||
|
|
||
|
When a user selects an inline result, and assuming that you have
|
||
|
**inline feedback** feature enabled, `chosen_inline_result` handlers
|
||
|
are dispatched:
|
||
|
|
||
|
```ts
|
||
|
dp.onChosenInlineResult(async (upd) => {
|
||
|
await upd.editMessage({
|
||
|
text: `${result.user.displayName}, thanks for using inline!`
|
||
|
})
|
||
|
})
|
||
|
```
|
||
|
|
||
|
As mentioned, these updates are only sent by Telegram when you
|
||
|
have enabled **inline feedback** feature. You can enable it
|
||
|
in [@BotFather](https://t.me/botfather).
|
||
|
|
||
|
It is however noted by Telegram that this should only be used
|
||
|
for statistical purposes, and even if probability setting is 100%,
|
||
|
not all chosen inline results may be reported
|
||
|
([source](https://core.telegram.org/api/bots/inline#inline-feedback)).
|
||
|
|
||
|
## Callback query
|
||
|
|
||
|
Whenever user clicks on a [callback button](../topics/keyboards.html#inline-keyboards),
|
||
|
`callback_query` or `inline_callback_query` handlers are dispatched, based on the origin of the message:
|
||
|
|
||
|
```ts
|
||
|
dp.onCallbackQuery(async (upd) => {
|
||
|
await upd.answer({ text: '🌸' })
|
||
|
})
|
||
|
|
||
|
dp.onInlineCallbackQuery(async (upd) => {
|
||
|
await upd.answer({ text: '🌸' })
|
||
|
})
|
||
|
```
|
||
|
|
||
|
For messages sent normally by the bot (e.g. using `sendText`), `callback_query` handlers are dispatched.
|
||
|
For messages sent from an inline query (e.g. inside `onInlineQuery`), `inline_callback_query` handlers are dispatched.
|
||
|
|
||
|
## Poll update
|
||
|
|
||
|
Whenever a poll state is updated (stopped, anonymous user has voted, etc.),
|
||
|
`poll` handlers are dispatched:
|
||
|
|
||
|
```ts
|
||
|
dp.onPollUpdate(async (upd) => {
|
||
|
// do something
|
||
|
})
|
||
|
```
|
||
|
|
||
|
Bots only receive updates about polls that they have sent.
|
||
|
|
||
|
Note that due to Telegram limitation, sometimes the update does not
|
||
|
include the poll itself, and mtcute creates a "stub" poll,
|
||
|
that is missing most of the information (including question text,
|
||
|
answers text, missing flags like `quiz`, etc.). Number of votes per-answer
|
||
|
is always there, though.
|
||
|
|
||
|
If you need that missing information, you will need to implement
|
||
|
caching by yourself. Bot API (strictly speaking, TDLib) does it internally
|
||
|
and thus is able to provide all the needed information autonomously.
|
||
|
This is not implemented in mtcute yet.
|
||
|
|
||
|
## Poll vote
|
||
|
|
||
|
When a user votes in a public poll, `poll_vote` handlers are dispatched:
|
||
|
|
||
|
```ts
|
||
|
dp.onPollVote(async (upd) => {
|
||
|
upd.user.sendText('Thanks for voting!')
|
||
|
})
|
||
|
```
|
||
|
|
||
|
Bots only receive updates about polls that they have sent.
|
||
|
|
||
|
This update currently doesn't contain information about the poll,
|
||
|
only the poll ID, so if you need that info,
|
||
|
you'll have to implement caching yourself.
|
||
|
|
||
|
|
||
|
## User status
|
||
|
|
||
|
When a user's online status changes (e.g. user goes offline),
|
||
|
and client receives an update about that*,
|
||
|
`user_status` handlers are dispatched:
|
||
|
|
||
|
```ts
|
||
|
dp.onUserStatusUpdate(async (upd) => {
|
||
|
console.log(`User ${upd.userId} is now ${upd.status}`)
|
||
|
})
|
||
|
```
|
||
|
|
||
|
<small>* Telegram might decide not to send these updates
|
||
|
in many cases, for example: you don't have an active PM
|
||
|
with this user, this user is from a large chat that
|
||
|
you aren't currently chatting in, etc.</small>
|
||
|
|
||
|
## User typing
|
||
|
|
||
|
When a user's typing status changes,
|
||
|
and client receives an update about that*,
|
||
|
`user_status` handlers are dispatched:
|
||
|
|
||
|
```ts
|
||
|
dp.onUserTyping(async (upd) => {
|
||
|
console.log(`${upd.userId} is ${upd.status} in ${upd.chatId}`)
|
||
|
})
|
||
|
```
|
||
|
|
||
|
<small>* Telegram might decide not to send these updates
|
||
|
in many cases, for example: you haven't talked
|
||
|
to this user for some time, that user/chat is archived, etc.</small>
|
||
|
|
||
|
## History read
|
||
|
|
||
|
When history is read in a chat (either by the other party or by you from another client),
|
||
|
and client receives an update about that, `history_read` handlers are dispatched:
|
||
|
|
||
|
```ts
|
||
|
dp.onHistoryRead(async (upd) => {
|
||
|
console.log(`History read in ${upd.chatId} up to ${upd.maxReadId}`)
|
||
|
})
|
||
|
```
|
||
|
|
||
|
## Bot stopped
|
||
|
|
||
|
When a user clicks "Stop bot" button in the bot's profile,
|
||
|
`bot_stopped` handlers are dispatched:
|
||
|
|
||
|
```ts
|
||
|
dp.onBotStopped(async (upd) => {
|
||
|
console.log(`Bot stopped by ${upd.user.id}`)
|
||
|
})
|
||
|
```
|
||
|
|
||
|
## Join requests
|
||
|
|
||
|
These updates differ depending on whether the currently logged in user is a bot or not.
|
||
|
|
||
|
### Bot
|
||
|
|
||
|
When a user requests to join a group/channel where the current bot is an admin,
|
||
|
`bot_chat_join_request` handlers are dispatched:
|
||
|
|
||
|
```ts
|
||
|
dp.onBotChatJoinRequest(async (upd) => {
|
||
|
console.log(`User ${upd.user.id} wants to join ${upd.chat.id}`)
|
||
|
|
||
|
if (upd.user.id === DUROV) {
|
||
|
await upd.decline()
|
||
|
}
|
||
|
})
|
||
|
```
|
||
|
|
||
|
### User
|
||
|
|
||
|
When a user requests to join a group/channel where the current user is an admin,
|
||
|
`chat_join_request` handlers are dispatched:
|
||
|
|
||
|
```ts
|
||
|
dp.onChatJoinRequest(async (upd) => {
|
||
|
console.log(`User ${upd.recentRequesters[0].id} wants to join ${upd.chatId}`)
|
||
|
})
|
||
|
```
|
||
|
|
||
|
These updates contain less information than bot join requests,
|
||
|
and additional info should be fetched manually if needed
|
||
|
|
||
|
## Pre-checkout query
|
||
|
|
||
|
When a user clicks "Pay" button, `pre_checkout_query` handlers are dispatched:
|
||
|
|
||
|
```ts
|
||
|
dp.onPreCheckoutQuery(async (upd) => {
|
||
|
await upd.approve()
|
||
|
})
|
||
|
```
|
||
|
|
||
|
## Story updates
|
||
|
|
||
|
When a story is posted or modified, `story` handlers are dispatched:
|
||
|
|
||
|
```ts
|
||
|
dp.onStoryUpdate(async (upd) => {
|
||
|
console.log(`${upd.peer.id} posted or modified a story!`)
|
||
|
})
|
||
|
```
|
||
|
|
||
|
## Delete story updates
|
||
|
|
||
|
When a story is deleted, `delete_story` handlers are dispatched:
|
||
|
|
||
|
```ts
|
||
|
dp.onDeleteStory(async (upd) => {
|
||
|
console.log(`${upd.peer.id} deleted story ${upd.storyId}!`)
|
||
|
})
|
||
|
```
|
||
|
|
||
|
## Raw updates
|
||
|
|
||
|
Dispatcher only implements the most commonly used updates,
|
||
|
but you can still add a handler for a custom MTProto update:
|
||
|
|
||
|
```ts
|
||
|
dp.onRawUpdate(
|
||
|
(cl, upd) => upd._ === 'updateUserName',
|
||
|
async (
|
||
|
client: TelegramClient,
|
||
|
update_: tl.TypeUpdate,
|
||
|
peers: PeersIndex,
|
||
|
) => {
|
||
|
const update = update_ as tl.RawUpdateUserName
|
||
|
// ...
|
||
|
}
|
||
|
)
|
||
|
```
|
||
|
|
||
|
Note that the signature is slightly different: since the update
|
||
|
is not parsed by the library, client, raw update and entities
|
||
|
are provided as-is.
|
||
|
|
||
|
Raw update handlers are dispatched independently of parsed update handlers
|
||
|
([learn more](groups-propagation.html#raw-updates)).
|
||
|
|
||
|
## `addUpdateHandler`
|
||
|
|
||
|
Another way to register a handler is to use `addUpdateHandler` method.
|
||
|
|
||
|
It supports all the updates that have a specialized methods, and this
|
||
|
method is actually used by `on*` under the hood. It accepts an object,
|
||
|
containing update type, its handler and optionally a `check` function,
|
||
|
which checks if the update should be handled by this handler
|
||
|
(basically a [Filter](filters.html)):
|
||
|
|
||
|
```ts
|
||
|
dp.addUpdateHandler({
|
||
|
type: 'new_message',
|
||
|
callback: async (msg) => {
|
||
|
...
|
||
|
},
|
||
|
check: filters.media
|
||
|
})
|
||
|
```
|
||
|
|
||
|
This may be useful in case you are loading your handlers dynamically.
|
||
|
|
||
|
## Removing handlers
|
||
|
|
||
|
It might be useful to remove a previously added handler.
|
||
|
|
||
|
### Removing a single handler
|
||
|
|
||
|
To remove a single handler, it must have been added using `addUpdateHandler`,
|
||
|
and then that object should be passed to `removeUpdateHandler`:
|
||
|
|
||
|
```ts
|
||
|
const handler = { ... }
|
||
|
dp.addUpdateHandler(handler)
|
||
|
|
||
|
// later
|
||
|
dp.removeUpdateHandler(handler)
|
||
|
```
|
||
|
|
||
|
Handlers are matched by the object that wraps them, because
|
||
|
the same function may be used for multiple different handlers.
|
||
|
|
||
|
### Removing handlers by type
|
||
|
|
||
|
To remove all handlers that have the given type, pass this type
|
||
|
to `removeUpdateHandler`:
|
||
|
|
||
|
```ts
|
||
|
dp.removeUpdateHandler('new_message')
|
||
|
```
|
||
|
|
||
|
### Removing all handlers
|
||
|
|
||
|
Pass `all` to `removeUpdateHandler`:
|
||
|
|
||
|
```ts
|
||
|
dp.removeUpdateHandler('all')
|
||
|
```
|