Bolt-js: Cannot post message to DM or private group

Created on 3 Jan 2020  路  15Comments  路  Source: slackapi/bolt-js

Description

When I call app.client.chat.postMessage with the channel id of a direct message or private group, I get the error channel_not_found. I'm confused because my bot is able to post in channels fine. I've read through the api for postMessage multiple times but still don't understand what my problem is. Thanks!

What type of issue is this? (place an x in one of the [ ])

  • [ ] bug
  • [ ] enhancement (feature request)
  • [x] question
  • [ ] documentation related
  • [ ] testing related
  • [ ] discussion
question

Most helpful comment

@seratch I'm just confused because I've played around with other bots where:

  1. I'm in a DM either by myself or with one other person
  2. I use the bot's slash command
  3. The bot posts a message to that DM (even though I never invited the bot to the DM).

How is the above situation possible if I never explicitly invited the bot to the direct message, but just initiated their slash command?

An example of a bot that can do this is quick-map. If I call /quick-map canada in a DM, it will post an image of a map of canada in the DM without any problem, and I never invited it to the DM beforehand.

All 15 comments

@jacklein
To post a message to a private channel, a bot user needs to be a member of the private channel.

As for DMs, it's similar. Your bot user needs to be a member of multiparty direct messages (mpim in APIs) or start a DM (im in APIs) with a user by im.open. Your bot user cannot jump into others' private conversations.

@seratch I'm just confused because I've played around with other bots where:

  1. I'm in a DM either by myself or with one other person
  2. I use the bot's slash command
  3. The bot posts a message to that DM (even though I never invited the bot to the DM).

How is the above situation possible if I never explicitly invited the bot to the direct message, but just initiated their slash command?

An example of a bot that can do this is quick-map. If I call /quick-map canada in a DM, it will post an image of a map of canada in the DM without any problem, and I never invited it to the DM beforehand.

@jacklein Ah, the app (quick-map) doesn't have a bot user and the app uses only user tokens. If a Slack app has a user token to access a DM / private channel, the app is eligible to post a message to those conversations.

@seratch If the main function of my bot is to post in the channel that the slash command is initiated from, should I consider using the user token instead of the bot token? This distinction has always confused me, and I never really understood what "bot" even means. I can of course do some more research myself, but any guidance is appreciated. Thank you!

@jacklein

Could you read the following documents?

If the main function of my bot is to post in the channel that the slash command is initiated from, should I consider using the user token instead of the bot token?

Generally speaking, using only bot tokens is suitable for most cases. Once an admin of a workspace installs the app, everyone in the workspace can use it with the same permissions and functionalities.

If your app really needs the same access a user who installed the app has, or the app may work on behalf of users, your app may use user tokens over bot tokens. In this case, the app expects every single user in the workspace to install the app. Installation here means granting user-level permissions to the app.

By the way, it seems that this question is not Bolt-specific. Is it okay to close this issue for you? Feel free to ask any other questions if you have something unclear with Bolt. 馃槃

@seratch I am running into the same issue with a new slack app when trying to post a message that is being triggered by a slash command. I am using a bot token in this case, but am getting this message:

{"code":"slack_webapi_platform_error","data":{"ok":false,"error":"channel_not_found","response_metadata":{"scopes":["chat:write","chat:write.public","links:write","commands","groups:write","im:write","mpim:write","mpim:read"],"acceptedScopes":["chat:write"]}}}

which seems cryptic because as we can see, proper scope is already there. IF i try to use a user token, it will post in the DM, but only as the user who installed the app which isnt the desired results.

Long story short, what can I use so that a bot can post a message in a DM as a user who invoked the command? The app is already installed in the workspace by the admin (me).

@securisec unfortunately it seems that this action isn't possible anymore with the new apps. with a legacy slack app this was possible, but as of a few months ago you have to use version 2 of slack's oauth. it's sort of unbelievable that the new version of slack apps leave this function out :--(

Ahh... that really sucks. @jacklein are you using a legacy token then? when i try to make a legacy token now, it tries to force me to create it for a specific user... is it still possible to create a legacy app?

Hey folks I think I can provide some help here.

In the case of slash commands, neither a bot token nor a user token are necessary. From a security point of view, a user invoking a slash command is considered an implicit temporary grant for the app to post a message back to that conversation.

So if your app just wants to send a message to the conversation where the slash command was invoked, there's no reason to try and get app.client.chat.postMessage() to work with the right token. Instead, use the respond() function, which is passed as an argument to your listener function. The respond() function have be used up to 5 times within 30 minutes after the slash command was invoked. Under the hood, it uses the response_url, which is described here: https://api.slack.com/interactivity/handling#message_responses. It's also alluded to in the Bolt for JS docs , but maybe a complete example would be more helpful:

app.command('/demo', async ({ command, ack, respond }) => {

  // Let the user know immediately that their command was received
  await ack('One moment please...');


  // Do some stuff that may take a while (possibly more than 3 seconds)
  const result = await doSomeStuff(command.text);
  await respond({
    // Replace the previous message to clean up the history
    replace_original: true,
    text: `The result is ${result}`,
  });
});

For completeness, I should probably also mention that if you can come up with a response immediately (without calling some other async operation) then you should just use that as the argument for await ack(), as I've done above with "One moment please...".

Thanks @aoberoi . This is brilliant, but I am actually not using bot for my application. Just WebClient. I started the conversation here because I couldnt find anything else that speaks about this particular implementation.

@securisec Not a problem! However you chose to handle the slash command, you should be able to find the response_url within the HTTP request body. If you send an HTTP POST request to that URL with a JSON-encoded body containing the message you want to post, it should work the same way. That's all Bolt is doing under the hood.

Thanks @aoberoi ! This is interesting, but the problem is circular. if I send a payload via Axios, then it posts as an emphemeral message from the bot, not a regular message as the user.

I'm not clear on your desired result then. Earlier you said:

IF i try to use a user token, it will post in the DM, but only as the user who installed the app which isn't the desired results.

I assumed this meant you wanted the app to respond as itself. Do you want the app to respond as the user, who presumably never installed/authorized the app? That isn't allowed by Slack, and in general is a bad practice. Impersonating a user without their permission doesn't seem like something we want to allow either (for security reasons).

You can gain a user's permission by responding to the slash command with an ephemeral message which contains a link to authorize your app (requesting a user token with the chat:write scope). If all goes well, you can provide a user experience where the user doesn't even need to retry their invocation of the slash command. You would stash the initial command invocation in some temporary storage and produce an installation URL that contains a state param in OAuth which would reference that stored value. Then at the end of the OAuth flow, you would use state to retrieve the command invocation, process it, and finally post the message using chat.postMessage and the newly issued user token.

The fact that you're trying to do something that seems sort of tricky like this feels like a sign that maybe there's another better way to do what you'd like to do using other parts of Slack and the platform. If you don't mind sharing some general information about what you're trying to accomplish, I can offer my suggestions for alternatives.

Ah, fair. I will add more context to what I am trying to achieve. What I am trying to do is what the giphy app does from the slack app store. The giphy app works in two stages. First, it will present an ephemeral message with a shuffle and send actions. This I get and is working as expected.

After that, if a user clicks on send, the giphy app posts the gif link as the user who invoked the /giphy command.

At the moment, what is happening when I use the user token is that it will post in the DM, but only as the user who installed the app in Slack in the first place, and not any other user who is using the command. So lets say, my slack username is securisec. I install the app. Now when bob and alice (two other users) try to use the command in a DM, it will post the content from securisec and not bob or alice who actually triggered the command.

Now going back to the giphy app example; they do not request any permissions when installing from the slack store, but can post messages in a DM, or anywhere else as the user who invoked the command. Now the giphy app is only being used by the securisec user, but if bob uses the giphy command in a DM with alice, then the gif that posts shows it is coming from bob themselves.

This is the part I am trying to get my head wrapped around. How is it possible that giphy can post a message anywhere as the user who invoked the command, and what permission are they actually using to do so? I do agree that from a security perspective, impersonating a user is a bad idea, but what I am trying to understand is how does a widely used app in Slack do so, and how can i get that behavior?

Hopefully this clears it up a bit.

Was this page helpful?
0 / 5 - 0 ratings