Giter VIP home page Giter VIP logo

openwhisk-slackapp's Introduction

A serverless Slack app built with IBM Cloud Functions and Slack Events API

This sample shows how to build a serverless Slack app using Slack Events API to receive events, with IBM Cloud Functions to process these events.

A previous version of this sample was using API Connect to expose the actions as HTTP endpoints. With the introduction of web actions in Cloud Functions this was no longer needed. You can browse this tag to view the code using API Connect.

Overview

Built using IBM Cloud, the app uses:

  • Cloud Functions - to implement the app bot and commands
  • Cloudant - to keep track of app installations
  • and Slack Events API.

When a user installs the app in a Slack team, or interacts with a bot user, or uses a custom command, Slack calls the app implementation. Slack will talk directly with IBM Cloud Functions.

From the perspective of the developer of the Slack app, there is no server involved, only actions in a serverless environment. Furthermore the code is not running if no user interacts with the app and if the app gets popular, it will benefit from IBM Cloud Functions scalability.

Architecture

architecture_diagram digraph G { node [fontname = "helvetica"] rankdir=LR user -> slack slack -> openwhisk openwhisk -> cloudant slack [shape=square style=filled color="%23e9a820" fontcolor=white label="%23 Slack"] openwhisk [shape=circle style=filled color="%23325c80" fontcolor=white label="Cloud Functions"] cloudant [shape=circle style=filled color="%2372c7e7" fontcolor=white label="Cloudant"] } architecture_diagram )

In this sample, we will:

  • create the Slack app in Slack,
  • prepare the Cloud Functions environment, creating the actions implementing our Slack app,
  • complete the integration by updating the endpoints in the Slack app,
  • add our Slack app to a Slack team,
  • test our Slack app.

Requirements

To deploy this app, you need:

Your own Slack team is recommended if you want to play with the integration without impacting others.

Create the Slack app

  1. Proceed to the new app creation in Slack

  2. Type a name for your app, select your Slack team.

  3. Click Create

  4. View the App Credentials

We will need the Client ID, Client Secret and Verification Token in the next steps.

At this stage, we will put the configuration of the Slack app on hold. For the next app configuration steps to work we need to have our actions up and running.

Deploy the IBM Cloud Functions

Get the code

  • Clone the app to your local environment from your terminal using the following command:

    git clone https://github.com/IBM-Cloud/openwhisk-slackapp.git
    
  • or Download and extract the source code from this archive

Create the IBM Cloud Cloudant service

  1. Open the IBM Cloud console

  2. Create a Cloudant instance named cloudant-for-slackapp.

  3. Open the Cloudant service dashboard and create a new database named registrations

  4. Select the database

  5. Create a new document.

  6. Replace the default JSON with the content of the file cloudant-designs.json

Deploy from Linux or macOS

  1. Copy the file named template.local.env into local.env

    cp template.local.env local.env
    
  2. Get the service credentials for the Cloudant service created above and set CLOUDANT_url, CLOUDANT_apikey in local.env to the corresponding value (url and apikey).

  3. Set the values for SLACK_CLIENT_ID, SLACK_CLIENT_SECRET, SLACK_VERIFICATION_TOKEN - these are the App Credentials we've seen in the previous steps.

  4. Ensure your ibmcloud fn command line interface is property configured with:

    ibmcloud fn list
    

    This shows the packages, actions, triggers and rules currently deployed in your namespace.

  5. Create the actions:

    ./deploy.sh --install
    

    If all goes well it outputs:

    Creating slackapp package
    ok: created package slackapp
    Adding app registration command
    ok: created action slackapp/slackapp-register
    Adding app event processing
    ok: created action slackapp/slackapp-event
    Adding app command processing
    ok: created action slackapp/slackapp-command
    OAuth URL:
    https://us-south.functions.cloud.ibm.com/api/v1/web/<your-namespace-id>/slackapp/slackapp-register
    Command URL:
    https://us-south.functions.cloud.ibm.com/api/v1/web/<your-namespace-id>/slackapp/slackapp-command
    Event Subscription Request URL:
    https://us-south.functions.cloud.ibm.com/api/v1/web/<your-namespace-id>/slackapp/slackapp-event
    

    Note: the script can also be used to --uninstall the Cloud Functions artifacts to --update the artifacts if you change the action code, or simply with --env to show the environment variables set in local.env.

Deploy from Windows

  1. Copy the file named template.local.cmd into local.cmd

    copy template.local.cmd local.cmd
    
  2. Get the service credentials for the Cloudant service created above and set CLOUDANT_url in local.cmd to the corresponding value (url). Make sure you take the full url including the username and password https://username:[email protected].

  3. Set the values for SLACK_CLIENT_ID, SLACK_CLIENT_SECRET, SLACK_VERIFICATION_TOKEN - these are the App Credentials we've seen in the previous steps.

  4. Ensure your ibmcloud fn command line interface is property configured with:

    ibmcloud fn list
    

    This shows the packages, actions, triggers and rules currently deployed in your namespace.

  5. Create the actions:

    deploy.cmd --install
    

    If all goes well it outputs:

    Creating slackapp package
    ok: created package slackapp
    Adding app registration command
    ok: created action slackapp/slackapp-register
    Adding app event processing
    ok: created action slackapp/slackapp-event
    Adding app command processing
    ok: created action slackapp/slackapp-command
    

    Note: the script can also be used to --uninstall the Cloud Functions artifacts to --update the artifacts if you change the action code, or simply with --env to show the environment variables set in local.cmd.

Our actions are ready. Back to the Slack app configuration.

Complete the Slack app configuration

Add an Event Subscription

  1. Go to the Event Subscriptions section of your app

  2. Enable Events

  3. Set the Request URL to the URL of the slack-event web action. The URL should look like https://us-south.functions.cloud.ibm.com/api/v1/web/your-namespace-id/slackapp/slackapp-event

    Slack will contact this URL immediately. It should turn to Verified if the Cloud Functions configuration steps worked.

Listen to Bot messages

  1. Add a new Bot User Event for message.im

    This allows us to react to direct messages sent to a bot. We could select more event type but for our simple app will only deal with this one today.

  2. Save the changes

Set the callback URL for authentication

  1. Under OAuth and Permissions, add a new Redirect URL. This URL will be called when a user installs your application in a team. It should point to the slackapp-register web action. The URL should look like https://us-south.functions.cloud.ibm.com/api/v1/web/your-namespace-id/slackapp/slackapp-register

  2. Click Save URLs

Create a new command

  1. Under Slash Commands, Create New Command

  2. Set the values

    1. Command: /myserverlessapp

    2. Request URL: URL of the slackapp-command web action. The URL should look like https://us-south.functions.cloud.ibm.com/api/v1/web/your-namespace-id/slackapp/slackapp-command

    3. Short Description: A test command

    4. Usage Hint: [param1] [param2]

  3. Save. This automatically creates a bot for the app.

Give the bot the permission to talk

  1. Go to OAuth & Permissions.
  2. Under Scopes / Bot Token Scopes, add the two OAuth Scopes: chat:write and users:read

Our app is finally ready to be installed!

Add the app to your team

  1. To see what's happening behind the scene as Slack calls our actions, open a new command prompt and run

    ibmcloud fn activation poll
    

    Leave it running. Actions triggered by Slack will show up there

  2. Go to Manage Distribution

  3. Click the Add the Slack button

  4. Authorize the app

    After a few seconds, the app is installed. You should see logs appearing in the activation polling as Slack will be sending the registration info we will use later to interact with Slack channels and users.

    In your web browser, you should see Registration was successful. You can try the command in Slack or send a direct message to the bot.

    Ideally you would redirect to another page once the registration is completed.

Test the integration

  1. Go into your team in Slack

  2. Send a direct message to our new bot friend

  3. The bot replies

  4. Go to the #general channel (although this could work from any place in Slack) and type /my you should see the custom command

  5. Try the command /myserverless two params as example

Code Structure

Cloud Functions - Deployment Script

File Description
deploy.cmd
deploy.sh
Helper script to install, uninstall, update the actions.

Cloud Functions - Actions

File Description
slackapp-register.js Handles the installation of the app in a team. It stores the authorization token in Cloudant for future use by the bot and commands.
slackapp-event.js Handles events triggered by the Events API. In this sample, it handles messages sent to the bot user and simply echoes the message sent by the user.
slackapp-command.js Handles custom commands. In this sample, it echoes the command parameters.

Contribute

Please create a pull request with your desired changes.

Troubleshooting

Cloud Functions

Polling activations is good start to debug the action execution. Run

ibmcloud fn activation poll

and send a message to the bot or use a custom command.

License

See License.txt for license information.

openwhisk-slackapp's People

Contributors

l2fprod avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

openwhisk-slackapp's Issues

Registration is getting failed

"2019-08-26T01:58:58.117281Z stderr: { Error: Cannot find module 'cloudant'",
"2019-08-26T01:58:58.117297Z stderr: at Function.Module._resolveFilename (internal/modules/cjs/loader.js:582:15)",
"2019-08-26T01:58:58.117303Z stderr: at Function.Module._load (internal/modules/cjs/loader.js:508:25)",
"2019-08-26T01:58:58.117326Z stderr: at Module.require (internal/modules/cjs/loader.js:637:17)",
"2019-08-26T01:58:58.117330Z stderr: at require (internal/modules/cjs/helpers.js:22:18)",
"2019-08-26T01:58:58.117334Z stderr: at NodeActionRunner.main [as userScriptMain] (eval at initializeActionHandler (/nodejsAction/runner.js:57:23), :59:18)",
"2019-08-26T01:58:58.117338Z stderr: at Promise (/nodejsAction/runner.js:73:35)",
"2019-08-26T01:58:58.117342Z stderr: at new Promise ()",
"2019-08-26T01:58:58.117346Z stderr: at NodeActionRunner.run (/nodejsAction/runner.js:71:16)",
"2019-08-26T01:58:58.117350Z stderr: at doRun (/nodejsAction/src/service.js:185:14)",
"2019-08-26T01:58:58.117353Z stderr: at runCode (/nodejsAction/src/service.js:141:20) code: 'MODULE_NOT_FOUND' }",
"2019-08-26T01:58:58.118Z stderr: The action did not initialize or run as expected. Log data might be missing."

error 401_client_error

Hi,
I follow the steps and deployed the app to IBM cloud, and successfully created a Slack app, but when I tried to enable Events, Slack got error http 500. Debugging this issue, I found that Bluemix requires to authenticate for a post, so Slack can not post the verification token. Is there any way to work around this issue?

wskdeploy?

Have you considered using wskdeploy for the deployment specification? Easier to do and does everything your script does...

Error when executing Slack command

Setup and registration seemed to work. When I execute the Slack command it fails with the error "http_client_error". From the "bx wsk activation poll" I see the following error:

"2020-05-30T21:45:09.880602Z stderr: { Error: Cannot find module 'cloudant'",
"2020-05-30T21:45:09.880608Z stderr: at Function.Module._resolveFilename (internal/modules/cjs/loader.js:636:15)",
"2020-05-30T21:45:09.880610Z stderr: at Function.Module._load (internal/modules/cjs/loader.js:562:25)",
"2020-05-30T21:45:09.880613Z stderr: at Module.require (internal/modules/cjs/loader.js:692:17)",
"2020-05-30T21:45:09.880615Z stderr: at require (internal/modules/cjs/helpers.js:25:18)",
"2020-05-30T21:45:09.880618Z stderr: at NodeActionRunner.main [as userScriptMain] (eval at initializeActionHandler (/nodejsAction/runner.js:57:23), :59:18)",
"2020-05-30T21:45:09.880621Z stderr: at Promise (/nodejsAction/runner.js:73:35)",
"2020-05-30T21:45:09.880623Z stderr: at new Promise ()",
"2020-05-30T21:45:09.880626Z stderr: at NodeActionRunner.run (/nodejsAction/runner.js:71:16)",
"2020-05-30T21:45:09.880629Z stderr: at doRun (/nodejsAction/src/service.js:196:14)",
"2020-05-30T21:45:09.880636Z stderr: at runCode (/nodejsAction/src/service.js:141:20) code: 'MODULE_NOT_FOUND' }",
"2020-05-30T21:45:09.882Z stderr: The action did not initialize or run as expected. Log data might be missing."

Did I do something wrong during setup?

Invalid_code error when trying to register through Slack Button

I keep getting the message "Registration failed" when I click the "Add to Slack" button. Checking the activation poll, I see the message "ok: false, error: 'invalid_code'." But for the life of me, I can't figure out what the problem is with the code returned by the url. The code in the activation poll works in the oauth.access tester on the Slack API documentation site, so I can't figure it out. Can you review the code and see what's going on?

So far, I haven't been able to get the chatbot working in my workspace. I get no response when I attempt direct messages, and using the slash command only returns "Team not found."

Thanks for any help you can provide!

Use integrated APIs experience in Functions

Instead of creating a new API Connect service, the steps could potentially be simplified by using the built-in APIs capability. Or remove API altogether and just use the HTTP exposing capability of functions

Slack authorisation failure

In step 8 myserverlessapp would like to access MyTeam but encounters a failure as follows:

<errorResponse>
<httpCode>500</httpCode>
<httpMessage>Internal Server Error</httpMessage>
<moreInformation>
{ "errorMessage":"Cannot read property 'success' of undefined", "errorCode":"0x8580005c", "errorDescription":"GatewayScript console log message.", "errorSuggestion":"GatewayScript console log message. Refer to the message for more information." }
</moreInformation>
</errorResponse>

I've checked and double checked all the steps and all the OpenWhisk and Slack credentials have been properly substituted into the gateway properties of openwhisk-slackapp-api.yaml => source of openwhisk-slackapp-api 1.0.0

There is a slight difference in the UI at https://misfitstheatre.slack.com/oauth/... in that I am being asked to select a channel (in the correct team) to post to.

Events API registration doesn't work.

When registering the events API with slack we got an error. The http code was 200 but did not return what slack expected. Upon further debugging it looks like the problem was due to "Team not found" . I verified the Cloudant url wassetup correctly. Only the design document was in the database before and after the execution.

Activation failed with error description Error team not found

Following the steps, I am able to deploy a slack app, and there is a document in the database registrations->_design/bots as
{
"_id": "_design/bots",
"_rev": "3-55382d5d47149be82319e8c7730a1d2d",
"views": {
"by_team_id": {
"map": "function (doc) {\n if (doc.type == "bot-registration") {\n emit(doc._id, 1);\n }\n}"
}
},
"language": "javascript"
}

But it always return "Error team not found"
Is there anything in the registration database that I should change?

Here is the log

Activation: 'slackapp-event' (17c8dad1151845a588dad1151885a5fd)
[
"2019-02-26T22:41:55.43472832Z stdout: Processing new bot event from Slack { __ow_headers:",
"2019-02-26T22:41:55.434810537Z stdout: { accept: '/',",
"2019-02-26T22:41:55.434817478Z stdout: 'accept-encoding': 'gzip',",
"2019-02-26T22:41:55.43482215Z stdout: 'cdn-loop': 'cloudflare',",
"2019-02-26T22:41:55.434826601Z stdout: 'cf-connecting-ip': '3.89.65.30',",
"2019-02-26T22:41:55.434831127Z stdout: 'cf-ipcountry': 'US',",
"2019-02-26T22:41:55.434835414Z stdout: 'cf-ray': '4af5ebff38849f3c-IAD',",
"2019-02-26T22:41:55.434839717Z stdout: 'cf-visitor': '{"scheme":"https"}',",
"2019-02-26T22:41:55.434844414Z stdout: 'content-type': 'application/json',",
"2019-02-26T22:41:55.434848786Z stdout: host: 'openwhisk.ng.bluemix.net',",
"2019-02-26T22:41:55.434853986Z stdout: 'user-agent': 'Slackbot 1.0 (+https://api.slack.com/robots)',",
"2019-02-26T22:41:55.434858483Z stdout: 'x-forwarded-for': '3.89.65.30, 172.68.65.86',",
"2019-02-26T22:41:55.434862794Z stdout: 'x-forwarded-host': 'openwhisk.ng.bluemix.net',",
"2019-02-26T22:41:55.434867232Z stdout: 'x-forwarded-port': '443',",
"2019-02-26T22:41:55.434871508Z stdout: 'x-forwarded-proto': 'https',",
"2019-02-26T22:41:55.434875877Z stdout: 'x-global-k8fdic-transaction-id': '3584c7454327364077d9050ad2535111',",
"2019-02-26T22:41:55.434884933Z stdout: 'x-real-ip': '172.68.65.86',",
"2019-02-26T22:41:55.434889363Z stdout: 'x-request-id': '3584c7454327364077d9050ad2535111',",
"2019-02-26T22:41:55.434893757Z stdout: 'x-slack-request-timestamp': '1551220915',",
"2019-02-26T22:41:55.434898151Z stdout: 'x-slack-signature': 'v0=2341af05c2e342e66a3783ac58f8bbb83be53b8a4d9d332b9376b45d8fe1f7bf' },",
"2019-02-26T22:41:55.434903006Z stdout: __ow_method: 'post',",
"2019-02-26T22:41:55.434907307Z stdout: __ow_path: '',",
"2019-02-26T22:41:55.434928786Z stdout: api_app_id: 'ACC4UMYG1',",
"2019-02-26T22:41:55.434933929Z stdout: authed_teams: [ 'T08LVDR7Y' ],",
"2019-02-26T22:41:55.434938302Z stdout: authed_users: [ 'WCHTQEZND' ],",
"2019-02-26T22:41:55.43494266Z stdout: cloudantDb: 'registrations',",
"2019-02-26T22:41:55.434947235Z stdout: cloudantUrl: 'https://86107998-b2e1-45cc-b98d-42f20cdcdfec-bluemix:5b11aa19dacdf145dc96316765e0fb756b3bf42e3ced0faa2d05d991a1c5109e@86107998-b2e1-45cc-b98d-42f20cdcdfec-bluemix.cloudantnosqldb.appdomain.cloud',",
"2019-02-26T22:41:55.434952094Z stdout: enterprise_id: 'E27SFGS2W',",
"2019-02-26T22:41:55.434956374Z stdout: event:",
"2019-02-26T22:41:55.434960696Z stdout: { channel: 'DCJ8YCUJY',",
"2019-02-26T22:41:55.434964921Z stdout: channel_type: 'im',",
"2019-02-26T22:41:55.434969274Z stdout: client_msg_id: '60e0566e-d407-46cb-b6be-2cde42d1285c',",
"2019-02-26T22:41:55.434973727Z stdout: event_ts: '1551220914.001700',",
"2019-02-26T22:41:55.434978178Z stdout: team: 'T08LVDR7Y',",
"2019-02-26T22:41:55.434982631Z stdout: text: 'hello_direct v11',",
"2019-02-26T22:41:55.43498697Z stdout: ts: '1551220914.001700',",
"2019-02-26T22:41:55.434991191Z stdout: type: 'message',",
"2019-02-26T22:41:55.435000115Z stdout: user: 'W4V5HF57Y' },",
"2019-02-26T22:41:55.435004595Z stdout: event_id: 'EvGJ248C3F',",
"2019-02-26T22:41:55.435008831Z stdout: event_time: 1551220914,",
"2019-02-26T22:41:55.435013202Z stdout: slackClientId: 8709467270.420164,",
"2019-02-26T22:41:55.435017578Z stdout: slackClientSecret: '6d696fa649fcbef2774821133b5b4829',",
"2019-02-26T22:41:55.435022043Z stdout: slackVerificationToken: 'QDfaWXhVbrgWvELHf1qAVzWx',",
"2019-02-26T22:41:55.435026441Z stdout: team_id: 'T08LVDR7Y',",
"2019-02-26T22:41:55.435030828Z stdout: token: 'QDfaWXhVbrgWvELHf1qAVzWx',",
"2019-02-26T22:41:55.435035201Z stdout: type: 'event_callback' }",
"2019-02-26T22:41:55.57468391Z stdout: Looking up bot info for team T08LVDR7Y",
"2019-02-26T22:41:55.65639819Z stdout: Error team not found"
]

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.