line

Publish Slack messages to an Airtable base

A workflow to pull all messages from a slack channel from a specific date and add them to an airtable base.

Workflow Code

The workflow logic:

  • Retrieve the last N messages from a Slack channel
  • Retrieve the content of the Airtable table
  • Save all Slack messages not already listed in Airtable

View the Github Project.. View all of the files, fork the project and deploy to Heroku in a few clicks.

module.exports.handle = function*(channelToSource, numberOfMessages, airtableBaseId, tableName) {
  // get last 10 messages from Slack channel
  const messages = yield this.run.task("getSlackMessages", channelToSource, numberOfMessages);
  
  // store messages in an Airtable base table
  yield this.run.task("storeInAirtable", airtableBaseId, tableName, messages);
};

Workflow Tasks

//getSlackMessages
const axios = require("axios");

const { SLACK_API_KEY } = process.env;

module.exports.handle = async function(channel, nbMessages) {
  const response = await axios.get(
    `https://slack.com/api/conversations.history?token=${SLACK_API_KEY}&channel=${channel}&limit=${nbMessages}`
  );
  return response.data.messages;
};
//StoreinAirtable
const axios = require("axios");
const _ = require("lodash");

const { AIRTABLE_KEY } = process.env;

module.exports.handle = async function(baseId, tableName, messages) {
  
  // get current records in Airtable
  const currentRecords = await axios.get(
    `https://api.airtable.com/v0/${baseId}/${tableName}`,
    {
      headers: {
        Authorization: `Bearer ${AIRTABLE_KEY}`
      }
    }
  );

  // remove records already stored in Airtable
  const newRecords = messages
    .map(message => {
      return {
        fields: {
          date: new Date(message.ts * 1000),
          from: message.username ? message.username : message.user,
          message: message.text
        }
      };
    })
    .filter(message => {
      return (
        currentRecords.data.records.filter(record => {
          return record.fields.message === message.fields.message;
        }).length === 0
      );
    });

  // create chunks of 10 records because of Airtable API limitation
  const newRecordsChunk = _.chunk(newRecords, 10);

  // store in Airtable
  newRecordsChunk.forEach(chunkRecords => {
    axios.post(
      `https://api.airtable.com/v0/${baseId}/${tableName}`,
      {
        records: chunkRecords
      },
      {
        headers: {
          Authorization: `Bearer ${AIRTABLE_KEY}`
        }
      }
    );
  });
};

Dispatching the workflow from within any web application

Dispatch this workflow by using the Zenaton graphQL API:

curl --request POST \
  --url 'https://gateway.zenaton.com/graphql' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer <api Token>' \
  --data '
  {
    "query": "mutation($input: DispatchWorkflowInput!) { dispatchWorkflow(input: $input) { id } }",
    "variables": {
      "input": {
        "appId": "<app id>",
        "environment": "<environment>",
        "name": "slackToAirtableWorkflow",
	    "input": "[\"SLACK_CHANNEL\", \"30\", \"AIRTABLE_TABLE\", \"AIRTABLE_SHEET\"]"
      }
    }
  }'

Or, use the Zenaton scheduler to dispatch the workflow on a set schedule:

curl --request POST \
  --url 'https://gateway.zenaton.com/graphql' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer <api Token>' \
  --data '
  {
    "query": "mutation($input: ScheduleWorkflowInput!) { scheduleWorkflow(input: $input) { id } }",
    "variables": {
      "input": {
        "appId": "<app id>",
        "environment": "<environment>",
	    "cron": "0 2 * * *",
        "name": "slackToAirtableWorkflow",
	    "input": "[\"SLACK_CHANNEL\", \"30\", \"AIRTABLE_TABLE\", \"AIRTABLE_SHEET\"]"
      }
    }
  }'

View the Github Project.. View all of the files, fork the project and deploy to Heroku in a few clicks.