Search code examples
node.jsslackslack-api

nodejs-slackbot - downloaded file is corrupted


i have created a slackbot with nodejs using @slack/bolt. i'm able to receive event in my nodejs server from slack whenever i tag with my slackapp and send textMessage or textMessage+Fileattachment. Please find the nodejs source code and output of the event object below.

The problem is, when i upload a file in slack, the event is received by my nodejs slack-bot. (shown in below screenshot+view the event object details in the output attached below)

  • The links to the uploaded file is retrieved by the nodejs slack-bot from the event object
  • i'm able to use the link in web browser(for eg. google chrome) to view the uploaded file.
  • When i use the link to download the file via nodejs code, the file gets downloaded, but the downloaded file is corrupted

i'm not sure how to download the valid file using nodejs slack-bot. i have tried changing few parameters and settings but no success. The docs aren't helping me so far

Please help me out

enter image description here

Nodejs source code

const { App : BoltApp } = require('@slack/bolt');
const boltAppObj = new BoltApp({
    token: constants.BOT_TOKEN, 
    appToken: constants.SLACK_APP_TOKEN,
    socketMode: true,
    scopes:["files:read","files:write"]
});
  
(async () => {
    await boltAppObj.start();
    console.log('⚡️ Bolt app started');


    boltAppObj.event('app_mention', async ({ event, context, client, say }) => {
        console.log("in app_mention")
        console.log("event ",event)
        try {
          await say({"blocks": [
            {
              "type": "section",
              "text": {
                "type": "mrkdwn",
                "text": `Thanks for the mention <@${event.user}>!`
              } 
            }
          ]});
        }
        catch (error) {
          console.error(error);
        }
      });
})();


Output

event  {
  type: 'app_mention',
  text: '<@U01NJR8JNDQ> good morning have a nice day',
  files: [
    {
      id: 'F01P0V0SC9X',
      created: 1613713912,
      timestamp: 1613713912,
      name: 'hiddencloudvillage.png',
      title: 'hiddencloudvillage.png',
      mimetype: 'image/png',
      filetype: 'png',
      pretty_type: 'PNG',
      user: 'U01NJPG7RGS',
      editable: false,
      size: 18207,
      mode: 'hosted',
      is_external: false,
      external_type: '',
      is_public: true,
      public_url_shared: false,
      display_as_bot: false,
      username: '',
      url_private: 'https://files.slack.com/files-pri/T01NC0NH1KQ-F01P0V0SC9X/hiddencloudvillage.png',
      url_private_download: 'https://files.slack.com/files-pri/T01NC0NH1KQ-F01P0V0SC9X/download/hiddencloudvillage.png',
      thumb_64: 'https://files.slack.com/files-tmb/T01NC0NH1KQ-F01P0V0SC9X-9f0babf31c/hiddencloudvillage_64.png',
      thumb_80: 'https://files.slack.com/files-tmb/T01NC0NH1KQ-F01P0V0SC9X-9f0babf31c/hiddencloudvillage_80.png',
      thumb_360: 'https://files.slack.com/files-tmb/T01NC0NH1KQ-F01P0V0SC9X-9f0babf31c/hiddencloudvillage_360.png',
      thumb_360_w: 360,
      thumb_360_h: 360,
      thumb_480: 'https://files.slack.com/files-tmb/T01NC0NH1KQ-F01P0V0SC9X-9f0babf31c/hiddencloudvillage_480.png',
      thumb_480_w: 480,
      thumb_480_h: 480,
      thumb_160: 'https://files.slack.com/files-tmb/T01NC0NH1KQ-F01P0V0SC9X-9f0babf31c/hiddencloudvillage_160.png',
      original_w: 640,
      original_h: 640,
      thumb_tiny: 'AwAwADDToqtPfRRP5YDO/ooqP+0VU/vIZUHqRQBdoqCW7iiiV87g33Qveof7Q/6d5fyoAu0VS/tD/p3l/KpIL2OaTyyrI/YMOtAEb208U7y2zr85yysKRkvplKOYkU8HHNXqz9r3lzMplZEjOAq0AJLbtatbvGpkWPII7/Wp/t8X2fzcN97btxzmmf2ee1zN+dO+wR/Z/K3Nndu3d80AN/tFf+eEv5UwM95dROsTIkZyWYdafZvItxLbvIZAnIY9au0AFVprJJZPMVmjc9Sp61ZooApf2ef+fmb86P7PP/PzN+dXaKAIbe2S3UhMknqx6mpqKKAP/9k=',
      permalink: 'https://pov-vtapbot.slack.com/files/U01NJPG7RGS/F01P0V0SC9X/hiddencloudvillage.png',
      permalink_public: 'https://slack-files.com/T01NC0NH1KQ-F01P0V0SC9X-df55a3dc04',
      is_starred: false,
      has_rich_preview: false
    }
  ],
  upload: false,
  blocks: [ { type: 'rich_text', block_id: 'ZecUF', elements: [Array] } ],
  user: 'U01NJPG7RGS',
  display_as_bot: false,
  ts: '1613713934.000900',
  channel: 'C01NXCJ2EN5',
  event_ts: '1613713934.000900'
}

Script to download file using the url available in events object

const axios=require("axios");
const fs = require("fs")
const constants = require("./constants");

let url="https://files.slack.com/files-pri/T01NC0NH1KQ-F01N7CHRGF9/download/poc.xlsx";
const config = {
    headers: { Authorization: `Bearer ${constants.SLACK_APP_TOKEN}` }
};
axios({
    method: "GET",
    url  : url,
    responseType: "stream",
    headers : config.headers
}).then((result)=>{
    console.log("result ")
    result.data.pipe(fs.createWriteStream("./poc.xlsx"));

}).catch((err)=>{
    console.log("err ");
})

Solution

    • The issue was due to the type of token passed to the file download api
    • BOT_TOKEN is the appropriate token that has to be passed to the file download api
    • Issue got fixed once i passed the BOT_TOKEN to the file download api

    Below is the logic to download the file that was uploaded in slack

    const downloadFile= (url,filename)=>{
    
        let fullFilePath = "";
        const promise = new Promise((resolve,reject)=>{
            const config = {
                headers: { Authorization: `Bearer ${constants.BOT_TOKEN}` }
            };
            axios({
                method: "GET",
                url  : url,
                responseType: "stream",
                headers : config.headers
            }).then((result)=>{
                const writeStream = fs.createWriteStream(fullFilePath);
                result.data.on('data', (chunk) => {
                    writeStream.write(chunk);
                });
                result.data.on('end', function () {
                    writeStream.uncork();
                    writeStream.close();
                    writeStream.end();
                    console.log("completely ending the pipe");
                });
                writeStream.on('finish', () => {
                    resolve(fullFilePath);
                });
                // This is here incase any errors occur
                writeStream.on('error', function (err) {
                    reject(err);
                });
            }).catch((err)=>{
                reject(err);
            });
        });
        return promise;
    };
    

    Below is the sample logic for nodejs-slackbot integration

    const constants = require("./constants");
    const { App : BoltApp } = require('@slack/bolt');
    const utility = require("./utility");
    const path = require("path");
    
    const BOT_PORT = 3000;
    const boltAppObj = new BoltApp({
        token: constants.BOT_TOKEN, 
        signingSecret : constants.SLACK_SIGNING_SECRET,
    });
      
    const sendMessageToSlack= async (event,say,chatmessage)=>{
        await say({
            text: `Hey there <@${event.user}>! , ${chatmessage}`
          });
    }
    
    (async () => {
        await boltAppObj.start(BOT_PORT);
        console.log('⚡️ Bolt app started');
    
        boltAppObj.event('app_mention', async ({ event, context, client, say }) => {
            const textMessage = event.text;
            try{
                const filePath = await utility.getLocalFilePath(event.files ? event.files : null);
                sendMessageToSlack(event,say,"success");
            }
            catch(err){
                sendMessageToSlack(event,say,"failed");
            }
        })
    
    })();
    
    

    Feel free to contact me if you are facing any issue related to this topic

    Response to comment made by @ppp

    Please verify if files:read permission is present in the slackbot application which you have created on slack website

    enter image description here