Lex is AWS’s service for building conversational interfaces into any application using voice or text. This allows for easy integration into other AWS services, and Lex has native integrations with Facebook, Kik, Slack or Twilio messaging platforms. You can also integrate with Lex directly via it’s API. In this post, I am going to show how to integrate Botkit with AWS Lex, AWS Lambda, and Botkit on its own. This will allow for complete programmatic access to everything passing between Slack and Lex, and not be limited by Lex’s timeouts, and you can directly communicate to Slack without Lex filtering information. For this post I am focusing on the interactions between Lex, and Botkit, and where the actions from the chat requests are processed, we will cover Botkit setup at another time.

Initially, I will provide everything to setup Botkit on an ECS cluster using CodePipeline for a Continuous Deployment Pipeline using Github as a source repository.  You will just have to clone a Github repository and run a CloudFormation template via the AWS CLI or AWS Console. Then integrate that with Slack and Lex which will be described.

After you have set up the environment, the first part I want to describe is using Botkit on its own for processing incoming Slack messages, then sending the responses back to the end user via Slack (see Figure 1)

Figure 1

As part of the same setup, based on a different incoming message from Slack, I will show how you can have Botkit use AWS’s Lex Natural Language Processing engine to recognize the incoming message, then have Botkit generate the response and send that response back to the end user via Slack (see Figure 2).

Figure 2

After that, as part of the same set up based on a different incoming message from Slack, I will show how we can have Botkit use AWS’ Lex Natural Language Processing engine to recognize the incoming message. From there, you can have Lex call to Lambda to generate the response that will send a response back to Botkit which will then forwarded back to the end user via Slack (see Figure 3).

Figure 3

Let’s get familiar with few basic terms related to AWS’ Lex Service before we get started with the set up. An intent represents an action that fulfills a user’s request via chat. An example intent might be startServer to start an EC2 Instance. A ‘Slot’ represents a list of values used by the Intent. A slot instance of a custom slot type called INSTANCE_NAME might take values dev, qa, prod, or uat. An ‘Utterance’ is structured text that links the intent to the likely words/sentences types by the user. An example utterance might be “I want to start a {INSTANCE_NAME} server.” So, when a user says, “I want to start a dev server,” Lex will understand that utterance with a specific “slot” that matches the utterance from the startServer intent.

Environment Setup Instructions

You will need to Fork this GitHub repository to your GitHub account.


To Fork the repo, go to the link above, and click Fork in the upper right-hand corner.

You will then need a Github Personal Access Token. To obtain this, you need go to your account settings, click Developer settings, then Personal access tokens, then Generate new token. Give it a description, and make sure you check the box read:repo_hook, then click generate token. Save the generated token for later.

Within your AWS account in the us-east-1 region, you will need a few things already setup:

  • A publicly available registered DNS Zone
    • Example: example.com
  • VPC ID
    • Example: vpc-1234567
  • VPC Subnet Range
    • Example:
  • 2 Subnets within the VPC above
    • Example: subnet-000001 and subnet-000002
  • An SSL/TLS Certificate within AWS’s Certificate Manager for DNS entry to be used for this Cluster(Application Load Balancer)
    • You will need the ARN of the certificate
      • Example: arn:aws:acm:us-east-1:123456789:certificate/0123456-1234-1234-1234-0123456
  • A Key Pair created or imported into EC2(Network & Security, Key Pairs)

Next you will need a Slack account and Slack App created initially to get a Slack “Client ID” & “Client Secret.”

To create a Slack account follow instructions here.

Once logged into your Slack account you will need to go to http://api.slack.com/apps and create a new application record. You will receive a Client ID and a Client Secret.

With the above information you will be able to create your AWS infrastructure following this link below.

Then click next.

Update fields that have defaults in the format of Your*Here with your data gathered above.

Click next twice, then check bow next to “I acknowledge that AWS CloudFormation might create IAM resources with custom names,” then click Create.

This will go through creating your CodePipeline pipeline. This will run initially sourcing your data from your Github repo and branch referenced as part of your CloudFormation.

At this point your CloudFormation and pipeline will build your code that will run on your ECS cluster, store that image in an ECR registry, and build all the infrastructure listed below.

  • CodePipeline
  • ECR Repository
  • IAM Roles
  • S3 Artifact Bucket
  • CodeBuild Project
  • Lambda Function
  • Security Groups
  • ECS Cluster
  • Autoscaling group
  • Application Load Balancer
  • Cloudwatch Log Groups
  • Route53 A Record
  • RDS DB Instance (Postgres)

When the CloudFormation Stack(s) above finishes successfully you will have to go and complete your Slack App Setup.  Follow this link.

  • Choose the app you created above
  • Click Interactive Components
    • In the Request URL Field enter https://botkit.<your hostedzonename>/slack/receive
    • Save Changes
  • Click OAuth & Permissions
    • In the Redirect URLs field enter https://botkit.<your hostedzonename>/oauth
    • Save URLs
    • Then select the “Add permission by scope or API method…” and choose the items below:
      • Add a bot user with the username @botkit
      • Access user’s public channels
      • Modify your public channels
      • Send messages as Botkit
      • Send messages as user
      • Modify a user’s Do Not Disturb settings
      • Access content in user’s private channels
      • Modify your private channels
      • Access content in user’s direct messages
      • Modify user’s direct messages
      • Access user’s group messages
      • Make changes user’s group messages
    • Save Changes
  • Click Event Subscriptions
    • In the Request URL field enter https://botkit.<your hostedzonename>/slack/receive
    • Then click Add Workspace Events then choose items below:
    • Save Changes
  • Click Bot Users
    • Click box for “Always Show My Bot as Online”
    • Save Changes
  • Click Basic Information
    • Choose “Install your app to your workspace”
      • Complete Installation
    • Choose “Manage distribution”
      • Distribute App
        • You want to enable distribution of your app to allow it to be installed, but Do not choose to add app to Slack Marketplace.

At this point you have your AWS ECS Cluster setup and accessible from the outside world, and you have your Slack App Setup. You should now be able to follow this link to test your slack app https://botkit.<your hostedzonename>/login.

You should be taken to an Authorize page, click Authorize and the bot should send you a Slack message saying “I am a bot that has just joined your team. You must now /invite me to a channel so that I can be of use!”

Next, we need to go to your AWS Console to setup Lex. From the list of Services in your AWS Console choose “Amazon Lex.”

  • Then you want to Create a Bot
  • Name the bot, botkit
  • Add an Intent (Intent is an individual piece of functionality
  • Your first intent we will name BookHotel (see below).

  • The utterances you can see in the picture above.
  • Make sure for Lambda initialization and validation to choose the “Bot” Lambda that was created from the CloudFormation.
  • For Slots, they should include:
    • Location
      • Slot Type = AMAZON.US_CITY
      • Prompt = What city will you be staying in?
    • CheckInDate
      • Slot Type = AMAZON.DATE
      • Prompt = What day do you want to check in?
    • Nights
      • Slot Type = AMAZON.NUMBER
      • Prompt = How many nights will you be staying?
    • RoomType
      • Click + under Slot types on Left (see below)

  • For Fulfillment, choose AWS Lambda function:
    • choose the “Bot” Lambda that was created from the CloudFormation.
  • Next choose to add another Intent named BotIdentification:
    • For this we will only be creating Utterances each is listed below.
      • who are you
      • identify yourself
      • what is your name
      • uptime
    • The last Intent we are adding we will name convHelp:
      • This will use a built-in Intent, when adding the intent search for and choose the AMAZON.HelpIntent
      • There is nothing else to setup, we only want this intent to match using its built-in help utterances.
    • Next go to the “Settings” tab.
      • Create an Aliases named beta.
    • Choose Build
    • Choose Publish
      • Choose the beta alias
    • At this point you can click “Test Chatbot” on right of screen.
      • This allows you to test your chatbot that is setup in Lex.
      • The only “Intent” that is fully functioning within Lex/Lambda is BookHotel.
        • You can test the BookHotel Intent by typing “Book a hotel” where it says, “Chat to your bot …”, it should chat back and forth asking for follow up information to book your hotel room.

At this point all of your infrastructure is setup on AWS and Slack.

Respository Files

Let’s go over some of the main files in your forked repository.

The GitHub repo you forked has the files listed below.

bot.js is the main bot file that is run. Within that file the skills directory is scanned, and all files in that directory are read. We currently only have lex.js. Within that file there are three main parts starting with controller.hears([regex1, regex2]).

The first one is attempting to match specific regex’s that are looking for hello or hi and replies with Hello or Hello <your name> if the system knows your name. The second one is attempting to match a phrase that contains ‘what is my name’ or ‘who am i’ when something contains either of those. The system has previously stored your name and it will reply with ‘Your name is <your name>.’  If it doesn’t already have your name saved, it will request your name and store it in the database for future use.

The third part matches on .* which is match anything that isn’t previously matched. From here, it takes everything and sends it to AWS Lex to be processed. Lex will take what is sent and run its Natural Language Processing (NLP) engine against it, and if Lex is set up to use Lambda to process the message, the messages will be processed then sent back to Botkit. Botkit will forward responses back and forth as part of that conversation. If Lex is not configured to use Lambda, it may only run it’s NLP based on Lex Utterances setup and send back what the intent is that Lex thinks the message matches.

There are three Intents setup in Lex.

  • BotIdentification
    • This is an Intent in Lex that is intended to only use Lex’s NLP functionality, and only has sample Utterances to match when the end user is trying to find the identity of the bot.
  • convHelp
    • This intent uses a built in Intent of Lex’s that has many utterances that have to do with trying to get help from the chat bot. Then once Lex replies with this intent it will run the intents/intentConvHelp.js file and help the end user informing them with categories and sample things that the bot will be able to accomplish.
  • BookHotel
    • This intent is not configured in Botkit at all, the messages are passed to Lex via the intents/intentDefault.js file, and Botkit is basically a transparent pass thru for this conversation. All processing is done in Lex and Lambda.

For an example that only uses Botkit, if Botkit receives (‘hello,’ ‘hi’) or (‘what is my name,’ ‘who am I’), botkit will not use any part of Lex. The Botkit cluster running bot.js reading the lex.js file in the skills directory, then matching on the message including (‘hello,’ ‘hi’) or (‘what is my name,’ ‘who am I’), then the appropriate actions are taken within those functions (see Figure 4).

Figure 4

For an example that uses Botkit and Lex for its NLP, we are going to try to match the BotIdentification intent by sending a message like ‘who are you’ or ‘what is your name’.’ This will be received by our Botkit cluster that is running bot.js, which is reading the lex.js file in the skills directory, then not matching any of the specific controller.hears regular expressions. It will fall through to the controller.hears([‘.*’] function and send the data to Lex, it will run its NLP for that message and reply with the intent it matched. With this example it will be the BotIdentification intent, so the lex.js script will call the intents/intentBotIdentification.js script, which will run this file below.

As you can see, this replies with a simple “I am a botnamed<@” which the bots identify as received from slack, and how long the bot has been up and running on that node, just as an example of calling some other function.

The convHelp intent is very similar to the BotIdentification intent in that it uses Botkit and Lex, but only for the NLP portion of Lex, and convHelp uses a builtin intent of Lex. This allows for a massive amount of predefined and learned utterances related to “needing help” are matched. So Botkit can send a message to Lex and Lex will reply with convHelp intent if the message is related to needed help, and Botkit will act according to the intents/intentConvHelp.js script.

convHelp – partial

The code above from convHelp shows an example of using Botkit for processing, and Lex for NLP only (see Figure 5).

Figure 5

The last example shows how to use Botkit, Lex for NLP and Lex to call Lambda for Lambda to process the messages and fulfillment of the intent. This is the BookHotel example. To show this, you can send a message to the slack bot of ‘book a hotel.’ This will be received by our Botkit cluster that is running bot.js which is reading the lex.js file in the skills directory. When not matching any of the specific controller.hears regular expressions, it will fall through to the controller.hears([‘.*’] function and match the default in the case statement, which tells Botkit to transparently forward messages back and forth between Slack and Lex for this conversation. Lex receives these messages and matches the BookHotel intent, which uses the Bot Lambda for Validation and Fulfillment. Since all the slots don’t have known information, the Lambda function will respond with questions asking about the empty slots, once they all have appropriate information. The Lambda function will run the fulfillment portion, which in this case is really nothing, prompting a message saying “Your document is complete,” but that could be any action (see Figure 6).

Figure 6

In summary, with these examples you now have a significant amount of flexibility. You can do a little processing, most processing or no processing in Botkit, other than transparently forwarding back and forth to Lex/Lambda. The ECS Cluster that is running Botkit can be autoscaled significantly to handle load. Lambda and Lex are managed services that scale automatically to load.

Scale is handled well, and any access to Slack messages that were hidden if you only used Lex/Lambda are now accessible to Botkit, any timeouts that you don’t have access to adjust can be worked around, by processing with Botkit on your ECS cluster. This still allows you to use Lex/Lambda processing for different intents that make sense to run there. There are definitely more simple ways to get a basic chatbot working, but given the OAuth functionality, the scale provided, and flexibility of processing available with this Architecture, this is an example of an environment that can grow into a significantly large infrastructure with minor changes as that growth is needed, with still keeping the overall Architecture rather simple.

Content Provided:

botkit-middleware-lex = https://github.com/jonchurch/botkit-middleware-lex

basic chatbot, bot.js(based on), components and public directories were all from  https://github.com/howdyai/botkit-starter-slack


Gregory Cox is a CTO Architect at Sungard Availability Services who specializes in network architecture with a concentration in data centers, service providers, enterprise architecture, Cloud and infrastructure automation. In his 18 years in the industry, he has also held Network Architect and Technical Fellow roles at Interactive Data (ICE, NYSE), Acxiom, Comcast (AT&T Broadband).


There are no comments.