New Serverside Chat Functionality: Unread Message Reminders

...

Learn how to use our new Reminders API to notify users of unread messages via push notifications, SMS, or email.

Unread Message Reminders feature image

Unread Message Reminders allow you to notify users of unread messages via SMS, push notifications, or email while they are offline. As the latest addition to our Stream Chat feature set, Unread Message Reminders are designed for 1-on-1 chat experiences and are available for all channel types.

If you were to build this on your own, you would have to set up a complex cron job architecture to check if a channel has unread messages. We made it easier with this new functionality.

This tutorial walks through one example of how to set up the Unread Message Reminders feature in tandem with external tools. For a technical overview of the feature itself, check out the documentation→

In this tutorial, you’ll learn how to configure and send an Unread Message Reminder. We’ll explore the functionality for sending reminder emails using SendGrid, Express, and our React Chat SDK.

Feel free to fork the Reminders API example repo on GitHub if you want to code along.

Enabling the Reminder Functionality

To get started, let’s set up our server with the code below:

import dotenv from 'dotenv';
dotenv.config();

const client = StreamChat.getInstance(process.env.API_KEY, process.env.API_KEY_SECRET);

await client.updateAppSettings({reminders_interval: 60});

await client.updateChannelType('messaging', {
    reminders: true,
    read_events: true,
});

In the code snippet above, we:

  • Imported the dotenv package so we can read the .env file, set our environment variables, and instantiate our Stream Chat client with those environment variables.
  • Enabled the Reminders API on the channel type messaging programmatically (you can also enable the Reminders API on your dashboard – keep scrolling to learn how).
  • Updated the app settings with the interval in seconds between each reminder event. The minimum time is 60 seconds.

Note: You can also enable the Reminders API in your Stream Dashboard:

  1. Go to your dashboard and select your app.
  2. Select the channel where you want to enable Message Reminders.
  3. Select the Message Reminders toggle button.
Enable Reminders API in dashboard

Setting Up the Webhook Endpoint With Express

In order to receive events from the Stream backend, we’ll import Express and set up a web server with a POST endpoint called “stream-event”. We’ll also need the body-parser middleware to parse the incoming request as JSON:

// ...
import express from 'express';
const app = express();
app.post('/stream-event', (request, response, next) => {
    console.log(request.body);
    res.send(request.body);
}
app.listen(YOURPORT, () => console.log(`Server running on port ${YOURPORT}`));

Start the server: node main.js

If you want, you can verify everything has been set up correctly so far by copy-pasting this curl command in your terminal:

curl -X POST -H "Content-Type: application/json" \ -d '{"hello": "world"}' \ htp://localhost::YOURPORT/stream-event

You should get a {"hello": "world"} back.

You can also sync up your SQS queue to trigger Reminder Events.

Tunneling Using ngrok

For development purposes, we’ll use a tunneling service like ngrok to expose our local development endpoint to the rest of the world.

Run the following command to fire up ngrok:

ngrok http YOURPORT

You should see something like this:

Tunneling with ngrok

We are interested in the second Forwarding field.

Add the path /stream-event to this URL and paste it to your dashboard:

Configure webhook dashboard

Reacting to the Reminder Event: Sending an Email

In this section, we’ll parse the Reminder event, of type user.unread_message_reminder, and send an email with the following text:

“You have X unread messages.”

To do so, we need a couple of things from the reminder event:

  • The user’s email address
  • The number of unread messages

Note: Reminders will only send if certain conditions are met. Check out the Reminder Event docs to learn more.

We then use JS Template literals to compose our “dynamic” message.

In order to send the email, we’ll use SendGrid’s Email API and their JS SDK.

To use SendGrid:

  1. Sign up for a free SendGrid account
  2. In your dashboard, select Email API > Integration Guide
  3. Create and verify your sender identity
Create a Single Sender Identity
  1. Go back to the Integration Guide and select Web API
    Choose Web API
  2. Select Node.js
  3. Follow the steps to create your SendGrid API KEY
SendGrid Email API

Once you have your SendGrid API Key, add it to your .env file.

With SendGrid ready to go, let’s install the Sendgrid mail dependency and update our webhook:

// ...

import mail from "@sendgrid/mail";
mail.setApiKey(process.env.SENDGRID_API_KEY)

app.post('/stream-event', (request, response, next) => {
    if (request.body.type === "user.unread_message_reminder") {
        console.log(request.body);
        const user = request.body.user;

        const msg = {
            to: user.email,
            from: "YOUR VERIFIED SENDGRID SENDER",
            subject: 'You have messages!',
            text:  `You have ${user.unread_count} unread messages.`,
            html: `<strong> You have ${user.unread_count} unread messages</strong>.`,
        }
        mail
            .send(msg)
            .then(() => {
                console.log('Email sent')
            })
            .catch((error) => {
                console.error(error)
            })

    } else {
        console.log(request.body.user);
        next();
    }
});

// ...

Make sure the from field is a verified email on SendGrid; otherwise, you’ll get an error.

Triggering the Reminder Event

You can use your preferred client-side Stream Chat SDK, but for our purposes in this article, we are going to stay in the wonderful world of Javascript and choose React. We need a channel of type messaging with two users.

Let’s generate the tokens for our two users using our token generator and add them to our .env file.

The remaining code is just boilerplate code that you can copy-paste from our frontend SDK docs or READMEs:

import React, {useEffect, useState} from 'react';
import {StreamChat} from 'stream-chat';
import {
    Chat,
    Channel,
    ChannelHeader,
    ChannelList,
    LoadingIndicator,
    MessageInput,
    MessageList,
    Thread,
    Window,
} from 'stream-chat-react';

import 'stream-chat-react/dist/css/index.css';

const tessToken = process.env.REACT_APP_TESS_TOKEN;
const nashToken = process.env.REACT_APP_NASH_TOKEN;
const runningUser = process.env.USER;

const filters = {type: 'messaging', members: {$in: [runningUser === 'nash' ? 'nash' : 'tess']}};
const sort = {last_message_at: -1};

const App = () => {
    const [chatClient, setChatClient] = useState(null);

    useEffect(() => {
        const initChat = async () => {
            const client = StreamChat.getInstance(process.env.REACT_APP_API_KEY);

            if (process.env.USER === 'nash') {
                await client.connectUser(
                    {
                        id: 'nash',
                    },
                    nashToken,
                );
            } else {
                await client.connectUser(
                    {
                        id: 'tess',
                        email: "tess@getstream.io"
                    },
                    tessToken,
                );
            }
            setChatClient(client);
        };

        initChat();
    }, []);

    if (!chatClient) {
        return <LoadingIndicator/>;
    }

    return (
        <Chat client={chatClient} theme='messaging light'>
            <ChannelList filters={filters} sort={sort}/>
            <Channel>
                <Window>
                    <ChannelHeader/>
                    <MessageList/>
                    <MessageInput/>
                </Window>
                <Thread/>
            </Channel>
        </Chat>
    );
};

export default App;

Let’s send a message impersonating one of the users. If everything was set up correctly, after one minute you should receive an email. Congratulations 🎉

You can the full source code showcased in this article in this repo: stream-reminders-api-example

Conclusion

In this article, we sent a gentle reminder to our users to remind them they have unread messages while offline. It was a fairly simple integration, but expanding from that, you could tweak your email to look beautiful using HTML/CSS, or send a more complex email detailing the actual messages for each channel.

You could also send a deep link making the user experience even better. We chose emails, but you could go the route of Push Notifications or SMS as well.

We’d love to hear how you implement the Reminders API into your app and how your users respond. Be sure to tweet us at @getstream_io to let us know what you think, or contact support if you have any questions.