Node-telegram-bot-api: force reply

Created on 8 Sep 2015  路  13Comments  路  Source: yagop/node-telegram-bot-api

I'm tryng to use force_reply without success..
any suggestion?

if (messaggio == '/love') {
    var opts = {
        ReplyKeyboardHide: {},
        reply_to_message_id: msg.message_id,

        reply_markup: JSON.stringify({
            "keyboard": [["Ti amo alla follia"], ["Mi spiace ma non fai per me"]], 
            "one_time_keyboard": true,
            "force_reply": true
        })

    };

bot.sendMessage(chatId, 'Mi ami?', opts);
}

Most helpful comment

To be honest I haven't tested force_reply and if I did I can't remember. According with Telegram docs sendMessage can receive an optional reply_markup with a JSON serialized ReplyKeyboardMarkup OR ReplyKeyboardHide OR a ForceReply.

According with that, ReplyKeyboardHide and ForceReply doesn't have to be JSON serialized. So I would do something like:

reply_markup: {
  "force_reply": true
};

But that makes no sense if you want to send a ReplyKeyboardMarkup too which is the common scenario so I would come with:

reply_markup: JSON.stringify({
  "keyboard": [["Ti amo alla follia"], ["Mi spiace ma non fai per me"]], 
  "one_time_keyboard": true,
  "force_reply": true
});

But you said isn't working so, no idea.

All 13 comments

@yagop, could you help on this?

Any news on this? It seems like the module does not implement forceReply, is that so @yagop? And if yes; any pointers on how to change that?

To be honest I haven't tested force_reply and if I did I can't remember. According with Telegram docs sendMessage can receive an optional reply_markup with a JSON serialized ReplyKeyboardMarkup OR ReplyKeyboardHide OR a ForceReply.

According with that, ReplyKeyboardHide and ForceReply doesn't have to be JSON serialized. So I would do something like:

reply_markup: {
  "force_reply": true
};

But that makes no sense if you want to send a ReplyKeyboardMarkup too which is the common scenario so I would come with:

reply_markup: JSON.stringify({
  "keyboard": [["Ti amo alla follia"], ["Mi spiace ma non fai per me"]], 
  "one_time_keyboard": true,
  "force_reply": true
});

But you said isn't working so, no idea.

That part of the docs confused me as well and I've tried to just add "force_reply": true to a reply_markup that otherwise just contains a ReplyKeyboardMarkup (basically exactly what you did there).

That does not change much.
I did find a way to force a reply message by the user in group chats only, though: When you set the reply_to_message_id of the bots message, the next user message by the "replied to" user will be a reply to that message.
This does not appear to work in private chats, though.

It might just have something to do with the Telegram API itself because, come to think of it, I have not been forced into a reply by a bot in a private chat. I sadly don't have any way to quickly verify that, since your library is my first contact with the bot API.

I have been trying to find answer for this question from other places. The only reasonable answer that I have found is this link

And from C# bot api I have found this (I don't think this is useful). link

In order to create complex reply conversation between bot and user, I think we need to use MongoDB or Redis which will store chat_ids and their current condition to make relevant replies.

The links in your message do not work for me for some reason, so I cannot look into that right now.

What I ended up doing in order to realize a reply conversation with bots (note, that replies _are_ forced in group chat by just setting the correct reply_to_message_id) is, I extended the TelegramBot with another function to specifically send messages, that await an answer and call a callback with the answer message.

To do that I ended up registering for a unique event (comprised of the chat.id and in case of group chats the message_id of the question message sent by the bot) everytime that I send one of those messages that await an answer. The event can then be fired on a general message handler that checks if an incoming message has set its reply_to_message_id.

You have to safeguard against leaking the listeners due to users dragging out the answer or plain not answering, e.g. by having them destroyed after they existed for a while.

@githugger I have edited links. Can you make example of your solution and make PR to examples of this repository?

Regarding the solution I went for, I don't really know if a Pull Request is in order, since the Bot API itself does not supply that kind of functionality and this repo is more of a node interface layer to directly communicate with the API - at least to my understanding.
That's just my opinion on it, though, and if @yagop sees a place for this somewhere I'd be happy to start a pull request with a more finished version of this.

But I'll supply what I did here, so you can take a look at it and see if it is any use to you.

I extended yagos TelegramBot constructor with a function sendUserQuery that sends a message to the user via sendMessage, and returns a promise that resolves when the bot received a reply to said message.

The onText handler in the constructor fires events to the listeners created in sendUserQuery when it receives an appropriate answer message from the user (chat.id and, in the case of a group chat, reply_to_message_id match the sent user-query message).

The handler is restricted to only handle messages that do _not_ start with a forward slash. This is because I stumbled upon some (I think undocumented) behaviour in group chats, where only the handler registered first handled an incoming message, blocking all commands from being received by their respective handler. In private chats I did not have that problem, but the way it is answers to a bot message cannot start with /.

var TelegramBot = require('node-telegram-bot-api');
var Promise = require('bluebird');
var EventEmitter = require('events');
var eventEmitter = new EventEmitter();
var util = require('util');

function TelegramBotPlus() {
    TelegramBot.apply(this, arguments);

    /**
     * Handles only incoming messages that do not (!) begin with a
     * forward-slash.
     * In group chats, messages matched by this one are not passed on to other
     * onText callbacks, which is why I have to distinguish here.
     */
    this.onText(/^(?!\/)(.+)/, function(msg, match) {
        if (msg.chat.type === 'private') {
            // in a private chat, the chatId makes for a unique event
            eventEmitter.emit('msg_rec_' + msg.chat.id.toString(), msg);
        }
        if (msg.chat.type === 'group') {
            // to uniquely identify the event corresponding to the query
            // message, chatId and querymessages id is needed
            eventEmitter.emit('reply_rec_' + msg.chat.id + '_' + msg.reply_to_message.message_id.toString(), msg);
        }
    });
}
util.inherits(TelegramBotPlus, TelegramBot);

TelegramBotPlus.prototype.sendUserQuery = function(chatId, messageText, options) {
    return this.sendMessage(chatId, messageText, options)
        .then(function(msg) {
            if (msg.chat.type == 'private') {
                return new Promise(function(resolve, reject) {
                    eventEmitter.once('msg_rec_' + msg.chat.id, function(replymsg) {
                        if (replymsg) resolve(replymsg);
                        else reject("No message text in reply.");
                    });
                });
            } else if (msg.chat.type === 'group') {
                return new Promise(function(resolve, reject) {
                    eventEmitter.once('reply_rec_' + msg.chat.id + '_' + msg.message_id, function(replymsg) {
                        if (replymsg) resolve(replymsg);
                        else reject("No message text in reply.");
                    });
                });
            }
        }, function(err) {
            // send message failed
            console.log(err);
        });
}

module.exports = TelegramBotPlus;

You can then use TelegramBotPlus instead of TelegramBot to instantiate your bot interface and use sendUserQuery (options must have the reply_to_message_id).

To achieve a "conversation" between the user and the bot (I prompt the user for various input values that depend on each other) I just send a sendUserQuery in response to the first command and then functions that each return Promises via another call to sendUserQuery.

@githugger :+1:

Regarding with force_reply is working as expected:

var opts = {
  reply_markup: JSON.stringify(
    {
      force_reply: true,
      keyboard: [['Over 18'],['Under 18']]
    }
  )};
bot.sendMessage(USER, 'How old are you?', opts)

I added the bot.onReplyToMessage function which makes much easier to handle responses. See an example here.

Keep in mind that its better to handle the replies with a database. If bot stops the callback will be lost.

There are two problems with this sample:

  • this doesnt work as soon as you add a keyboard. this a big issue for me, for ex it`s a good idea to use smth like this for the bot tutor or startup
  • onReplyToMessage is called after (dubbling) the main msg handler (that we usually have) is called. and I think it as soon as there is a reply handler no other handlers should be called.

Well i'm trying to use this example for force_reply but it doesn't force like Telegram "PollBot" actually. Keyboards are not working with force_reply option and you should manually double tap on main message and then type your reply.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Kasra-S picture Kasra-S  路  3Comments

Lemmmy picture Lemmmy  路  3Comments

jacopocappelli1989 picture jacopocappelli1989  路  4Comments

alikhil picture alikhil  路  3Comments

Dohoon-Kim picture Dohoon-Kim  路  3Comments