Adding an Unsubscribe Feature to Your Bot

Arielle Johnson

Tuesday, July 25th 2017

When you’re in a Spark space with a bot and you want to stop getting notifications from that bot, you can simply leave the space or remove the bot from the space. As a developer, your bot even can subscribe to a membership webhook to get notified when someone leaves or removes your bot, allowing you to handle subscription status updates in your backend.

 

Have you ever had trouble leaving a 1:1 Spark space with an unrelenting bot? Since a Spark user can never truly leave a 1:1 space (whether it is with another user or a bot account), it can become frustrating when there is no way for them to stop a bot from sending automatic messages to the space. For example, if a Spark user enters a 1:1 space with a bot that continuously posts messages every time an RSS feed is updated.

 

In 1:1 spaces, those kind of Spark bots can continue to generate messages even if you close or attempt to leave the space. This can become even more confusing since the user will still receive a response that implies that the space has been left successfully.

 

In a group space, you’d be able to use the membership deleted webhook to get notified that someone doesn’t want to talk to your bot anymore, but in a 1:1, the membership deleted webhook never fires, since the person isn’t actually leaving the 1:1 or ejecting your bot from it. Your applications get no notification at all that the user even attempted to leave the space.

 

To remedy this issue, the onus is on the developer to ensure there is a way for a Spark user to stop receiving messages. Incorporating a UI and workflow option in the bot application to handle an unsubscribe feature is especially necessary when a bot is designed to send unprompted messages.

 

One way this can be done is shown in the node.js snippet below. This is a simple example, showing a bot application that updates a database to keep track of the users' subscription status and can make changes as necessary, depending on what their status is. The code is broken down and briefly explained here. However, to see the code snippet in its entirety, please scroll down to the end.

 

To begin this example, you will first want make sure that the Express library is installed, as well as a RESTful and database library of your choice.

 

// This code is under the assumption that you have installed: 
//  - Express
//  - A RESTful library
//  - A database library

 

Next, include the two general functions for this application. You will want a SendRequest function to send the requests and an UpdateTable function, which will continuously update the table that tracks the status of each user. Inside the curly brackets, you will write what you would like the function to handle.

 

// General Functions 
// Handles Sending Requests
var sendRequest (myURL, myMethod, myHeaders, myBody, callback) { /* ... */ };

// Handles Updating Database Table
var updateTable (myTableID, myKey, myValue) { /* ... */ };

 

The previous steps are completed before your endpoint is generated. Once they are complete you will add the following portion to generate the endpoint for your webhook, call the sendRequest function, and use GET to set Token as the environmental variable.

 

 

app.post('/spark/receive', function (req, res) { // Endpoint for your webhook      
   sendRequest( // Get message details 
      'https://api.ciscospark.com/v1/messages/' + req.body.data.id,    
      'GET',
      // GET TOKEN as environmental variable         
      {'Content-type':'application/json; charset=utf-8', 'Authorization':'Bearer ' + process.env.TOKEN;}, {}, // No body needed for GET request

 

Within the sendRequest function, you will also define the myResponseBody function, which sends the request to get the message content and controls the unsubscribe feature.

 

function(myResponseBody) { // Sends request to get the message contents    
         // Additional conditional code to handle other message contents     
         // Handle 'Unsubscribe' -- Start   
         if (myResponseBody.text == 'Unsubscribe') {      
            UpdateTable(
               myResponseBody.roomId,
               'subscribed', // Key indicates subscribed
               'no' // New value to indicate subscription status as "no"     
            );    
         }     
         // Additional conditional code to handle other message contents   
      });   
      res.sendStatus(200);
});

 

Below is the full snippet in its entirety.

 

// This code is under the assumption that you have installed: 
//  - Express
//  - A RESTful library
//  - A database library

// General Functions
// Handles Sending Requests
var sendRequest (myURL, myMethod, myHeaders, myBody, callback) { /* ... */ };

// Handles Updating Database Table
var updateTable (myTableID, myKey, myValue) { /* ... */ };

app.post('/spark/receive', function (req, res) { // Endpoint for your webhook     
   sendRequest( // Get message details 
      'https://api.ciscospark.com/v1/messages/' + req.body.data.id,    
      'GET',
      // GET TOKEN as environmental variable         
      {'Content-type':'application/json; charset=utf-8', 'Authorization':'Bearer ' +    process.env.TOKEN;}, {}, // No body needed for GET request   

      function(myResponseBody) { // Sends request to get the message contents   
         // Additional conditional code to handle other message contents     
         // Handle 'Unsubscribe' -- Start   
         if (myResponseBody.text == 'Unsubscribe') {      
            UpdateTable(
               myResponseBody.roomId,
               'subscribed', // Key indicates subscribed
               'no' // New value to indicate subscription status as "no"     
            );    
         }     
         // Additional conditional code to handle other message contents   
      });   
      res.sendStatus(200);
});

 

We hope this information is helpful. If you have any questions about this snippet or others, please feel free to contact us at the Spark space #spark4devs.

 

Phil Bellanti and Timothy Scheuering also contributed to this post.