How to get your daily news right within Slack

By Jason Wong

In this tutorial, we'll be creating a Workflow Builder step for you and your teammates to use. Adding this step to a Workflow will send news articles to a specified channel, based on some kind of preset trigger. Here's an example of what your app will post:

example news app message

First of all, what is Workflow Builder?

Workflow Builder is a no-code set of tools to automate tasks within Slack. In essence, a Workflow consists of a single trigger, followed by one or more steps. The built-in steps that you can use are:

  1. Sending a message into Slack
  2. Sending a form into Slack to gather information.

In addition to the built-in steps, it is also possible to ✨ create your own custom step ✨ , which is what we'll be doing in this tutorial. If you want to know more about Workflow Builder itself, take a look at our Help Center documentation or watch the video below.

Before we start, let's cover some of the use cases that this app could be useful for.

Handy use cases

  • You want to create a daily trigger to get news articles on a particular topic to stay up-to-date and in the know. Some examples could be:
    • Industry trends (new technologies, stock prices, world news)
    • Mentions about your company or products
  • You want to hear about the newest sports scores and stimulate some conversation within your company's #baseball or other hobby and interests channels
    • baseball, MLB, Ohtani
    • anime, cooking, language learning
  • You don't have a particular preference about what topic you want to read, but you want the latest news right within Slack to start off your morning.

Things you'll need

  • A workspace to create your app on. Your workspace must be a paid team as Workflow Builder is only available for paid teams.
  • A News API account. Here's where you can sign up.
  • A Slack App.
  • A Python dev environment with Bolt for Python installed.

Let's start building

This is how your app will look once it is finished:

news workflow step invocation

Clone the Github Repository

The code for this app can be found on this GitHub repository. Clone the repository so that you can have a local version to work with.

Retrieving your News API key

You can get a News API key for free if you are creating an app for development purposes. On the sign up page, fill in your name, e-mail address, and password, then choose "I am an individual" and submit the form. You will be granted an API key, which you should keep in a safe spot as we'll use this to set the NEWS_API_KEY environment variable later in this tutorial. Note that the News API free plan has a limit of 100 requests per day.

News API key page

Creating and setting up your Slack App

Create a Slack app

You can create a Slack app from the link above and doing so from this link will automatically add the correct scopes needed for your app to function. Just choose which workspace that you would like it to live on.

Once this is done, you'll need to issue an App-Level token which allows for you to use Socket Mode, a feature that allows you to send and receive payloads securely over a WebSocket. To do this, scroll down to App-Level Tokens and press the Generate Token and Scopes button. Give it a name and grant it the connections:write scope and press Generate. Copy the value and keep it safe as we'll use it to set the SLACK_APP_TOKEN environment variable.

Next, you'll need to install your app in your workspace. Doing so will create a bot token, which will allow your Slack app to call the Slack API. In the left-hand sidebar, head to Install App and then hit the Install to Workspace button and follow the prompts. After you've been redirected, copy the value of your Bot User OAuth Token. This will be used to set the value of SLACK_BOT_TOKEN in the next step.

Set up your Python environment and environment variables

Set up your Python environment by installing all the required libraries. This can be done by utilizing the requirements.txt file found in the repository. You'll also need to set the values of the following variables that you obtained in steps above as environment variables so that your Slack app can access them. Make sure that you don't mix up SLACK_APP_TOKEN with SLACK_BOT_TOKEN as they look similar! Your SLACK_APP_TOKEN will start with xapp- and your SLACK_BOT_TOKEN will start with xoxb-.

# Setting up your python environment
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

# Set your API keys and tokens from the steps above as environment variables 
export NEWS_API_KEY=as2fs...
export SLACK_APP_TOKEN=xapp-...
export SLACK_BOT_TOKEN=xoxb-...

You can use the following command to start your app:

python app.py

Three simple functions

Now that we have everything setup for success, we'll need to define the following three functions in your code: edit, save and execute. The edit function will be called whenever your workflow step is edited and will be a place to configure your step. After that, we'll implement the save function, which will define what inputs and outputs we want from our step. Lastly, we'll build the execute, which is where the actual functionality of your app will happen -- in our case calling the News API. Let's get started.

Configuration for your Workflow step (edit)

news workflow step editing

This is where you define the configuration parameters of your custom step, shown above. You can use the Block Kit Builder to mock out what this modal will look like and how it will gather information from the user. One thing to note is that this function is called not only when the user first opens up your step, but also on subsequent opens of the modal as well. For that reason, you’ll need to make sure that you repopulate the modal with the previous input. You can find more details on the edit function within the API documentation.

Following is an example of what this looks like for just the query block in this app. Note that the rest of the code has been removed for clarity.

def edit(ack: Ack, step: dict, configure: Configure):
    ack()
    inputs = step.get("inputs")
    blocks = []

    # Block for users to enter a search query
    query_block = {
        "type": "input",
        "block_id": input_query,
        "optional": True,
        "element": {
            "type": "plain_text_input",
            "action_id": "_",
            "placeholder": {
                "type": "plain_text",
                "text": "technology, sports, finance (Separate multiple terms by a comma)",
            },
        },
        "label": {
            "type": "plain_text",
            "text": "Query (Leave blank for all news articles)",
        },
    }

    # Re-populates your configuration blocks with the previously filled out values when editing your workflow step
    if input_query in inputs:
        value = inputs.get(input_query).get("value")
        if value is not None:
            query_block["element"]["initial_value"] = value

    # Add your blocks in the order that you would like them to show up in the modal
    blocks.append(query_block)

    # Pass your blocks to the `configure` function and it will handle the rest
    configure(blocks=blocks)

Saving your Workflow step configuration (save)

news workflow step saving

This function is where you retrieve the values that the user inputs into your modal from the edit stage. This particular function is run whenever a user presses the green Save button. The inputs dictionary represents the data your app expects to receive from the user upon workflow step execution (i.e. what is shown from the edit modal). The outputs dictionary is a list of objects containing data that your app will provide upon the workflow step’s completion which can be used in subsequent steps of your Workflow. More details can be found within our API documentation. Again in this snippet, some code has been removed for clarity.

    state_values = view["state"]["values"]

    #  Extracts the values found within the `state` parameter
    query = _extract(state_values, input_query, "value")

    update(
        # Defines what is required from the user for this Workflow step
        inputs={
            input_query: {"value": query},
        },
        # Defines what will be produced when this step is properly completed
        outputs=[
            {
                "name": channel_id,
                "type": "text",
                "label": "Posted message timestamp",
            }
            for channel_id in channels
        ],
    )
    ack()

Execution of your Workflow step (execute)

news workflow step invocation

This particular function is different from the two before it and is run whenever a workflow containing your step is run. In this tutorial, this is where any logic associated with interacting with a third-party API lives. Fetch the needed data and bring it back to this function for further processing. In this case, we’re calling the News API for some articles and then sending a message into Slack with this information. For the full details on this method, visit the documentation page.

def execute(step: dict, client: WebClient, complete: Complete, fail: Fail):
    inputs = step.get("inputs", {})
    try:
        query = inputs.get(input_query).get("value")
        num_articles = int(inputs.get(input_num_articles).get("value"))
        channels = inputs.get(input_channel_ids).get("value").split(",")

        # Calls the third-party News API and retrieves articles using inputs from above
        articles = fetch_articles(news_api_key, query, num_articles)
    except Exception as err:
        fail(error={"message": f"Failed to fetch news articles ({err})"})
        return

    outputs = {}
    try:
        if articles:
            for article in articles:
                blocks = format_article(article)
                for channel in channels:
                    # Send a message to all the specified channels
                    response = client.chat_postMessage(
                        channel=channel,
                        blocks=blocks,
                        unfurl_links=False,
                        unfurl_media=False,
                        text=article.title,
                    )
                    outputs[channel] = response.get("message").get("ts")
        else:
            # Notify the user that no articles were found.
            for channel in channels:
                response = client.chat_postMessage(
                    channel=channel,
                    text=f"No articles matched your query: {query}.",
                )
                outputs[channel] = response.get("message").get("ts")

    except SlackClientError as err:
        fail(error={"message": f"Notification failed ({err})"})

    complete(outputs=outputs)

Creating a workflow and adding your step into it

Now that we're done coding, all that's left is to create a workflow and add your newly created step. If you need detailed instructions on how to do this, check this Help Center article. Otherwise, create a workflow with whichever kind of trigger that you want. In this example, a shortcut trigger was used so that we can test it easily. Don't forget to add in your newly created step!

Adding news workflow step to a workflow

Finish

Once you have all the functions defined and your workflow created, you're good to go! Within Slack, click on the lightning bolt icon in the bottom left hand corner, type "News Please!" and hit Enter. If everything is set up correctly, you'll see some news articles pop up for your reading pleasure. You can also modify the trigger to be on a schedule so that you can your favorite news every day at 9AM.

Take your app to the next level! (Next steps)

  • Build an app home for your app to use and reuse the same blocks to post within an App Home
  • Add more options so that your users can customize even further. The News API documentation has a multitude of other parameters that we haven't explored but can help to get you the information that you need. A quick and fun one could be adding new languages.
  • Build a different step that hooks up into a different service.

Related documentation

Was this page helpful?