Home

Awesome

Telegram bot nodes for Node-RED

Platform License Downloads Total Downloads NPM Known Vulnerabilities Telegram Package Quality Build Open Issues Closed Issues ...

This package contains a receiver and a sender node which act as a Telegram Bot. The only thing required is the token that can be retrieved by the @botfather Telegram Bot.

Thanks for your donation

If you want to support this free project. Any help is welcome. You can donate by clicking one of the following links: <a target="blank" href="https://blockchain.com/btc/payment_request?address=1PBi7BoZ1mBLQx4ePbwh1MVoK2RaoiDsp5"><img src="https://img.shields.io/badge/Donate-Bitcoin-green.svg"/></a> <a target="blank" href="https://www.paypal.me/windkh"><img src="https://img.shields.io/badge/Donate-PayPal-blue.svg"/></a>

<a href="https://www.buymeacoffee.com/windka" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="41" width="174"></a>

Credits

Installation

NPM

You can install the nodes using node-red's "Manage palette" in the side bar.

Or run the following command in the root directory of your Node-RED installation

npm install node-red-contrib-telegrambot --save

Note that the minimum node-red version 1.3.7 and minimum nodejs version is 12.x.

Dependencies

The nodes are tested with Node.js v18.20.0 and Node-RED v3.1.9.

Changelog

Changes can be followed here.

Hints

Usage

Basics

Receiver and sender nodes

The Telegram receiver node receives messages from the bot and sends a message object with the following layout:

msg.payload contains the message details with the following elements:

msg.originalMessage contains the original message object from the underlying node-telegram-bot-api lib.

The Telegram sender node sends the content to a specified username or chat. The node's input msg object is similar to the output msg object of the Telegram receiver node. Some of the additional elements are mandatory and some are optional (see table below).

A simple echo flow looks like:

Alt text
echo flow
Fig. 1: Simple echo flow

Types of telegram messages

The following message contents can be sent and received (given in msg.payload.type):

Note that media groups are sent/received not as a group, but as separate messages of type photo and video.

The following msg.payload.type contents indicate changes in the group or channel itself:

For more details of the content types listed above also refer to the telegram api description.

Message types and corresponding content elements in msg.payload

The msg.payload contains several elements additional to chatId, type and content. These additional elements depend on the contents of msg.payload.type. In addition the format of msg.payload.content depends on the type.

The following table shows the relationship between the message type and additional elements.

msg.payload.typemsg.payload.contentchatcaptionblobphotosmediaGroupId
messagetext-----
photophoto[index].file_id-optionaltrue+optional
audioaudio.file_id-optionaltrue--
dicedice--false--
stickersticker.file_id--true--
animationanimation.file_id-optionaltrue-optional
videovideo.file_id-optionaltrue-optional
video_notevideo_note.file_id--true--
voicevoice.file_id-optionaltrue--
locationlocation-----
venuevenue-----
contactcontact-----
documentdocument.file_id-optionaltrue--
pollpoll-----
invoiceinvoice-----
successful_paymentsuccessful_payment-----
new_chat_titlenew_chat_title-----
new_chat_photonew_chat_photo[index].file_id--true+-
new_chat_membersnew_chat_members-----
left_chat_memberleft_chat_members-----
delete_chat_photodelete_chat_photo-----
pinned_messagepinned_message-----
channel_chat_createdchannel_chat_created-----
group_chat_createdgroup_chat_created+----
supergroup_chat_createdsupergroup_chat_created+----
migrate_from_chat_idmigrate_from_chat_id+----
migrate_to_chat_idmigrate_to_chat_id+----

Legend:

Tab. 1: Data elements in msg.payload depending on msg.payload.type

For more details of the content types listed above also refer to the telegram api description and the telegram bot api description.

Error handling

There are two ways of how to handle errors in the sender node:

  1. Default: all errors can be handled in a catch-all node.
  2. Enable second output: errors are sent to this additional output. The msg contains an additional msg.error property.
<img src="images/TelegramBotErrorHandling2.png" title="Sender node dialog" width="600" />

Fig. 2: Sender node dialog

Alt text
error handling example flow

Configuration Node

The mandatory configuration entries are

which you received from @botfather when creating a new bot.

<img src="images/TelegramBotConfigurationNodeDialog.png" title="Configuration node dialog" width="600" />

Fig. 3: Configuration node dialog

Reading token from external location

Instead of entering the token from bot father directly into the token field, you can also instruct the node to read ot from on external location.

Reading token from environment variable

Environment variables are entered in the settings.js of node-red before startup.

process.env.BOT_TOKEN = "<your bot token here>";

The token field in the configuration node must then look like

{env.get("BOT_TOKEN")}

Configuration properties Users and ChatIds

The node contains the two optional properties: Users and ChatIds. You may enter a list of names and/or chatIds that are authorized to use this bot. This is useful, if the bot should only accept incoming calls from dedicated persons resp. chat groups. The values in the property fields must be separated by a comma e.g.: Hugo,Sepp,Egon Leave the fields Users and ChatIds blank if you do not want to use this feature to mask senders.

User names can only be used, if a telegram user has set its Username in the Telegram settings. The following screenshot shows the according settings dialog in the Telegram app where you can set your personal preferences:

<img src="images/TelegramSettingsUsername.png" title="Telegram user name settings" width="350" />

Fig. 4: Telegram app settings dialog (example Android phone app)

If no Username is set you can only filter via the ChatId property.

Configuration property Server URL

This is the server url of the telegram server (https://api.telegram.org). If you use a different instance of a telegram server somewhere else (e.g. on premise) you could then use this property to connect to that server instead the global one.

Typically this field is left blank.

Configuration property Test Environment

This enables the test environment see: (https://core.telegram.org/bots/features#testing-your-bot).

Typically this checkbox is unchecked.

Configuration property Update Mode

The update mode can be chosen from Polling or Webhook.

Polling mode

By default the bot is polling every 300ms for new messages. This polling interval can be set via the property Poll Interval in the Do you mean a table which describes the logic within the function getMessageDetails(botMsg)? E.g. a table with these coloumns:

Webhook mode

The Webhook method may be chosen to avoid polling.

<img src="images/TelegramBotWebHookConfiguration.png" title="Webhook configuration with self signed sertificate" width="350" />

Fig. 5: Example configuration for webhook mode

As setting up a webhook can be very complex depending on the infrastructure this was moved to a seprate readme file. See also WEBHOOK.md

None mode

The None method may be chosen to avoid traffic due to polling or incoming webhook calls. You can only send messages using the sender node but you can not receive any data.

Configuration property IP address family

You can force a certain IP version to be used when making requests to the telegram server. Normally this is up to the operating system und you can just keep the default setting "Any". If you discover problems e.g. the telegram server is unreachable. Then you can try to force using IPv4 or IPv6.

Configuration property flag Use SOCKS

SOCKS4/5 proxy support is optional when running behind a SOCKS4/5 proxy that requires authentication. In this case, additional configuration properties have to be set in the configuration node.

As setting up a socks5 proxy can be very complex depending on the infrastructure this was moved to a seprate readme file. See also SOCKS5.md

Configuration property flag Verbose Logging

The Verbose Logging flag should only be activated when debugging network problems as this will create cyclic warnings when the network is down.

Receiver Node

This node can receive telegram messages sent to the bot and (under certain circumstances) also receive messages from a chat.

node-appearance-receiver
Fig. 6: Receiver node appearance

Telegram messages sent directly to the bot are automatically received (if not masked via the configuration node property Users).
To be able to receive telegram chat messages, invite the bot to a chat (/setprivacy must be configured correctly!). If the configuration node property ChatIds is not set, all chat messages are received.

You can control if the bot receives every message by calling /setprivacy @botfather (refer also to there).

Note that there are certain limitations for bots in channels, groups and super groups. You should make the bot admin or grant the rights if you discover problems. Bot to bot communication is also not allowed by telegram. Please read the telegram bot documentation.

Configuration

Configuration property Download Directory

When the receiver node receives data like videos, documents and so on, the file is downloaded automatically to the local harddisc when the node's property Download Directory is set in the configuration node. The directory may also be part of the message payload: msg.payload.path. In addition to that the message object may contain the direct download link in the payload: msg.payload.weblink.

Configuration property Filter

Normally, a receiver node receives all content that is sent to the bot. However if you have command nodes next to a receiver you can enable the commands flag in the configuration property Filter so that commands meant for a command node will not be handled by the receiver node.
I.e. the command message then only appears in the configured command node and not in this node.

Outputs

The original message from the underlying node library is stored in msg.originalMessage. The msg.payload contains the most important data like chatId, type and content. Additional elements are present in the msg.payload structure and depend on the message type. These additional elements are described in the table Tab. 1 above.

The content format depends on the message type. E.g. if you receive a text message then the content format is a string, if you receive a location, the content format is an object containing latitude and longitude. See also "available methods" in the api core description.

The node has two outputs:

  1. The node's upper output (Standard Output) is used if the message is from an authorized user.
  2. The node's lower output (Unauthorized Output) is used when security is applied (via configuration properties Users and ChatIds) and the user is not authorized to access the bot.

Sender Node

This node sends contents to a telegram user or to a telegram chat. It is triggered by an incoming msg object at its input containing all necessary telegram information.
node-appearance-sender
Fig. 7: Sender node appearance

Inputs

The input msg.payload must contain the following elements:

Additional elements are present in the msg.payload structure and depend on the message type. These additional elements are described in the table Tab. 1 above.

msg.payload.typemsg.payload.contentmsg.payload.optionssee also
messagetext (string)optional argumentshttps://core.telegram.org/bots/api#sendmessage
documentdocument (InputFile/string)optional argumentshttps://core.telegram.org/bots/api#senddocument
photophoto (InputFile/string)optional argumentshttps://core.telegram.org/bots/api#sendphoto
audioaudio (InputFile/string)optional argumentshttps://core.telegram.org/bots/api#sendaudio
videovideo (InputFile/string)optional argumentshttps://core.telegram.org/bots/api#sendvideo
animationanimation (InputFile/string)optional argumentshttps://core.telegram.org/bots/api#sendanimation
voicevoice (InputFile/string)optional argumentshttps://core.telegram.org/bots/api#sendvoice
video_notevideo_note (InputFile/string)optional argumentshttps://core.telegram.org/bots/api#sendvideonote
mediaGroupmedia (array of InputMediaAudio, InputMediaDocument, InputMediaPhoto and InputMediaVideo)optional argumentshttps://core.telegram.org/bots/api#sendmediagroup
poll{ question (string), options (array of string) }optional arguments
stickersticker (InputFile/string)optional argumentshttps://core.telegram.org/bots/api#sendsticker
dice-optional argumentshttps://core.telegram.org/bots/api#senddice
venue{ latitude (float), longitude (float), title (string), address (string) }optional argumentshttps://core.telegram.org/bots/api#sendvenue
contact{ phone_number (string), first_name (string) }optional argumentshttps://core.telegram.org/bots/api#sendcontact

The content format depends on the message type. E.g. if you send a text message then the content format is a string, if you send a location, the content format is an object containing latitude and longitude. See also "available methods" in the api core description.

Outputs

Basically the input msg object is forwarded unchanged to the node's output.

The node has up to two outputs (selectable via the Send errors to second output flag):

  1. The node's first/upper output (Standard Output) is used if the message was successfully transmitted.
  2. The node's second/lower output (Error Output) is used when an exception occured. The output msg object contains a string property msg.error.

Issueing API commands

Additionally to sending content, the sender node can be used to issue commands direct to the API. In this case the msg.payload elements contain (see examples for further details):

The msg.payload.type needs to be set to one of the following values:

msg.payload.typemsg.payload.contentmsg.payload.optionssee also
editMessageCaption-optional argumentshttps://core.telegram.org/bots/api#editmessagecaption
editMessageTexttext (string)optional argumentshttps://core.telegram.org/bots/api#editmessagetext
editMessageReplyMarkup-optional argumentshttps://core.telegram.org/bots/api#editmessagereplymarkup
editMessageMediamedia (InputMedia)optional argumentshttps://core.telegram.org/bots/api#editmessagemedia
deleteMessagemessage_id (integer)-https://core.telegram.org/bots/api#deletemessage
editMessageLiveLocation{ latitude (float), longitude (float) }optional argumentshttps://core.telegram.org/bots/api#editmessagelivelocation
stopMessageLiveLocation-optional argumentshttps://core.telegram.org/bots/api#stopmessagelivelocation
callback_queryurl (string)optional argumentshttps://core.telegram.org/bots/api#answercallbackquery
answerCallbackQueryurl (string)optional argumentshttps://core.telegram.org/bots/api#answercallbackquery
inline_query{ inlineQueryId (string), results (array of InlineQueryResult) }optional argumentshttps://core.telegram.org/bots/api#answerinlinequery
answerInlineQuery{ inlineQueryId (string), results (array of InlineQueryResult) }optional argumentshttps://core.telegram.org/bots/api#answerinlinequery
answerWebAppQuery{ webAppQueryId (string), result (InlineQueryResult) }-https://core.telegram.org/bots/api#answerwebappquery
actionaction (string)optional argumentshttps://core.telegram.org/bots/api#sendchataction
sendChatActionaction (string)optional argumentshttps://core.telegram.org/bots/api#sendchataction
leaveChat--https://core.telegram.org/bots/api#leavechat
exportChatInviteLink--https://core.telegram.org/bots/api#exportchatinvitelink
createChatInviteLink--https://core.telegram.org/bots/api#createchatinvitelink
banChatMemberuser_id (integer)optional argumentshttps://core.telegram.org/bots/api#banchatmember
unbanChatMemberuser_id (integer)optional argumentshttps://core.telegram.org/bots/api#unbanchatmember
restrictChatMemberuser_id (integer){ permissions (ChatPermissions) }https://core.telegram.org/bots/api#restrictchatmember
promoteChatMemberuser_id (integer)optional argumentshttps://core.telegram.org/bots/api#promotechatmember
setChatAdministratorCustomTitleuser_id (integer)custom title as stringhttps://core.telegram.org/bots/api#setchatadministratorcustomtitle
setChatPhotophoto (InputFile)-https://core.telegram.org/bots/api#setchatphoto
setChatTitletitle (string)-https://core.telegram.org/bots/api#setchattitle
setChatDescriptionoptional argumentsoptional argumentshttps://core.telegram.org/bots/api#setchatdescription
pinChatMessagemessage_id (integer)optional argumentshttps://core.telegram.org/bots/api#pinchatmessage
unpinChatMessageoptional arguments-https://core.telegram.org/bots/api#unpinchatmessage
unpinAllChatMessages--https://core.telegram.org/bots/api#unpinallchatmessages
getChatAdministrators--https://core.telegram.org/bots/api#getchatadministrators
getChatMemberCount--https://core.telegram.org/bots/api#getchatmembercount
getChat--https://core.telegram.org/bots/api#getchat
getChatMemberuser_id (integer)-https://core.telegram.org/bots/api#getchatmember
approveChatJoinRequestuser_id (integer)-https://core.telegram.org/bots/api#approvechatjoinrequest
declineChatJoinRequestuser_id (integer)-https://core.telegram.org/bots/api#declinechatjoinrequest
sendInvoice{ title (string), description (string), payload (string), providerToken (string), startParameter (string), currency (string), prices (string) }optional argumentshttps://core.telegram.org/bots/api#sendinvoice
answerPreCheckoutQuery{ preCheckoutQueryId (string), ok (boolean)}optional argumentshttps://core.telegram.org/bots/api#answerprecheckoutquery
pre_checkout_query{ preCheckoutQueryId (string), ok (boolean)}optional argumentshttps://core.telegram.org/bots/api#answerprecheckoutquery
shipping_query{ shippingQueryId (string), ok (boolean) }optional argumentshttps://core.telegram.org/bots/api#answershippingquery
answerShippingQuery{ shippingQueryId (string), ok (boolean) }optional argumentshttps://core.telegram.org/bots/api#answershippingquery
getForumTopicIconStickers--https://core.telegram.org/bots/api#getforumtopiciconstickers
createForumTopicname (string)optional argumentshttps://core.telegram.org/bots/api#createforumtopic
editForumTopicmessage_thread_id (integer)optional argumentshttps://core.telegram.org/bots/api#editforumtopic
closeForumTopicmessage_thread_id (integer)-https://core.telegram.org/bots/api#closeforumtopic
reopenForumTopicmessage_thread_id (integer)-https://core.telegram.org/bots/api#reopenforumtopic
deleteForumTopicmessage_thread_id (integer)-https://core.telegram.org/bots/api#deleteforumtopic
editGeneralForumTopicname (string)-https://core.telegram.org/bots/api#editgeneralforumtopic
closeGeneralForumTopic--https://core.telegram.org/bots/api#closegeneralforumtopic
reopenGeneralForumTopic--https://core.telegram.org/bots/api#reopengeneralforumtopic
hideGeneralForumTopic--https://core.telegram.org/bots/api#hidegeneralforumtopic
unhideGeneralForumTopic--https://core.telegram.org/bots/api#unhidegeneralforumtopic
stopPollmessage_id (integer)optional argumentshttps://core.telegram.org/bots/api#stoppoll
setChatAdministratorCustomTitlemessage_id (integer)optional argumentshttps://core.telegram.org/bots/api#setchatadministratorcustomtitle

The content format of the command arguments (required and optional) depends on the api command.
See also "available methods" in the api core description.

Command Node

The command node can be used for triggering a message when a specified command is received: e.g. /help. See examples below. Note that commands always start with a / like /help, /start, /stop. if you have several bots in one group chat implementing the same command e.g. /help you should send commands directly to a dedicated bot using the full notation /help@YourBot to avoid that different bots would get triggered at once. It it recommended to turn on the strict mode in this case.

node-appearance-command
Fig. 8: Command node appearance

Configuration

The node's configuration contains the following special properties:

Outputs

The node has up to two outputs (selectable via the Has response output flag):

  1. The node's first/upper output (Standard Output) is used if the message is from an authorized user and it contains a specified command at the beginning of the message.
  2. The node's second/lower output (Unauthorized Output) is used in all other cases. This may be the case when security is applied (via configuration properties Users and ChatIds) and the user is not authorized to access the bot or if it is from an autorized user and the message does not contain a specified command.

The second output is useful when you want to use a keyboard. See example below.
It is only issued if a command was received before. If another command was triggered in the meantime, the pending status of the first one is reset. The state is stored per user and per chat.

Event Node

A telegram node that triggers the output when a event is received from a chat.

node-appearance-event
Fig. 9: Event node appearance

The node receives events from the bot like:

Configuration

The Event to be received is configured via the node's configuration dialog:

<img src="images/TelegramBotConfigurationDialogEvent.png" title="Event node configuration" width="400" />

Fig. 10: Event node configuration dialog

With the Event property the listening event is selcted.

The Auto-Answer checkbox can be set for Callback_Query. If activated, you do not need to send an explicit answer to the bot on your own.

Outputs

The output msg.payload typically contains the parsed data as follows:

The output msg.originalMessage contains the raw data object from the underlying library, and contains many useful properties.

See also the following descriptions for the event handling:

Reply Node

A telegram node that is triggered when someone answered to a specified message.

node-appearance-reply
Fig. 11: Reply node appearance

The reply node waits for an answer to a specified message. It should be used in conjunction with the sender node.

Input

Standard Input: Calls the onReplyToMessage of the bot.

Output

Standard Output: Contains the result from the onReplyToMessage call.

This node may be useful, when the bot e.g. sent a message and you want to take some action when someone responded to this specified message. Responding to messages is done by clicking on the message in your client and choose answer from the popup.

The msg.payload contains:

Control Node

This node can be used to stop, start, restart a bot (config node). It send a message to the output after every poll cycle. See the example flow control bot in the examples folder.

Input

The msg.payload contains:

Output

Standard Output: Contains the msg object passed to the input or a message with poll information:

Keyboards

General

Keyboards can be used to interact with the user by displaying a flexibly definable keyboard. The user then presses one of the keys to give his selection.
Two different keyboard types are available:

A remarkable feature of Inline Keyboards is the ability to change them on the fly. See examples section for further details.

With keyboards also complex keyboard procedures with several hierarchy levels can be implemented to direct the user in a step by step button pressing procedure.

Custom keyboards

Examples for Custom keyboards can be seen in the Keyboards section of the telegram bot description.

Custom keyboards act with message nodes and telegram receiver nodes to handle the keyboard procedure, which is as follows:

  1. The appearance of the keyboard is initiated via a message sent to the bot. In the message the keyboard configuration is defined within the msg.payload.options property.
  2. The user presses a displayed key.
  3. The key text is sent back via a message. This message is received with a receiver node and can then be evaluated.

The keyboard configuration contains the key description, layout and further options. A description of it can be found in the ReplyKeyboardMarkup section.
See also the custom keyboard example.

A basic flow handling a custom keyboard with its reply shows the following figure.

Alt text
custom keyboard basic flow
Fig. 12: Custom keyboard basic flow example

<details> <summary>Click to expand code snippet for <em><b>build keyboard</b></em> function</summary>
var opts = {
  reply_markup: JSON.stringify({
    keyboard: [
      ['A1'],
      ['A2']],
      'resize_keyboard' : true,
      'one_time_keyboard' : true
  })
};


msg.error = false;
// Dialogaufbau
msg.payload.content = 'Selection?';
msg.payload.options = opts;


msg.payload.chatId = 123445;
msg.payload.messageId = 99;
msg.payload.sentMessageId = 99;
msg.payload.type = "message";

return [ msg ];
</details> <br>

The look&feel at a mobile device could look like the following figure:

<img src="images/TelegramBotConfirmationMessage4.png" title="Keyboard look@feel" width="300" />

Fig. 13: Custom keyboard example screenshot

The answering options are located below the user text input field.

Several options for the keyboard layout can be found there in the Telegram Bot API SDK description.
The keyboard layout shown in Fig. 12 (given in the create response node) is

keyboard: [
  ['Yes'],
  ['No']],

Another example of a different key layout may be to arrange several keys in one line. This may be like:

keyboard: [
  ['Yes','No','Maybe'],
  ['Conditional']],

This leads to a layout like:

<img src="images/TelegramBotConfirmationMessage5.png" title="Keyboard look@feel" width="300" />

Fig. 14: Custom keyboard example screenshot with different layout

Inline keyboards

Examples for Inline keyboards can be seen in the Inline keyboards section of the telegram bot description.

Inline keyboards act with message nodes and event nodes (event Callback Query) to handle the keyboard procedure, which is as follows:

  1. The appearance of the keyboard is initiated via a message sent to the bot. In the message the keyboard configuration is defined within the msg.payload.options property.
  2. The user presses a displayed key.
  3. The key text is sent back via an Callback Query event. This message is received with an event node and can then be evaluated.

The keyboard configuration contains the key description, layout and further options. A description of it can be found in the InlineKeyboardMarkup section.
See also the inline keyboard example.

A basic flow handling an inline keyboard with its reply shows the following figure.

Alt text
inline keyboard basic flow
Fig. 15: Inline keyboard basic flow example

<details> <summary>Click to expand code snippet for <em><b>build keyboard</b></em> function</summary>
var opts = {
  reply_markup: JSON.stringify({
    "inline_keyboard": [[
                {
                    "text": "A1",
                    "callback_data": "1"            
                },
                {
                    "text": "A2",
                    "callback_data": "2"            
                }]
            ]
  })
};

msg.payload.content = "Selection?";
msg.payload.options = opts;
msg.payload.chatId = 12345;
msg.payload.messageId = 99;
msg.payload.sentMessageId = 99;
msg.payload.type = "message";

return [ msg ];
</details> <br>

The look&feel at a mobile device could look like the following figure:

<img src="images/TelegramBotInlineKeyboard4.png" title="Inline keyboard look@feel" width="300" />

Fig. 16: Inline keyboard example screenshot

The answering options are located within the dialog field.

Several options for the keyboard layout can be found there in the Telegram Bot API SDK description.
The keyboard layout shown in Fig. 15 (given in the inline keyboard message node) is

"inline_keyboard": [[
     {"text": "Yes","callback_data": "FOO YES"},
     {"text": "No", "callback_data": "FOO NO"}
]]

Another example of a different key layout may be to arrange several keys in one line. This may be like:

"inline_keyboard": [[
      {"text": "Yes","callback_data": "FOO YES"},
      {"text": "No","callback_data": "FOO NO"}],
    [
      {"text": "#1","callback_data": "FOO ONE"},
      {"text": "#2","callback_data": "FOO TWO"},
      {"text": "#3","callback_data": "FOO THREE"}
    ],
    [
      {"text": "dog","callback_data": "FOO DOG"},
      {"text": "eel","callback_data": "FOO EEL"},
      {"text": "cow","callback_data": "FOO COW"},
      {"text": "cat","callback_data": "FOO CAT"}
    ]
]

This leads to a layout like:

<img src="images/TelegramBotInlineKeyboard5.png" title="Inline keyboard look@feel" width="200" />

Fig. 17: Inline Keyboard example screenshot with different layout

Examples


Remark: Example flows are present in the examples subdirectory. In Node-RED they can be imported via the import function and then selecting Examples in the vertical tab menue.
All example flows can also be found in the examples folder of this package.


Implementing a simple echo

This example is self-explaining. The received message is returned to the sender.

Alt text
echo flow
Fig. 18: Simple echo flow

Implementing a /help command

This flow returns the help message of your bot. It receives the command and creates a new message, which is returned:

Alt text
help message flow
Fig. 19: Help command flow example

<details> <summary>Click to expand code snippet for <em><b>create help text</b></em> function</summary>
var helpMessage = "/help - shows help";

helpMessage += "\r\n/foo - opens a dialog";

helpMessage += "\r\n";
helpMessage += "\r\nYou are welcome: "+msg.originalMessage.from.username;
helpMessage += "\r\nYour chat id is " + msg.payload.chatId;
helpMessage += "\r\n";

msg.payload.content = helpMessage;

return msg;
</details> <br>

The output looks on a mobile device like the following figure:

Alt text
Fig. 20: Help command screenshot

Note: You can access the sender's data via the msg.originalMessage property.

Implementing a custom keyboard

Custom keyboards are very useful for getting additional data from the sender. When the command is received the first output is triggered and a dialog is opened:

Alt text
keyboard flow
Fig. 21: Keyboard example

The answer is send to the second output triggering the lower flow. Data is passed via global properties here.

<details> <summary>Click to expand code snippet for <em><b>confirmation message</b></em> function</summary>
context.global.keyboard = { pending : true };

var opts = {
  reply_to_message_id: msg.payload.messageId,
  reply_markup: JSON.stringify({
    keyboard: [
      ['Yes'],
      ['No']],
      'resize_keyboard' : true,
      'one_time_keyboard' : true
  })
};

msg.payload.content = 'Really?';
msg.payload.options = opts;

return [ msg ];
</details> <details> <summary>Click to expand code snippet for <em><b>create response</b></em> function</summary>
if (context.global.keyboard.pending) {
    context.global.keyboard.pending = false;

    if(msg.payload.content === 'Yes') {
        msg.payload.content = 'Yes';
        return [msg, null];
    }
    else     {
        msg.payload.content = 'No';
        return [null, msg];
    }
}
</details>

Implementing an on reply node

Next to the keyboard the bot could also ask a question and wait for the answer. When the user responds to a specified message the telegram reply node can be used:

Alt text onreplymessage flow
Fig. 22: On reply example flow

The question is sent to the chat. This node triggers the on reply node waiting for the answer.

Note: The user has to explicitly respond to this message. If the user only writes some text, the get reply node will not be triggered.

<details> <summary>Click to expand code snippet for <em><b>create question</b></em> function</summary>
msg.payload.type = 'message';
msg.payload.content = 'Really?';
msg.payload.options = {reply_to_message_id : msg.payload.messageId}

return [ msg ];
</details> <details> <summary>Click to expand code snippet for <em><b>switch answer</b></em> function</summary>
if(msg.payload.content === 'Yes')
{
    return [msg, null];   
}
else
{
    return [null, msg];   
}
</details>

Implementing an inline keyboard

An inline keyboard contains buttons that can send a callback query back to the bot to trigger any kind of function. When the command is received the first output is triggered and a inline keyboard is shown:

Alt text
inlinekeyboard flow
Fig. 23: Inline keyboard example flow

The callback query is received by the event node. It must be answered like shown as follows. There you can add your code to trigger the desired bot command. The answer contains the callback query data in msg.payload.content.

<details> <summary>Click to expand code snippet for <em><b>inline keyboard message</b></em> function</summary>
var opts = {
  reply_to_message_id: msg.payload.messageId,
  reply_markup: JSON.stringify({
    "inline_keyboard": [[
                {
                    "text": "Yes",
                    "callback_data": "FOO YES"
                },
                {
                    "text": "No",
                    "callback_data": "FOO NO"
                }]
            ]
  })
};

msg.payload.content = 'Are you sure?';
msg.payload.options = opts;

return [ msg ];
</details> <details> <summary>Click to expand code snippet for <em><b>set answer options</b></em> function</summary>
var show_alert = false; // you can set this to true to open a dialog with the answer in the client.

// msg.payload.content contains the callback data from the keyboard.
// You may change this value here.
msg.payload.options = show_alert;

return [ msg ];
</details>

Edit an inline keyboard

An inline keyboard can be modified using the 'editMessageReplyMarkup' instruction. To be able to modify an existing message you need to know the messageId of the message of the keyboard. A sample flow is provided in the examples folder and could look like this:

Alt text
editinlinekeyboard flow
Fig. 24: Edit an inline keyboard example flow

The message id needs to be saved in the flow or global context (via node save messageId). This is just a demo assuming that there is only one single chat.

As next the initial keyboard has to be replaced with a modified one using the api command editMessageReplyMarkup command as type (via node edit inline keyboard message).
As an alternative to 'editMessageReplyMarkup you can also use the api command editMessageText to replace the keyboard and also the text as given in the function example edit message text.

The switch node evaluate callback query just handles the response and hides the keyboard using another api command deleteMessage.

<details> <summary>Click to expand code snippet for <em><b>initial inline keyboard message</b></em> function</summary>
context.global.keyboard = { pending : true, messageId : msg.payload.messageId };

var opts = {
  reply_to_message_id: msg.payload.messageId,
  reply_markup: JSON.stringify({
    "inline_keyboard": [[
                {
                    "text": "Yes",
                    "callback_data": "FOO YES"
                },
                {
                    "text": "No",
                    "callback_data": "FOO NO"
                }]
            ]
  })
};

msg.payload.content = 'Do you want to hide the inline keyboard?';
msg.payload.options = opts;

return [ msg ];
</details> <details> <summary>Click to expand code snippet for <em><b>save messageId</b></em> function</summary>
// We store the messageId to be able to edit this reply in the callback query.
context.global.keyboard.messageId = msg.payload.sentMessageId;
return [ msg ];
</details> <details> <summary>Click to expand code snippet for <em><b>edit inline keyboard message</b></em> function</summary>
// This is the message id of the initial keyboard that is simply exchanged by a new one.
var messageId = context.global.keyboard.messageId;

// This is a sample of how to send a second inline keyboard with modified buttons
var reply_markup = JSON.stringify({
    "inline_keyboard": [[
                {
                    "text": "Are you really sure?",
                    "callback_data": "FOO YES REALLY"
                },
                {
                    "text": "No",
                    "callback_data": "FOO NO"
                }]
            ]
  });


var options = {
    chat_id : msg.payload.chatId,
    reply_markup : reply_markup,
    message_id : messageId
};

msg.payload.type = 'editMessageReplyMarkup';
msg.payload.content = reply_markup;
msg.payload.options = options;

return [ msg ];
</details> <details> <summary>Click to expand code snippet for <em><b>evaluate callback query</b></em> function</summary>
// This is a sample switch to demonstrate the handling of the user input.
if(msg.payload.content === "FOO YES REALLY")
{
    // Hide the keyboard and forget the messageId
    msg.payload.type = 'deleteMessage';
    msg.payload.content = context.global.keyboard.messageId
    context.global.keyboard.messageId = null;

    // You could also send a editMessageReplyMarkup with an empty reply_markup here
    return [ null, msg ];
}
else
{
    var show_alert = false; // you can set this to true to open a dialog with the answer in the client.

    // msg.payload.content contains the callback data from the keyboard.
    // You may change this value here.
    msg.payload.options = show_alert;

    return [ msg, null ];
}
</details> <details> <summary>Click to expand code snippet for <em><b> an alternative to node edit inline keyboard message</b></em> function</summary>
// This is the message id of the initial keyboard that is simply exchanged by a new one.
var messageId = context.global.keyboard.messageId;

// This is a sample of how to send a second inline keyboard with modified buttons
var reply_markup = JSON.stringify({
    "inline_keyboard": [[
                {
                    "text": "Are you really sure?",
                    "callback_data": "FOO YES REALLY"
                },
                {
                    "text": "No",
                    "callback_data": "FOO NO"
                }]
            ]
  });


var options = {
    chat_id : msg.payload.chatId,
    reply_markup : reply_markup,
    message_id : messageId
};

msg.payload.type = 'editMessageText';
msg.payload.content = "Confirmation question";
msg.payload.options = options;

return [ msg ];
</details> <br>

The following figure shows the behaviour on a mobile device (e.g. cell phone). The example given above replaces the button description within the node edit inline keyboard message:

Alt text
Fig. 25: Edit an inline keyboard example screenshot

Implementing an inline_query

Bots can be called from any chat via inline_query when the bot is set to inline mode in botfather via /setinline (see https://core.telegram.org/bots/api#inline-mode).
A sample flow is provided in the examples folder and could look like this:

Alt text
inlinequery flow
Fig. 26: inline_query example flow

The inline_query must be answered by sending a results array. See https://core.telegram.org/bots/api#inlinequeryresult.
The example just returns two simple articles, but almost every kind of content can be returned.

Note that the inline_query can also contain the location of the sender. To enable this call /setinlinegeo in botfather.

<details> <summary>Click to expand code snippet for <em><b>create results</b></em> function</summary>
// we have to set the results propery with the answer(s)
// see https://core.telegram.org/bots/api#inlinequeryresult
var results = [
    // result 1 is InlineQueryResultArticle
    {
        type : "article",
        id : "1",
        title : "Result 1",

        // InputTextMessageContent see https://core.telegram.org/bots/api#inputmessagecontent
        input_message_content : {
            message_text : "The message 1",
            parse_mode : "Markdown",
            disable_web_page_preview : true
        }
    },

    // result 2 is InlineQueryResultArticle
    {
        type : "article",
        id : "2",
        title : "Result 2",

        // InputTextMessageContent see https://core.telegram.org/bots/api#inputmessagecontent
        input_message_content : {
            message_text : "The message 2",
            parse_mode : "Markdown",
            disable_web_page_preview : false
        }
    }
];

msg.payload.results = results;

return msg;
</details>

Receiving a location

Locations can be send to the chat. The bot can receive the longitude and latitude:

Alt text
receivinglocation flow
Fig. 27: Receiving a location example

<details> <summary>Click to expand code snippet for <em><b>create location message</b></em> function</summary>
if(msg.payload.location) {
    var lat = msg.payload.location.latitude;
    var lng = msg.payload.location.longitude;
    var user = msg.payload.from.username;

    msg.payload.type = 'message';
    msg.payload.content = user + ' moved to lat=' + lat + ' lon=' + lng;

    return msg;
}
else {
    return null;
}
</details>

Sending messages to a specified chat

If you have the chatId, you can send any message without the need of having received something before.

Alt text
sendmessagetochat flow
Fig. 28: Sending messages to a chat example flow

Sending markdown contents in messages is described below.

<details> <summary>Click to expand code snippet for <em><b>send to specific chat</b></em> function</summary>

msg.payload = {chatId : 138708568, type : 'message', content : 'ping'}

return msg;
</details>

Sending photos, videos, ...

Additionally to sending text messages you can send almost any file based content like photos and videos. Set the right type and content and you are done. If you want to respond to a received message with a picture you could write:

msg.payload.content = 'foo.jpg';
msg.payload.type = 'photo';

Note: The chatId is already the correct one when you reuse the received msg object from a receiver node.

You can use one of the following types to send your file as content:

Note that some clients convert gif animations to videos. This will lead to problems when passing a received animation object to the sender node as the content is mp4 instead of gif. The content can be downloaded automatically to a local folder by setting the Download Directory property in the receiver node configuration dialog. You can add a caption to photo, audio, document, video, video_note, animation, voice by setting the caption property as follows:

msg.payload.caption = "You must have a look at this!";

The following types require a special content format to be used. See the underlying node api for further details.

An example flow to send a photo is shown in the following figure:

Alt text
Fig. 29: Photo sending example flow

<details> <summary>Click to expand code snippet for <em><b>send picture</b></em> function</summary>
msg.payload.content = 'foo.jpeg';
msg.payload.type = 'photo';

/* type can be one of the following
photo
audio
video
sticker
dice
voice
document
*/

return msg;
</details>

Instead of passing a file name you can also directly pass the buffer of a photo as msg.payload.content. This is useful whenever you download files or already have the buffer in memory from a previous action. The following example demonstrates how to accomplish this: SendPhotoBuffer

<details> <summary>Click to expand code snippet for <em><b>send picture buffer</b></em> function</summary>
msg.payload.content = buffer;
msg.payload.type = 'photo';

/* type can be one of the following
photo
audio
video
sticker
dice
voice
document
*/

return msg;
</details>

Sending a mediaGroup as album

To send several photos as an album you can use the mediaGroup type. For this type of media group you have to set the content to an array of object type InputMediaPhoto. The contents of the msg.payloadobject is shown below (in JSON format).

An example flow sending a media group is shown in the following figure:

Alt text
sendmediagroup flow
Fig. 30: Sending media group example flow

<details> <summary>Click to expand code snippet for <em><b>create media group</b></em> function</summary>
// sendMediaGroup example: send  between 2 and 10 media.
// Note that type can also be video.
// and the caption property is optional.
// see https://core.telegram.org/bots/api#inputmediaphoto
// see https://core.telegram.org/bots/api#inputmediavideo

msg.payload.type = "mediaGroup";
msg.payload.content = [
    {
        type : "photo",
        media : "/pic/frame_1.jpg",
        caption : "Photo 1"
    },
    {
        type : "photo",
        media : "/pic/frame_2.jpg",
        caption : "Photo 2"
    }
];

return msg;
</details>

Sending contacts

Sending a contact is limited to the elements supported by the underlying API to "phone_number" and "first_name". But you can also receive "last_name" if the client sends it.

msg.payload.type = 'contact';
msg.payload.content : {  phone_number: "+49 110", first_name: "Polizei" };

An example flow sending a contact is shown in the following figure:

Alt text
sendcontacttochat flow
Fig. 31: Sending contact example flow

<details> <summary>Click to expand code snippet for <em><b>contact</b></em> function</summary>
msg.payload =
{
    chatId : 12345,
    type : "contact",
    content :
    {
        phone_number: "+49 30 987654321",
        first_name: "Max",
        last_name: "Mustermann"
    },
    options :
    {
        disable_notification : true
    }
}

return msg;
</details>

The display within the telegram app of a sent contact may look like this:

Alt text
Fig. 32: Sending contact example screenshot

Sending chat actions

When the bot needs some time for further processing but you want to give a hint to the user what is going on, then you can send a chat action which will appear at the top of the channel of the receiver. You can either use "sction" or "sendChatAction" as type:

msg.payload.type = 'action';
msg.payload.content = "typing";

The content can be one of the following

The following example illustrate how to send for example "typing...". Of course a real bot would send the real data after finishing the processing, but this is not part of the example.

An example flow sending a chat action is shown in the following figure:

Alt text
sendchataction flow
Fig. 33: Sending chat actions example flow

<details> <summary>Click to expand code snippet for <em><b>send chat action</b></em> function</summary>
// demonstrates sending a chat action (see https://core.telegram.org/bots/api#sendchataction)
var type = msg.payload.type;
msg.payload.type = "action";

switch(type){
    case "message":
        msg.payload.content = "typing";
        break;

    case "photo":
        msg.payload.content = "upload_photo";
        break;

    case "video":
        msg.payload.content = "upload_video";
        break;

    case "audio":
        msg.payload.content = "upload_audio";
        break;

    case "document":
        msg.payload.content = "upload_document";
        break;

    case "location":
        msg.payload.content = "find_location";
        break;

    case "video_note":
        msg.payload.content = "upload_video_note";
        break;

    default:
        msg = null;
        break;
}

return msg;
</details>

Sending live locations

Locations can be send to the chat as described above and then updated afterwards: live location update. To achieve this, you have to provide the live_period in seconds in the options when sending the location.

msg.payload.type = 'location';
msg.payload.content = {
    latitude : lat,
    longitude : lng
};

msg.payload.options = {
    live_period : time
};  

To be able to update this location message you need to store the message id of that sent message. This can be done by storing it somewhere in the flow context as follows:

var messageId = msg.payload.sentMessageId;
flow.set("messageId", messageId);

Now you can edit the location as often as you want within the live_period using the API command editMessageLiveLocation:

var messageId = flow.get("messageId");
var chatId = msg.payload.chatId;

msg.payload.type = 'editMessageLiveLocation';
msg.payload.content = {
    latitude : lat,
    longitude : lng
};

msg.payload.options = {
    chat_id : chatId,
    message_id : messageId
};  

If you want to abort updating the location then you can send the API command stopMessageLiveLocation.

var messageId = flow.get("messageId");
var chatId = msg.payload.chatId;

msg.payload.type = 'stopMessageLiveLocation';
msg.payload.options = {
    chat_id : chatId,
    message_id : messageId
};  

An example flow sending the live location is shown in the following figure:

Alt text
livelocation flow
Fig. 34: Sending live location example flow

Receiving live location updates

When a user sends his location then it is received by the standard message receiver node. In the case of a live location update, you will receive the same message event as one would edit an already existing message in the chat (edit_message). The example above contains an event handler node that receives those message edits, and filters for the ones that contain a location.

Forwarding messages

All types of messages can be forwarded to another chat (see forwardMessage). Just send a message to the sender node and add forward property to the payload. The forward object must contain the chatId of the chat the message should be sent to. In the following example the received message will be forwarded to the chat 1:

msg.payload.forward = { chatId : 1 };
return msg;

You can pass the optional paramaters via mag.payload.forward.options. See the example flow forward message in the examples folder.

The messageId to forward is taken from: msg.payload.messageId. The source chatId is taken from: msg.payload.chatId. Both properties are set by the receiver node, but you can also set those manually without having received anything. The following example sends message 2 from chat 1 to chat 3:

msg.payload.chatId = 1;
msg.payload.messageId = 2;
msg.payload.forward = { chatId : 3 };
return msg;

Remark: You need to have sufficient permissions to be able to do this message forwarding.

Copying messages

All types of messages can be copied to another chat (see copyMessage). Just send a message to the sender node and add copy property to the payload. The copy object must contain the chatId of the chat the message should be sent to. In the following example the received message will be copied to the chat 1:

msg.payload.copy = { chatId : 1 };
return msg;

You can pass the optional paramaters via mag.payload.copy.options. See the example flow copy message in the examples folder.

Remark: You need to have sufficient permissions to be able to do this message forwarding.

Downloading files manually

The receiver node can automatically download files into the configured download directory. You can also download the files manually using a fileId by passing the following message to the sender node:

msg.payload.download = { 
  fileId : "<yourfileidhere>",
  filePath : "c:\\downloaddirectory",
  fileName : "foo.jpg" // this is optional
  };
return msg;

See the example flow download file in the examples folder.

Getting file information manually

If you do not want to download a file but forward a weblink, then you can retrieve the info using getfile. You can also get the file information manually using a fileId by passing the following message to the sender node:

msg.payload.getfile = { 
  fileId : "<yourfileidhere>",
  };
return msg;

See the example flow download file in the examples folder.

Creating polls

You can create polls, listen to poll events and even receive polls. A poll can be created using the following pattern:

msg.payload.type = 'poll';
msg.payload.content = "What do you think?";
msg.payload.options = ["A", "B", "C" ];
// you can add optional parameters see https://core.telegram.org/bots/api#sendpoll
msg.payload.optional = { allows_multiple_answers : true };
return msg;

See the example flow create poll in the examples folder.

Use the event node 'Poll' for receiving the updated poll results.

Web App Data

WebApps is a new feature described here: https://core.telegram.org/bots/webapps You need to run your web app using a public web server. The url of this web page can be used to communicate with the bot. Note that the communication must use the https protocol.

See the example flow web app data in the examples folder.

Advanced options when sending messages

Text messages can be formatted as markdown, e.g. to support bold and italic style. To enable markdown format set the parse_mode options property as follows:

msg.payload.options = {parse_mode : "Markdown"};

or

msg.payload.options = {parse_mode : "MarkdownV2"};

An example function node may contain:

Alt text
sendmessagetochat flow
Fig. 35: Sending messages to a chat example flow

<details> <summary>Click to expand code snippet for <em><b>send markdown</b></em> function</summary>
var message = 'You can also send *markdown* formatted messages.';
msg.payload = {chatId : 138708568, type : 'message', content : message};

// activate markdown
msg.payload.options = {disable_web_page_preview : true, parse_mode : "Markdown"};

return msg;
</details> <br>

Telegram always adds a preview when you send a web link. To suppress this behavior you can disable the preview by setting the disable_web_page_preview options property as follows:

msg.payload.options = {disable_web_page_preview : true};

The callback query answer has a show_alert option to control the visibility of the answer on the client. It is directly mapped to the options property.

msg.payload.options = true;

Configuring security

The configuration node contains two properties for applying security to your bot. You can choose between configuring the single user names or configure one or more chatIds that are allowed to access the bot. The values must be separated by a comma like shown in the screenshot.

<img src="images/TelegramBotSecurity.png" title="Applying security" width="500" />

Fig. 36: Security configuration in the bot configuration node

Note: The Users in the security configuration are defined via their usernames. These usernames are configured in the telegram app via the settings dialog. Configured usernames typically begin with a '@' in the app. In the Users field, no '@' is used.

Note: The chatIds are positive in chats where you talk to the bot in an 1:1 manner. A negative chatId indicates a group chat. Everybody in this group is allowed to use the bot if you enter the chatId of the group into the lower field of the configuration node.

Detecting unauthorized access

The receiver node has a second output, that is triggered when authorization fails. The message is send to this output for further processing. You can reply on that message or log it to a file to see who wanted to access your bot.

Alt text
unauthorizedaccess flow
Fig. 37: Detecting unautorized access example flow

The message needs to be formatted before the log to file node can be triggered.

<details> <summary>Click to expand code snippet for <em><b>create log string</b></em> function</summary>
var chatId = msg.payload.chatId;
var username = msg.originalMessage.from.username;

msg.originalMessage.timestamp = new Date();
var message = JSON.stringify(msg.originalMessage);

msg.topic = username + ' ' + chatId;
msg.payload = [msg.topic, message];

return msg;
</details> <br>

Dynamic authorization

If you want to authorize and unauthorize users or chats during runtime you can insert a script into the config instead of a hard coded list. The script starts with { and ends with }. Generally spoken you can make use of the context in two ways (e.g. in a function node):

  1. context.global.mykey = myvalue; // = old notation
  2. global.set(mykey, myvalue); // = new notation

Only the latter one can be seen in the context browser window while the first is only stored as variable in memory. For using a dynamic list stored in the context you must add a script into the configuration (in the row Users and/or ChatIds):

  1. {context.global.hereyourkey} for approach one
  2. {gobal.get("hereyourkey")} for approach two

If the config starts with { and ends with } the expression is evaluated as a script. For example you can write something like

{context.global.username}
{context.global.chatids}

or

{global.get("usernames")}
{global.get("chatids")}

Usage of the latter notation is recommended.

The following example flow shows the various options:

<img src="images/TelegramBotDynamicAuthorization3.png" title="Granting access using a function node" width="550" />

dynamic authorization flow
Fig. 38: Dynamic granting access example flow

The configuration dialog for scripting contents looks like this:

<img src="images/TelegramBotDynamicAuthorization.png" title="Dynamic authorization" width="700" />

Fig. 39: Dynamic authorization with scripting contents

The authorization can be modified using a change node:

<img src="images/TelegramBotDynamicAuthorization2.png" title="Granting access using a change node" width="550" />

Fig. 40: Granting access using a change node

As an alternative, the authorization can be modified using a function node:

Note that you can also use the function node with the new notation like gobal.set(key, value).

<details> <summary>Click to expand code snippet for <em><b>Grant access to User and chat 1</b></em> function</summary>
// classic aproach for using context.
context.global.usernames = [ "User" ];
context.global.chatids = [ 1 ];

return msg;
</details> <details> <summary>Click to expand code snippet for <em><b>No Access</b></em> function</summary>
// classic aproach for using context.
context.global.usernames = [];
context.global.chatids = [];

return msg;
</details> <br>

Restricting permissions in super groups

If you make your bot administrator of a super group then you can use it to restrict permissions of users. See code below for the different options and the documentation here: https://core.telegram.org/bots/api#restrictchatmember

<details> <summary>Click to expand code snippet for <em><b>Restrict permission of User in super group</b></em> function</summary>
var userId = <add your id here>;
var superGroupId = <add your id here>;
var seconds = 60 
var options = {
	// set your permissions here:
	can_send_messages : false,
	can_send_media_messages : false,
	can_send_polls : false,
	can_send_other_messages : false,
	can_add_web_page_previews : false,
	can_change_info : false,
	can_pin_messages : false,
	can_invite_users: true,
	
    until_date: Math.round(((new Date()).getTime() + seconds * 1000) / 1000),
}

var payload = {
    type: "restrictChatMember",
    chatId: superGroupId,
    content: userId,
    options: options
}

msg.payload = payload;

return msg;
</details> <br>

Pin a sent message to a group

If your bot sends a message and you want to pin that sent message, then take the message id of the sent message as content and pin it using the following code. See documentation here: https://core.telegram.org/bots/api#pinchatmessage

<details> <summary>Click to expand code snippet for <em><b>Pin a sent message to a group</b></em> function</summary>

var chatId = <your chatId here>; var message = msg.payload.sentMessageId;

var options = { disable_notification: true }

var payload = { type: "pinChatMessage", chatId: chatId, content: message, options : options }

msg.payload = payload;

return msg;

</details> <br>

Payments

For a full description of how to handle payments see https://core.telegram.org/bots/payments
https://core.telegram.org/bots/api#sendinvoice

send invoice flow full payment example flow

Implementing a simple bot

Putting all pieces together you will have a simple bot implementing some useful functions.

Alt text
simplebot flow
Fig. 41: Simple bot example flow

License

Author: Karl-Heinz Wind

The MIT License (MIT) Copyright (c) <year> <copyright holders>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.