Calling a Slack Web API method from a slash command app

I’m currently trying to build a little Slack app. The (Botkit based) app is a “slash command” that also needs access to the team and user info Slack web API methods.

I ran into a problem where I needed an access token that Botkit stores in its users object store. The issue is that when I need to use the access token to call the Slack web API methods, I need to find that token. I wanted to lookup that access token via the team_id that is passed in through the slash command message, but I couldn’t if the token is in the users object store, with the install user’s ID as the key.

Here is an example of what I had in the Botkit users and teams object stores after a user installed my app:

botkit:store:teams
{"id":"T1DDTABCD","createdBy":"U1DDKABCD","url":"https://someslackteam.slack.com/","name":"slack-team-name"}

botkit:store:users
{"id":"U1DDKABCD","access_token":"xoxp-49999999999-49999999999-59999999999-7e05d2266c","scopes":["identify","commands"],"team_id":"T1DDTABCD","user":"johndoe"}

When my app gets a message, it doesn’t have access to the install user’s ID, but it does have the team ID. I needed to pass in that “access_token” for the web API calls like this:

var token = ''; //Need the access token
var options = {
  user: message.user,
  token: token
}

bot.api.users.info(options, function (err, response) {
            var user = response.user;
            var username = user.name;
});

My workaround was to get the access_token from the install user during the install process and store it with the team. This is what that code looks like:

controller.setupWebserver(process.env.PORT, function(err, webserver) {

  controller.createWebhookEndpoints(controller.webserver);

  // Used for app install url/login
  controller.createOauthEndpoints(controller.webserver, function(err, req, res) {
    if (err) {
      res.status(500).send('ERROR: ' + err);
    } else {

      // Need to store the user access_token with the team for easy access later
      var teamID = req.identity.team_id;
      var userID = req.identity.user_id;
      controller.storage.users.get(userID, function(err, user) {
          var token = user.access_token;
          if (err) {
              console.error('users get err: ' + err);
          }
          controller.storage.teams.get(teamID, function (err, team) {
              if (err) {
                  console.error('teams get error: ' + err);
              }
              team.access_token = token;
              controller.storage.teams.save(team, function(err) {
                  if (err) {
                      console.error('teams save err: ' + err);
                  }
              });
          });
      });
      res.send(templates.appInstallSuccess);
    }
  });
});

That happens once during install and adds the access token to the teams record:

botkit:store:teams
{"id":"T1DDTABCD","createdBy":"U1DDKABCD","url":"https://someslackteam.slack.com/","name":"slack-team-name", "access_token":"xoxp-49999999999-49999999999-59999999999-7e05d2266c"}

botkit:store:users
{"id":"U1DDKABCD","access_token":"xoxp-49999999999-49999999999-59999999999-7e05d2266c","scopes":["identify","commands"],"team_id":"T1DDTABCD","user":"johndoe"}

I could then call the Slack web API like this:

controller.storage.teams.get(message.team_id, function(err, team) {
        var token = team.access_token;
        var options = {
            user: message.user,
            token: token
        }
        // Call users.info Slack web API to look up user info
        bot.api.users.info(options, function (err, response) {
            var user = response.user;
            var username = user.name;
});

Notice I’m calling the teams storage and passing in the message.team_id to look up the team for the user who submitted the command.

Leave a comment