Looping over Botkit startPrivateConversation method for many users

269 Views Asked by At

I'm trying to send an update to all my users in a separate js file, so in order to do this properly I needed to create a new bot controller. I'm currently attempting to call a notification function inside my controller.on rtm_open function. I am then trying to iterate through my controller storage of users and send each of them a message in their DM's. This is the code I am using for this approach.

controller.on('rtm_open',function(bot) {
console.log('** The RTM api just connected!');
controller.storage.users.all(function(err, users) {
  if (err) {
    throw new Error(err);
    console.log('Error hit in user storage')
  })
 updateInstallers(bot, users)
}


function updateInstallers(bot, users) {
console.log('Hello World', users)
var msg = 'Welcome to...., here is a list of our new features that you can try out with a reinstallation...'
for (var u in users) {
  var installer = users[u].id
  console.log('checking installer', installer)
  bot.startPrivateConversation({user: installer}, function(err, convo) {     
   console.log('Why is installer the SAME?', installer)
   if (err) {
     console.log(err)
   } else {
     console.log('Bot should say something', installer)
     convo.say(msg)
    }
  })
 }
}

After playing around and trying to start my loop in different ways the end result is always this for an array of 2 or more users. The last/final user object in the array is always the only user that goes through the bot.startPrivateConversation function properly.

checking installer U0T1AL5CN
checking installer UCSPVKE0H
Why is installer the SAME? UCSPVKE0H
Bot should say something UCSPVKE0H
Why is installer the SAME? UCSPVKE0H
user_not_found
Why is installer the SAME? UCSPVKE0H
Bot should say something UCSPVKE0H
Why is installer the SAME? UCSPVKE0H
user_not_found

Any idea on how to fix/resolve this? My guess is this is an async issue with the call stack, but I'm also worried that this type of loop may not work with this botkit function. If there's some code anyone can refactor this to that would work better that would be awesome. Thank you so much in advance.

1

There are 1 best solutions below

0
On

You should try the following:

for (let u in users) {
   const installer = users[u].id

Or avoid the loop and use forEach:

users.forEach(
  (user)=>{
    var installer = user.id;
    //...rest of the code
  }
)

But the best way would probably be mapping your users array to promises:

//start conversation as promise
const startPrivateConversationPromise = (installer) =>
  new Promise((resolve, reject) =>
    bot.startPrivateConversation(
      { user: installer },
      (err, convo) => (err ? reject(err) : resolve(convo)),
    ),
  );
//special value for rejected promise
const Fail = function(reason) {
  this.reason = reason;
};
const isFail = (o) => o && o.constructor === Fail;
const isNotFail = (o) => !o.isFail(o);

Promise.all(
  users.map(
    (user) =>
      startPrivateConversationPromise(user.id).catch(
        (e) => new Fail(e)//still resolve even if it rejects
      ), 
  ),
).then((results) => {
  const convos = results.filter(isNotFail); //resolved instances
  const failed = reslts.filter(isFail); //rejected instances
  convos.forEach((convo) => convo.say(message));
});