Catalyst Type Extension

Catalyst Integration in Cliq allows you to integrate Cliq components with Catalyst for custom-built coding functions that contain the intense business logic of a Cliq Extension. By integrating catalyst with Cliq, you can code your business logic of any development platform that is supported via Catalyst, in Java or Node.js platform. When a particular feature is accessed in the external service, Catalyst automatically invokes the execution of the Integration function coded in Catalyst through an HTTP call.

Catalyst Integration Functions can bundle the following Cliq internal tools: 

  • Bots: Conversational interface that handles tasks or responds to queries
  • Commands: Slash commands that perform custom actions
  • Message Actions: Actions that are performed as the response to individual messages
  • Widgets: Enables you to customize your Cliq home screen
  • Functions: Custom functions that can execute tasks based on event occurrences

You can learn more about these internal tools and extensions from the Cliq developer documentation.

 Creating a project in Catalyst 

To create a project in Catalyst navigate to https://cliq.zoho.com/developer.

  • Click on Create Extension button.
  • Enter the name and the description of the project.
  • Select execution type as Catalyst Function.
  • Click on Create Project. Clicking on it will navigate you to the catalyst project page, where you can create a new project.

Creating a function in Catalyst

  • To create a function enter the name and the description of the extension you want to create.
  • Select execution type as Catalyst Function.
  • Select a project from the project dropdown or create a new project by clicking on Create project.
  • Click on Create Function, once you click on it, you will see a popup with the necessary CLI command to initialize a Catalyst Function.
  • You can create a function either by executing this command line in the terminal or by accessing your project and selecting Functions, present under COMPUTE in the left-hand side menu.
  • Click on Create Function.
  • Select function type as Integration.
  • Select Zoho Cliq as a service.
  • Choose your preferred stack.
  • Click Create.

 Integrating Catalyst with Cliq 

  • Once you have created a project and a function, add the required components by clicking on the + icon.
  • Activate or deactivate the installation flow as per your requirement.
  • Click on Create Extension.

This will integrate Catalyst with Cliq and enable the Integration function to be executed each time Cliq components bundled with the extensions are accessed.

Catalyst configuration :

catalyst-config.json will be used to associate the Cliq handler with the Catalyst executor class/function.

Java:



//$Id$
package com.handlers;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.json.JSONObject;
import com.zc.cliq.enums.ACTION_TYPE;
import com.zc.cliq.enums.BUTTON_TYPE;
import com.zc.cliq.enums.SLIDE_TYPE;
import com.zc.cliq.interfaces.BotHandler;
import com.zc.cliq.objects.Action;
import com.zc.cliq.objects.ActionData;
import com.zc.cliq.objects.BotContext;
import com.zc.cliq.objects.BotContextParam;
import com.zc.cliq.objects.BotSuggestion;
import com.zc.cliq.objects.ButtonObject;
import com.zc.cliq.objects.CardDetails;
import com.zc.cliq.objects.MessageBuilder;
import com.zc.cliq.objects.Slide;
import com.zc.cliq.requests.BotContextHandlerRequest;
import com.zc.cliq.requests.BotMentionHandlerRequest;
import com.zc.cliq.requests.BotMenuActionHandlerRequest;
import com.zc.cliq.requests.BotMessageHandlerRequest;
import com.zc.cliq.requests.BotWebhookHandlerRequest;
import com.zc.cliq.requests.BotWelcomeHandlerRequest;
import com.zc.cliq.util.ZCCliqUtil;

public class BotClass implements BotHandler
{
 Logger LOGGER = Logger.getLogger(BotClass.class.getName());
 
 @Override
 public Map welcomeHandler(BotWelcomeHandlerRequest req) {
  Map resp = new HashMap();
  resp.put("text", "Hello " + req.getUser().getFirstName() + ". Thank you for subscribing :smile:");
  return resp;
 }
 
 @Override
 public Map messageHandler(BotMessageHandlerRequest req) {
  try{
   
  String message = req.getMessage();
  Map resp = new HashMap();
  
  String text;
  if(message.equalsIgnoreCase("hi") || message.equalsIgnoreCase("hey")){
   text = "Hi " + req.getUser().getFirstName() + " :smile:. How are you doing?";
   BotSuggestion suggestion = BotSuggestion.getInstance();
   suggestion.addSuggestion("Good");
   suggestion.addSuggestion("Not bad");
   suggestion.addSuggestion("Meh");
   suggestion.addSuggestion("Worst");
   resp.put("suggestions", suggestion);
  }
  else if(message.equalsIgnoreCase("Good") || message.equalsIgnoreCase("Not bad")){
   text = "That's glad to hear :smile:";
  }
  else if(message.equalsIgnoreCase("Meh") || message.equalsIgnoreCase("Worst")){
   text = "Oops! Don't you worry. Your day is definitely going to get better. :grinning:";
  }
  else if(message.equalsIgnoreCase("details")){
   text = "Welcome to details collection center :wink:";
   
   BotContext context = BotContext.getInstance();
   context.setId("personal_details");
   context.setTimeout(300);
   
   BotContextParam param1 = BotContextParam.getInstance();
   param1.setName("name");
   param1.setQuestion("Please enter your name: ");
   
   BotContextParam param2 = BotContextParam.getInstance();
   param2.setName("dept");
   param2.setQuestion("Please enter your department: ");
   param2.addSuggestion("CSE");
   param2.addSuggestion("IT");
   param2.addSuggestion("MECH");
   
   context.setParams(param1, param2);
   resp.put("context", context);
  }
  else{
   text = "Oops! Sorry, I'm not programmed yet to do this :sad:";
  }
  
  resp.put("text", text);
  return resp;
  
  }
  catch(Exception ex){
   LOGGER.log(Level.SEVERE, "Exception in message handler. ", ex);
   throw ex;
  }
 }
 
 @Override
 public Map contextHandler(BotContextHandlerRequest req) {
  
  Map resp = new HashMap();
  if(req.getContextId().equals("personal_details")){
   Map answers = req.getAnswers();
   StringBuilder str = new StringBuilder();
   str.append("Name: ").append(answers.get("name")).append("\n");
   str.append("Department: ").append(answers.get("dept")).append("\n");
   
   resp.put("text", "Nice ! I have collected your info: \n" + str.toString());
  }
  return resp;
 }

 @Override
 public Map mentionHandler(BotMentionHandlerRequest req)
 {
  String text = "Hey *" + req.getUser().getFirstName() + "*, thanks for mentioning me here. I'm from Catalyst city";
  Map resp = new HashMap();
  resp.put("text", text);
  return resp;
 }

 @Override
 public Map menuActionHandler(BotMenuActionHandlerRequest req) {
  Map resp = new HashMap();
  String text;
  if(req.getActionName().equals("Say Hi")){
   text = "Hi";
  }
  else if(req.getActionName().equals("Look Angry")){
   text = ":angry:";
  }
  else{
   text = "Action Press :fist:";
  }
  resp.put("text", text);
  return resp;
 }

 @Override
 public Map webhookHandler(BotWebhookHandlerRequest req) throws Exception
 { 
  // For incoming mails in ZohoMail
  JSONObject reqBody = req.getBody();
  String summary;
  String bodyStr = new StringBuilder("*From*: ").append(reqBody.getString("fromAddress"))
      .append("\n*Subject*: ").append(reqBody.getString("subject"))
      .append("\n*Content*: ").append((summary=reqBody.getString("summary")).length() > 100 ? summary.substring(0, 100) : summary).toString();
  MessageBuilder msg = MessageBuilder.getInstance(bodyStr);
  msg.setBot("PostPerson", "https://previews.123rf.com/images/nastyatrel/nastyatrel2006/nastyatrel200600035/149438272-flat-vector-illustration-chat-bot-icon-flat-line-style-vector-.jpg");
  CardDetails card = CardDetails.getInstance();
  card.setTitle("New Mail");
  card.setThumbnail("https://seekicon.com/free-icon-download/mail-icon_18.svg");
  msg.setCard(card);
  
  ButtonObject button = new ButtonObject();
  button.setLabel("Open mail");
  button.setType(BUTTON_TYPE.GREEN_OUTLINE);
  button.setHint("Click to open the mail in a new tab");
  Action action  = new Action();
  action.setType(ACTION_TYPE.OPEN_URL);
  ActionData actionData = new ActionData();
  actionData.setWeb("https://mail.zoho.com/zm/#mail/folder/inbox/p/" + reqBody.getLong("messageId"));
  action.setData(actionData);
  button.setAction(action);
  
  msg.addButton(button);
  
  Slide gifSlide = Slide.getInstance();
  gifSlide.setType(SLIDE_TYPE.IMAGES);
  gifSlide.setTitle("");
  List obj = new ArrayList(){{add("https://media.giphy.com/media/efyEShk2FJ9X2Kpd7V/giphy.gif");}};
  gifSlide.setData(obj);
  
  msg.addSlide(gifSlide);
  
  return ZCCliqUtil.toMap(msg);
 }
}

Download full source code (Default code - Java)
To try out this extension you can install it from here

Node.js function :



const cliq = require('zcatalyst-integ-cliq');
const command = cliq.command();
const https = require('https');

command.executionHandler(async (req, res) => {
    let text;
    const commandName = req.name;
    if(comp(commandName, 'catalystquote')) {
        try {
            text = await getQuote();
        } catch(err) {
            console.error('Exception while getting quote from third-party API. ' + err);
            text = 'Error while fetching quote!';
        }
    }
    else if (comp(commandName, 'catalystresource')) {
        const suggestions = req.selections;
        if(suggestions === undefined || suggestions.length === 0) {
            text = 'Please select a suggestion from the command';
        }
        else {
            const prefix = 'Take a look at our ';
            if(comp(suggestions[0].title, 'API doc')) {
                text = prefix + '[API Documentation](https://www.zoho.com/catalyst/help/api/introduction/overview.html)';
            }
            else if (comp(suggestions[0].title, 'CLI doc')) {
                text = prefix + '[CLI Documentation](https://www.zoho.com/catalyst/help/cli-command-reference.html)';
            }
            else {
                text = prefix + '[help documentation](https://www.zoho.com/catalyst/help/)';
            }
        }
    }
    else if(comp(commandName, 'getform')) {      
        return getForm();
    }
    else {
        text = 'Command executed successfully!';
    }
    res.text = text;
    return res;
});

command.suggestionHandler(async (req, res) => {
    if(comp(req.name,'catalystresource')) {
        const suggestion1 = command.newCommandSugestion();
        suggestion1.title = 'API doc';
        suggestion1.description = 'Catalyst API documentation';
        suggestion1.imageurl = '/sites/zweb/images/product-home-page/catalyst-icon.png';

        const suggestion2 = command.newCommandSugestion();
        suggestion2.title = 'CLI doc';
        suggestion2.description = 'Catalyst CLI documentation';
        suggestion2.imageurl = '/sites/zweb/images/product-home-page/catalyst-icon.png';

        const suggestion3 = command.newCommandSugestion();
        suggestion3.title = 'Help docs';
        suggestion3.description = 'Catalyst help documentation';
        suggestion3.imageurl = '/sites/zweb/images/product-home-page/catalyst-icon.png';

        res.push(suggestion1,suggestion2,suggestion3);
        return res;
    }
});

async function getQuote() {
    return new Promise((resolve, reject) => {
        let responseBuffer = [];
        const options = {
            hostname: 'quote.com',
            path: '/api/v3/quotes/random',
            method: 'GET'
          };
        const req = https.request(options, res => {
            res.on('data', (d) => {
                responseBuffer.push(d);
            });
            res.on('error', (err) => {
                if(req.aborted) {
                    return;
                }
                reject(err);
            })
            res.on('end', () => {
                try {
                    const responseData = Buffer.concat(responseBuffer);
                    const data = JSON.parse(responseData.toString('utf-8')).data[0];
                    const quote = data.quoteText + '\n\nAuthor: ' + data.quoteAuthor 
                    + '\nGenere: ' + data.quoteGenre;
                    resolve(quote);
                }
                catch(err) {
                    reject(err);
                }
            });
        });
        req.on('error', error => {
            if(req.aborted) {
                return;
            }
            reject(err);
        });

        req.end();
    });
}

function getForm() {
    const form = command.newHandlerResponse().newForm();
    form.title = 'Asset Request';
    form.hint = 'Raise your asset request';
    form.name = 'ID';
    form.button_label = 'Raise Request';
    form.version = 1;

    const actions = form.newFormActionsObject();
    actions.submit = actions.newFormAction('formFunctionLatest');// ** ENTER YOUR FORM FUNCTION NAME HERE **

    form.actions = actions;

    const userName = form.newFormInput();
    userName.type = 'text';
    userName.name = 'username';
    userName.label = 'Name';
    userName.hint = 'Please enter your name';
    userName.placeholder = 'John Reese';
    userName.mandatory = true;
    userName.value = 'Harold Finch';
    form.addInputs(userName);

    const email = form.newFormInput();
    email.type = 'text';
    email.format = 'email';
    email.name = 'email';
    email.label = 'Email';
    email.hint = 'Enter your email address';
    email.placeholder = "johnreese@poi.com";
    email.mandatory = true;
    email.value = "haroldfinch@samaritan.com";
    const assetType = form.newFormInput();
    assetType.type = 'select';
    assetType.trigger_on_change = true;
    assetType.name = 'asset-type';
    assetType.label = "Asset Type";
    assetType.hint = "Choose your request asset type";
    assetType.placeholder = "Mobile";
    assetType.mandatory = true;   
    assetType.addOption(assetType.newFormValue('Laptop', 'laptop'));
    assetType.addOption(assetType.newFormValue('Mobile', 'mobile'));
    form.addInputs(email, assetType);
    return form;
}

function comp(var1, var2) {
    return var1.toUpperCase() === var2.toUpperCase();
}

Download full source code (Default code - Node.js)
To try out this extension you can install it from here