Slack Node



Coding a Slack Bot using Bolt.Js. Now that we've created our bot, we can code its behaviors. We’re going to use Bolt.JS (the official framework to build Slack Apps) and Node.Js. To begin with, clone my project from Glitch. Glitch allows us to code our bot and make its code live. A Slack group for ASP.NET, C#, F#,.NET, Visual Basic, and other Microsoft technology developers. Node.js — Relevance: High / Flow: Low (very low for non-dev channels) / Responsive — 1,300+ A Node.js and JavaScript development community. The dev channel is the only relatively active channel. Slack bots — they’re fun, they’re useful, and people are building new businesses around bot interaction models on a daily basis. To build it we’ll be using Autocode, the Node.js.

The @slack/web-api package contains a simple, convenient, and configurable HTTP client for making requests to Slack’sWeb API. Use it in your app to call any of the over 130methods, and let it handle formatting, queuing, retrying, pagination, and more.

Installation

Initialize the client

The package exports a WebClient class. All you need to do is instantiate it, and you’re ready to go. You’ll typicallyinitialize it with a token, so that you don’t have to provide the token each time you call a method. A token usuallybegins with xoxb or xoxp. You get them from each workspace an app is installed onto. The app configuration pageshelp you get your first token for your development workspace.

Initializing without a token

Alternatively, you can create a client without an token, and use it with multiple workspaces as long as you supply atoken when you call a method.

Call a method

The client instance has a named method for each of the public methods in the Web API. The most popular one iscalled chat.postMessage, and its used to send a message to a conversation. For every method, you pass arguments asproperties of an options object. This helps with the readablility of your code since every argument has a name. Allnamed methods return a Promise which resolves with the response data, or rejects with an error.

Hint: If you’re using an editor that supports TypeScript, even if you’re not using TypeScript to write your code,you’ll get hints for all the arguments each method supports. This helps you save time by reducing the number oftimes you need to pop out to a webpage to check the reference. There’s more information about usingTypeScript with this package in the documentation website.

Note: Use the Block Kit Builder for a playgroundwhere you can prototype your message’s look and feel.

Using a dynamic method name

If you want to provide the method name as a string, so that you can decide which method to call dynamically, or to calla method that might not be available in your version of the client, use the WebClient.apiCall(methodName, [options])method. The API method call above can also be written as follows:

Handle errors

Errors can happen for many reasons: maybe the token doesn’t have the proper scopes tocall a method, maybe its been revoked by a user, or maybe you just used a bad argument. In these cases, the returnedPromise will reject with an Error. You should catch the error and use the information it contains to decide how yourapp can proceed.

Each error contains a code property, which you can check against the ErrorCode export to understand the kind oferror you’re dealing with. For example, when Slack responds to your app with an error, that is anErrorCode.PlatformError. These types of errors provide Slack’s response body as the data property.

Slack node diagramMore error types

There are a few more types of errors that you might encounter, each with one of these codes:

  • ErrorCode.RequestError: A request could not be sent. A common reason for this is that your network connection isnot available, or api.slack.com could not be reached. This error has an original property with more details.

  • ErrorCode.RateLimitedError: The Web API cannot fulfill the API method call because your app has made too manyrequests too quickly. This error has a retryAfter property with the number of seconds you should wait before tryingagain. See the documentation on rate limit handling tounderstand how the client will automatically deal with these problems for you.

  • ErrorCode.HTTPError: The HTTP response contained an unfamiliar status code. The Web API only responds with 200(yes, even for errors) or 429 (rate limiting). If you receive this error, its likely due to a problem with a proxy,a custom TLS configuration, or a custom API URL. This error has the statusCode, statusMessage, headers, andbody properties containing more details.

Pagination

Many of the Web API’s methods returnlists of objects, and are known to be cursor-paginated. The result of calling these methods will contain a part ofthe list, or a page, and also provide you with information on how to continue to the next page on a subsequent API call.Instead of calling many times manually, the WebClient can manage getting each page, allowing you to determine when tostop, and help you process the results.

The process of retrieving multiple pages from Slack’s API can be described as asynchronous iteration, which meansyou’re processing items in a collection, but getting each item is an asynchronous operation. Fortunately, JavaScripthas this concept built in, and in newer versions of the language there’s syntax to make it even simpler:for await...of.

Slack node login

The for await...of syntax is available in Node v10.0.0 and above. If you’re using an older version of Node, seefunctional iteration below.

Using functional iteration

The .paginate() method can accept up to two additional parameters. The third parameter, stopFn, is a function thatis called once for each page of the result, and should return true when the app no longer needs to get another page.The fourth parameter is reducerFn, which is a function that gets called once for each page of the result, but canbe used to aggregate a result. The value it returns is used to call it the next time as the accumulator. The firsttime it gets called, the accumulator is undefined.

The returned value is a Promise, but what it resolves to depends on whether or not you include the fourth (optional)parameter. If you don’t include it, the resolved value is always undefined. In this case, its used for control flowpurposes (resuming the rest of your program), and the function in the third parameter is used to capture a result. Ifyou do include the fourth parameter, then the resolved value is the value of the accumulator. This is a familiarpattern for people that use functional programming.

Opening modals

Modals can be created by calling the views.open method. The method requires you to pass a valid view payload in addition to a trigger_id, which can be obtained when a user invokes your app using a slash command, clicking a button, or using another interactive action.

Dynamically updating a modal

After the modal is opened, you can update it dynamically by calling views.update with the view ID returned in the views.open result.

Logging

The WebClient will log interesting information to the console by default. You can use the logLevel to decide howmuch information, or how interesting the information needs to be, in order for it to be output. There are a few possiblelog levels, which you can find in the LogLevel export. By default, the value is set to LogLevel.INFO. While you’rein development, its sometimes helpful to set this to the most verbose: LogLevel.DEBUG.

All the log levels, in order of most to least information are: DEBUG, INFO, WARN, and ERROR.

Sending log output somewhere besides the console

You can also choose to have logs sent to a custom logger using the logger option. A custom logger needs to implementspecific methods (known as the Logger interface):

MethodParametersReturn type
setLevel()level: LogLevelvoid
setName()name: stringvoid
debug()...msgs: any[]void
info()...msgs: any[]void
warn()...msgs: any[]void
error()...msgs: any[]void

A very simple custom logger might ignore the name and level, and write all messages to a file.

Automatic retries

In production systems, you want your app to be resilient to short hiccups and temporary outages. Solving for thisproblem usually involves building a queuing system that handles retrying failed tasks. The WebClient comes with thisqueuing system out of the box, and its on by default! The client will retry a failed API method call up to 10 times,spaced out over about 30 minutes. If the request doesn’t succeed in that time, then the returned Promise will reject.You can observe each of the retries in your logs by setting the log level to DEBUG. Try running thefollowing code with your network disconnected, and then re-connect after you see a couple of log messages:

Shortly after re-connecting your network, you should see the Done! message. Did you notice the program doesn’t use avalid token? The client is doing something clever and helpful here. It knows the difference between an error such as notbeing able to reach api.slack.com and an error in the response from Slack about an invalid token. The former issomething that can be resolved with a retry, so it was retried. The invalid token error means that the call isn’t goingto succeed until your app does something differently, so it stops attempting retries.

You might not think 10 reties in 30 minutes is a good policy for your app. No problem, you can set the retryConfig toone that works better for you. The retryPolicies export contains a few well known options, and you can always writeyour own.

Here are some other values that you might want to use for retryConfig:

retryConfigDescription
retryPolicies.tenRetriesInAboutThirtyMinutes(default)
retryPolicies.fiveRetriesInFiveMinutesFive attempts in five minutes
retryPolicies.rapidRetryPolicyUsed to keep tests running fast
{ retries: 0 }No retries (other options)

Note: If an API call results in a rate limit being exceeded, you might still notice the client automaticallyretrying the API call. If you’d like to opt out of that behavior, set the rejectRateLimitedCalls option to true.

Upload a file

A couple methods, files.upload and users.setPhoto, allow you to upload a file over the API. In Node, there are a fewways you might be dealing with files, or more generally, binary data. When you have the whole file in memory (like whenyou’ve just generated or processed an image), then in Node you’d have a Buffer that contains that binary data. Or,when you are reading the file from disk or a network (like when you have a path to file name), then you’d typically havea ReadableStream. The client can handle both of these binary data types for you, and it looks like any other API call.

The following example shows how you can use files.upload to upload afile that is read from disk (as a ReadableStream).

In the example above, you could also use a Buffer object as the value for the file property of the options object.

Proxy requests with a custom agent

Slack

The client allows you to customize the HTTPAgent used to create the connection to Slack.Using this option is the best way to make all requests from your app through a proxy, which is a common requirement inmany corporate settings.

Slack Node

Slack Node Login

In order to create an Agent from some proxy information (such as a host, port, username, and password), you can useone of many npm packages. We recommend https-proxy-agent. Startby installing this package and saving it to your package.json.

Import the HttpsProxyAgent class, and create an instance that can be used as the agent option of the WebClient.

Rate limits

When your app calls API methods too frequently, Slack will politely ask (by returning an error) the app to slow down,and also let your app know how many seconds later it should try again. This is called rate limiting and theWebClient handles it for your app with grace. The client will understand these rate limiting errors, wait theappropriate amount of time, and then retry the request without any changes in your code. The Promise returned onlyresolves when Slack has given your app a real response.

It’s a good idea to know when you’re bumping up against these limits, so thatyou might be able to change the behavior of your app to hit them less often. Your users would surely appreciate gettingthings done without the delay. Each time a rate limit related error occurs, the WebClient instance emits an event:WebClientEvent.RATE_LIMITED. We recommend that you use the event to inform users when something might take longer thanexpected, or just log it for later.

You might not want to the WebClient to handle rate limits in this way. Perhaps the operation was time sensitive, andit won’t be useful by the time Slack is ready for another request. Or, you have a more sophisticated approach. In thesecases, you can set the rejectRateLimitedCalls option on the client to true. Once you set this option, method callscan fail with rate limiting related errors. These errors have a code property set to ErrorCode.RateLimitedError. Seeerror handling for more details.

Request concurrency

Each of the API method calls the client starts are happening concurrently, or at the same time. If your app triesto perform a lot of method calls, let’s say 100 of them, at the same time, each one of them would be competing for thesame network resources (such as bandwidth). By competing, they might negatively affect the performance of all the rest,and therefore negatively affect the performance of your app. This is one of the reasons why the WebClient limits theconcurrency of requests by default to ten, which means it keeps track of how many requests are waiting, and onlystarts an eleventh request when one of them completes. The exact number of requests the client allows at the same timecan be set using the maxRequestConcurrency option.

The lower you set the maxRequestConcurrency, the less parallelism you’ll have in your app. Imagine setting theconcurrency to 1. Each of the method calls would have to wait for the previous method call to complete before it caneven be started. This could slow down your app significantly. So its best not to set this number too low.

Another reason, besides competing for resources, that you might limit the request concurrency is to minimize theamount of state in your app. Each request that hasn’t completed is in some ways a piece of state that hasn’t yet beenstored anywhere except the memory of your program. In the scenario where you had 100 method calls waiting, and yourprogram unexpectedly crashes, you’ve lost information about 100 different things going on in the app. But by limitingthe concurrency to a smaller number, you can minimize this risk. So its best not to set this number too high.

Custom TLS configuration

Each connection to Slack starts with a handshake that allows your app to trust that it is actually Slack you areconnecting to. The system for establishing this trust is called TLS. In order for TLS to work, the host running your appkeeps a list of trusted certificate authorities, that it can use to verify a signature Slack produces. You don’tusually see this list, its usually a part of the operating system you’re running on. In very special cases, like certaintesting techniques, you might want to send a request to another party that doesn’t have a valid TLS signature that yourcertificate authority would verify. In these cases, you can provide alternative TLS settings, in order to change how theoperating system might determine whether the signature is valid. You can use the tls option to describe the settingsyou want (these settings are the most common and useful from the standard NodeAPI).

tls propertyDescription
caOptionally override the trusted CA certificates. Any string or Buffer can contain multiple PEM CAs concatenated together.
keyPrivate keys in PEM format. PEM allows the option of private keys being encrypted. Encrypted keys will be decrypted with passphrase.
certCert chains in PEM format. One cert chain should be provided per private key.
pfxPFX or PKCS12 encoded private key and certificate chain. pfx is an alternative to providing key and cert individually. PFX is usually encrypted, if it is, passphrase will be used to decrypt it.
passphraseShared passphrase used for a single private key and/or a PFX.

Custom API URL

Slack Nodes

The URLs for method calls to Slack’s Web API always begin with https://slack.com/api/. In very special cases, such ascertain testing techniques, you might want to send these requests to a different URL. The slackApiUrl option allowsyou to replace this prefix with another.

Exchange an OAuth grant for a token

There’s one method in the Slack Web API that doesn’t requires a token, because its the method that gets a token! Thismethod is called oauth.v2.access. It’s used as part of the OAuth2.0 process that users initiate when installing your app into a workspace. In thelast step of this process, your app has received an authorization grant called code which it needs to exchange foran access token (token). You can use an instance of the WebClient that has no token to easily complete thisexchange.

Webhook Nodejs

Note: If you’re looking for a more complete solution that handles more of the OAuth process for your app, take alook at the @aoberoi/passport-slack Passport Strategy.





Comments are closed.