2

I have been staring at this for hours and can't find a solution and that is even though by all suggestions it SHOULD be quite easy - https://learn.microsoft.com/en-us/bot-framework/nodejs/bot-builder-nodejs-proactive-messages.

I have created a simple code which will "register" the user and save their data in my cosmosDatabse on Azure. That works perfectly.

//ON "register" SAVE USER DATA AND SAY REGISTERED MESSAGE 
bot.dialog('adhocDialog', function(session, args) {

    var savedAddress = session.message.address;
    session.userData.savedAddress = savedAddress;
    //REGISTERED MESSAGE
    session.endDialog("*Congratulations! You are now registered in our network! (goldmedal)*");
})
.triggerAction({
    matches: /^register$/i
})
  • But how can I then access that specific user and send him a message if, say, a condition is met? (in fact on HTTP request)

I am fairly certain we have to write the conversation ID or user ID somewhere. The question is where?

function startProactiveDialog(address) {
    bot.beginDialog(address, "A notification!");
}
  • This is how simple I think it should be. But where do you specify the user then?
Richard Franek
  • 302
  • 3
  • 12
  • What do you meant by "I am fairly certain we have to write the conversation ID or user ID somewhere". The address object contains the conversationId as well as the UserId. If you use the begin dialog, it will begin the dialog for the user, to whom that address belongs (in that particular channel). You can also use bot.send() to send a message instead of starting a dialog. – Master Chief Mar 09 '18 at 10:44
  • By that I mean that I have to specify somehow to which of the users will get that message. I don't want to contact every stored user. Just someone specific. How can I specify if not by ID? – Richard Franek Mar 09 '18 at 11:21
  • 1
    `bot.send("this is a message", address)` will send the message to the user to which this address belongs. You already capture that. Similarly use `bot.beginDialog("dialog name", address)` to initiate a dialog for that user. No need to specify any ID, they are contained in the address object. Just pass that address object. If by any chance you are using Typescript, you will be able to see the type of the address object. Its `IAddress`, and that is what those send methods expect. – Master Chief Mar 09 '18 at 13:29
  • You are very helpful, bot.send("Message", address) is a thing I will have a great use of, the thing I don't understand (and you may find it obvious and that is why you didn't address it) is how do I specify the address. I use savedAddress = session.message.address; for every person who writes register. In the database I have files for every person that did it. But how do I specify the object of that specific person? – Richard Franek Mar 09 '18 at 17:37
  • If I would use bot.send("Message", savedAddress) which is probably wrong but I can't think of nothing else, then it doesn't know which person to text. Do you see what I am having a problem with now? – Richard Franek Mar 09 '18 at 17:38
  • Oh, and maybe your TypeScript idea was related to that, the problem is I am using the Azure environment because my bot is fully hosted on Azure. There is a browser VisualCode, but they don't provide TypeScript. – Richard Franek Mar 09 '18 at 17:42

3 Answers3

1

You've saved the address of the user inside of your database by saving it to session.userData.savedAddress. When the event triggers, perform a query to your database that checks for the users that meet two criteria.

  1. They're registered to listen for the event
  2. Their address has been saved inside of the database.

In your case, you can save a property to the session.userData object, a property that lists which events they're listening for. If you just need to send a message to the user, then you can simply use bot.loadSession(savedAddress) to ping the user.


Edit:

So instead of looking specifically by user ID, you should send a query to your CosmosDB that looks for entries that have a "listen-to" Boolean-type flag corresponding to the event.

You're not worrying about the user ID at first, you're just retrieving all entries with a query that would (broadly speaking) look like this: SELECT * FROM BotState WHERE data LIKE 'listenForEvent=1.

So to setup your session.userData so that the above theoretical query would work, you would need to modify that snippet of code in your question to something like the following:

bot.dialog('adhocDialog', function(session, args) {
    var savedAddress = session.message.address;
    session.userData.savedAddress = savedAddress;
    session.userData.listenForEvent = 1 // Our property we're going to look for.

    session.endDialog("*Congratulations! You are now registered in our network! (goldmedal)*");
})
.triggerAction({
    matches: /^register$/i
})
Steven G.
  • 1,632
  • 1
  • 11
  • 14
  • Okey, thank you, that is useful, loadSession isn't something I managed to find. But how do you load a specific address to ping the specific user? savedAddress are all addresses and in the database there is a file for each person. There are IDs etc (which you probably know), so if I want to ping a user, I need to specify which user somehow. There is a specific ID for the entire userData, then there is an ID for the conversation, it doesn't really matter which we would use I think so say to ping a conversation by conversation ID which I have in there. How would I do it using your solve? – Richard Franek Mar 09 '18 at 08:23
  • 1
    I've edited my answer, does that help clear things up? – Steven G. Mar 09 '18 at 17:39
  • Okey, so I took your advice and started trying to get this to work. The thing is, even though `listenForEvent = 1` gets added into the user's file, I still have to somehow solve how will the bot know WHO has this line in his file. I tried, in a very hard way, to get the bot to listen for files in the database. For 5 hours I was trying to get `query: 'SELECT * FROM root...` work, but it just doesn't. The basic idea everyone has is that if it is easy to save the user data, it is easy to get it. But I just haven't found a single example of a person who did that. Yet so many people say it works. – Richard Franek Mar 10 '18 at 08:44
  • Now... I am a beginner in JavaScript, but 2 professional programmers were trying to solve it with me and no luck. They didn't see the Bot Framework before, but they know pretty much about JS itself. So the question is, are we missing some feature from the Bot Framework? Do you do something easy like `bot.loadSession(savedAddress.event1)`? Thank you for your patience! – Richard Franek Mar 10 '18 at 08:48
1

Actually, the savedAddress should be an instance of IAddress, and also, the function loadSession(address: IAddress, callback: (err: Error, session: Session) => void): void; and address(adr: IAddress): Message; under Message class all require IAddress as the parameter.

So first of all, you should save the entire address json object in cosmosDB for later using.

As botbuilder for Node.js is built on Restify or Express, you can build an addition route for your user to trigger and send proactive messages. The work flow could be following:

  1. Guide user to register & Save the user's address object with the account mapping in your DB
  2. Create a Route in Restify or Expressjs for trigger the proactive message:

    server.get('/api/CustomWebApi', (req, res, next) => {
       //find the user's address in your DB as `savedAddress`
        var msg = new builder.Message().address(savedAddress);
        msg.text('Hello, this is a notification');
        bot.send(msg);
        res.send('triggered');
        next();
      }
    );
    

    or if you want to leverage loadSession

    server.get('/api/CustomWebApi', function (req, res, next) {
        bot.loadSession(savedAddress, (err, session) => {
            if (!err) {
                session.send('Hello, this is a notification')
                session.endConversation();
            }
        })
        res.send('triggered');
        next();
    });
    
Gary Liu
  • 13,758
  • 1
  • 17
  • 32
  • Thanks for the post, I almost thought people gave up on this question. When I try your code, it always says in skype "default_error" and in the channels report "Unknown activity type". I tried to formulate your idea in more ways with no success. The most simple thing I tried is `bot.send(address(savedAddress),"This is a message");`. Any problem with the syntax? EDIT: I should also add that when I don't specify the address it works without errors (only doesn't send anywhere obviously) – Richard Franek Mar 14 '18 at 08:51
  • 1
    There is no syntax as `bot.send(address(savedAddress),"This is a message");`! First of all, you can try to run https://github.com/Microsoft/BotBuilder-Samples/blob/master/Node/core-proactiveMessages/simpleSendMessage/index.js this sample to get some hints. – Gary Liu Mar 14 '18 at 09:59
  • Okey, that was just one of the attempts, first I followed your example. Now I tried to apply the structure of the link you sent and the bot doesn't work at all. No errors even. Here is a link of my code and what precisely it contains (except for most of the unrelative to the topic like dialogs) [link](https://pastebin.com/zdVKMiVF) – Richard Franek Mar 14 '18 at 11:10
  • update - was able to get a proper error this time, it has a problem with `server.post` and `server.get`. Spec. it says it cannot read property "post" of undefined. – Richard Franek Mar 14 '18 at 12:34
  • You haven't define the restify server in your code man, please look the full picture of the simple sample, especially https://github.com/Microsoft/BotBuilder-Samples/blob/master/Node/core-proactiveMessages/simpleSendMessage/index.js#L5. Or is that link your whole project or part of? – Gary Liu Mar 15 '18 at 01:15
  • When you create a Functions bot in Azure, it generates the entire bot with the code and some basic interactions as examples, this I posted is the code I inserted, but in Azure I got the entire bot, it is 217420 lines of code. Now... Your comment about restify made me think it doesn't work as well as if I would host it on my own server. When I do `var restify = require('restify');` the bot doesn't work. I have to mention that except for that the bot works excellent. The only thing I can't do is use savedAddress, so I was thinking it was a problem with how I am trying to get that to work. – Richard Franek Mar 15 '18 at 11:01
  • I haven't got that you were using Fucntion Bot, which is quite different with Web App Bot. What template you are using? – Gary Liu Mar 16 '18 at 02:14
  • I am using the Proactive bot template. I set it up because initially I thought it would have some clues, the problem is, there was just a setup which sends messages after some time AND to the already connected session. That I can do no problem myself. Meanwhile I am going to try to get your advices work with a bot installed on a linux server. – Richard Franek Mar 16 '18 at 10:12
  • Okey, so I moved further. Now my bot works with Restify, and much more things work that didn't work when it was hosted on Azure. So I will be using this one. Yet I still can't get the Bot to send the message. – Richard Franek Mar 21 '18 at 11:13
  • It is saying `"TypeError: Cannot read property 'conversation' of undefined"` [HERE](https://pastebin.com/mF5QDRkH) is my entire code. The error occurs when I run Test action. – Richard Franek Mar 21 '18 at 11:20
  • the `savedAddress` is a local variable, the second time you called `test`, the `savedAddress` is `undefined`. so, you can modify the `savedAddress` to `session.message.address` in line 83 – Gary Liu Mar 27 '18 at 01:48
0

I created a users.json file, to which I save all the users. It works the way I need it to. I guess database would be better, but I don't really have a clue where to begin with that. Database is a whole new chapter I have not encountered yet, so it doesn't make sense to work on it when the project needs are resolved.

Richard Franek
  • 302
  • 3
  • 12