What?
A quick article on my adaptation of some code posted by Poorvik Palanikumar on the Zoho Community Forums to connect a ZohoCliq to the OpenAI API and ChatGPT... Note that I have another article for those who want to integrate ChatGPT with ZohoZIA.

Why?
Previously, I would edit the message handlers of a Cliq Bot I created but it would only understand the questions I have programmed it to and respond with the responses I programmed it to answer with. Any variations of the questions it did not understand, it would return a response similar to Siri or Ask Google where it includes the keywords in a "Search the web..." task.

ChatGPT by OpenAI is viral at the time of print and although the solution below only connects to the OpenAI API, it is not currently connected to ChatGPT. The plus side however is that it is connected to the Internet unlike ChatGPT which only has information up to 2021.

How?
The basics of setting this up would be to setup a bot in ZohoCliq and then to edit either a message handler or participation handler. As for the usage, simply ask the Cliq Bot a question and it will forward the query and respond with the response from the OpenAI API.

Setup a Zoho Cliq bot
  1. Login to ZohoCliq
  2. Click on your profile picture
  3. Select "Bots & Tools"
  4. Click on "Create Bot" (or "Edit Handlers" on an existing bot)
  5. Fill in the details then edit the Handlers.
  6. For demo purposes, edit the code for the "Message Handler"

Get an OpenAI API Key
  1. Login to OpenAI
  2. Browse to https://platform.openai.com/account/api-keys
  3. Click on "Create a new key"
  4. Copy & paste into a text editor for use in the code below

Edit the Message Handler code
ChatGTP 3.5 Turbo
// 
// initialize
m_Response = Map();
l_Messages = List();
v_Message = message.trim();
//
// if the message contains a question mark, send it to OpenAI
if(v_Message.contains("?"))
{
	v_Question = v_Message;
	//
	// Need to add your own OpenAI token here
	v_Token = "sk-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
	//
	// build header of request
	m_Header = Map();
	m_Header.put("Authorization","Bearer " + v_Token);
	m_Header.put("Content-Type","application/json");
	//
	// build params of request
	m_Params = Map();
	m_Params.put("model", "gpt-3.5-turbo");
	m_Message = Map();
	m_Message.put("role", "user");
	m_Message.put("content", v_Question);
	l_Messages.add(m_Message);
	m_Params.put("messages", l_Messages);
	//
	// https://platform.openai.com/docs/api-reference/chat/create
	m_Params.put("temperature", 0.8);
	m_Params.put("n", 1);
	m_Params.put("max_tokens", 256);
	m_Params.put("presence_penalty", 0);
	m_Params.put("frequency_penalty", 0);
	// 
	// send request to chatgpt openai
	r_ChatGPTResponse = invokeurl
	[
		url :"https://api.openai.com/v1/chat/completions"
		type :POST
		parameters:m_Params.toString()
		headers:m_Header
		detailed:true
	];
	if(r_ChatGPTResponse.get("responseCode") == 200)
	{
		//
		// retrieve the answer in text
		l_Choices = r_ChatGPTResponse.get("responseText").get("choices");
		for each m_Choice in l_Choices
        {
			if(!isnull(m_Choice.get("message")))
			{
				if(m_Choice.get("message").get("role")=="assistant")
				{
					//
					// add the answer text to the response map
					m_Response.put("text",m_Choice.get("message").get("content"));
					break;
				}
			}
        }
	}
	else 
	{
		// store in response text
		m_Response.put("text","I don't know. Consider doing a Google search.");
	}
}
//
// output
return m_Response;
Yields something like the following:
Zoho Cliq: Integrate OpenAI and now ChatGPT 3.5 Turbo
Text-Davinci-003: (preceded ChatGPT API)
// 
// initialize
m_Response = Map();
v_Answer = "";
v_Message = message.trim();
//
// if the message contains a question mark, send it to OpenAI
if(v_Message.contains("?"))
{
	v_Question = v_Message;
	//
	// Need to add your own OpenAI token
	v_Token = "sk-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
	//
	// build header of request
	m_Header = Map();
	m_Header.put("Authorization","Bearer " + v_Token);
	m_Header.put("Content-Type","application/json");
	//
	// build params of request
	m_Params = Map();
	m_Params.put("model", "text-davinci-003");
	m_Params.put("prompt", v_Question);
	m_Params.put("temperature", 0.9);
	m_Params.put("max_tokens", 256);
	m_Params.put("top_p", 1);
	m_Params.put("frequency_penalty", 0);
	m_Params.put("presence_penalty", 0);
	m_Params.put("stop", {" Human:"," AI:"});
	// 
	// send request to chatgpt openai
	r_ChatGPTResponse = invokeurl
	[
		url :"https://api.openai.com/v1/completions"
		type :POST
		parameters:m_Params.toString()
		headers:m_Header
		detailed:true
	];
	if(r_ChatGPTResponse.get("responseCode") == 200)
	{
		// retrieve the answer in text
		v_Answer = r_ChatGPTResponse.get("responseText").get("choices").getJSON("text");
	}
	else if(r_ChatGPTResponse.get("responseCode") == 429)
	{
		// store in response text
		v_Answer = "I dont have any knowledge in this. Please ask me something else";
	}
}
else if (v_Message.containsIgnoreCase("Hi") || v_Message.containsIgnoreCase("Hello") || v_Message.containsIgnoreCase("Hey")) 
{
		// store in response text
	v_Answer = "Hi " + user.get("first_name") + "!\nMy name is JoelBot and I am currently connected to the OpenAI API.  I can try and answer any question you end with a question mark.  Try me out!";
}
//
// add the answer text to the response map
m_Response.put("text", v_Answer);
//
// output
return m_Response;

Even in test mode or simply starting a Cliq conversation will now yield something like the following:

Zoho Cliq: Integrate OpenAI and soon ChatGPT

Additional
  • For every chat message, the OpenAI ChatGPT forgets what the context of the conversation was about: Well we can populate the list of messages so that there is a conversation trail for the OpenAI ChatGPT to build on:
    // 
    // initialize
    m_Response = Map();
    v_Message = message.trim();
    l_Messages = List();
    v_ChatID = ifnull(chat.getJSON("id"),0);
    //
    // get conversations from last 10 minutes
    v_MinutesAgo = 10;
    v_FromTime = zoho.currenttime.subMinutes(v_MinutesAgo).toLong();
    v_TillTime = zoho.currenttime.toLong() / 1000;
    //
    // by default gets last 100 messages (doesn't return anything if no parameters specified)
    v_Endpoint = "https://cliq.zoho.eu/api/v2/chats/"+v_ChatID + "/messages?fromtime=" + v_FromTime + "&limit=50";
    r_ChatDetails = invokeurl
    [
    	url: v_Endpoint
    	type: GET
    	connection: "ab_cliq"
    ];
    //
    // let's build up the conversation thread
    if(!isnull(r_ChatDetails.get("data")))
    {
    	for each r_MessageData in r_ChatDetails.get("data")
        {
    		v_Role = "user";
    		if(!isnull(r_MessageData.get("message_source")))
    		{
    			v_UserType = r_MessageData.get("message_source").get("type");
    			v_Role = if(v_UserType=="bot", "assistant", "user");
    		}
    		if(!isnull(r_MessageData.get("content")))
    		{
    			v_Content = r_MessageData.get("content").get("text");
    		}
    		//
    		m_Message = Map();
    		m_Message.put("role",v_Role);
    		m_Message.put("content",v_Content);
    		l_Messages.add(m_Message);
        }
    }

The one to rule them all
So I've been asked here and on the forums as well for the full query that includes making ChatGPT keep the context of the conversation along with new cliqs:
// 
// initialize
m_Response = Map();
v_Message = message.trim();
v_ChatID = ifnull(chat.getJSON("id"),0);
//
// get conversations from last 5 minutes
v_MinutesAgo = 10;
v_FromTime = zoho.currenttime.subMinutes(v_MinutesAgo).toLong();
v_TillTime = zoho.currenttime.toLong() / 1000;
//
// by default gets last 100 messages (doesn't return anything if no parameters specified)
v_Endpoint = "https://cliq.zoho.eu/api/v2/chats/" + v_ChatID + "/messages?fromtime=" + v_FromTime + "&limit=50";
r_ChatDetails = invokeurl
[
	url :v_Endpoint
	type :GET
	connection:"ab_cliq"
];
//
// let's build up the conversation thread
l_Messages = List();
if(!isnull(r_ChatDetails.get("data")))
{
	for each  r_MessageData in r_ChatDetails.get("data")
	{
		v_Role = "user";
		if(!isnull(r_MessageData.get("message_source")))
		{
			v_UserType = r_MessageData.get("message_source").get("type");
			v_Role = if(v_UserType == "bot","assistant","user");
		}
		if(!isnull(r_MessageData.get("content")))
		{
			v_Content = r_MessageData.get("content").get("text");
		}
		//
		m_Message = Map();
		m_Message.put("role",v_Role);
		m_Message.put("content",v_Content);
		l_Messages.add(m_Message);
	}
}
//
// if the message is not blank, send it to OpenAI
if(!isBlank(v_Message))
{
	v_Question = v_Message;
	//
	// Need to add openAI token here
	v_Token = "sk-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
	//
	// build header of request
	m_Header = Map();
	m_Header.put("Authorization","Bearer " + v_Token);
	m_Header.put("Content-Type","application/json");
	//
	// build params of request
	m_Params = Map();
	m_Params.put("model","gpt-3.5-turbo");
	//
	// add latest message to conversation thread
	m_Message = Map();
	m_Message.put("role","user");
	m_Message.put("content",v_Question);
	l_Messages.add(m_Message);
	m_Params.put("messages",l_Messages);
	//
	// https://platform.openai.com/docs/api-reference/chat/create
	m_Params.put("temperature",0.8);
	m_Params.put("n",1);
	m_Params.put("max_tokens",256);
	m_Params.put("presence_penalty",0);
	m_Params.put("frequency_penalty",0);
	// 
	// send request to chatgpt openai
	r_ChatGPTResponse = invokeurl
	[
		url :"https://api.openai.com/v1/chat/completions"
		type :POST
		parameters:m_Params.toString()
		headers:m_Header
		detailed:true
	];
	if(r_ChatGPTResponse.get("responseCode") == 200)
	{
		//
		// retrieve the answer in text
		l_Choices = r_ChatGPTResponse.get("responseText").get("choices");
		for each  m_Choice in l_Choices
		{
			if(!isnull(m_Choice.get("message")))
			{
				if(m_Choice.get("message").get("role") == "assistant")
				{
					//
					// add the answer text to the response map
					m_Response.put("text",m_Choice.get("message").get("content"));
					//break;
				}
			}
		}
	}
	else if(r_ChatGPTResponse.get("responseCode") == 429)
	{
		// catch all
		v_Response = "I don't know. Would you like me to Google: [" + v_Message + "](https://www.google.com/search?q=" + zoho.encryption.urlEncode(v_Message) + ")?";
		m_Response = {"text":v_Response };
	}
}
return m_Response;

Error(s) Encountered:
  • Exceeded Quote Limit: Try generating a new API key (aka Secret). If you need a paid plan, then you can access this by logging into OpenAI and browsing to https://platform.openai.com/account/billing/overview. At time of print, the paid plan is a pay as you go or pay as you use.

Previous Models/Versions
Based on the documentation: In the above code, you would change the model value to one of the following
code-davinci-002	(optimized for code-completion tasks)
text-davinci-002
text-davinci-003
gpt-3.5-turbo    (optimized for chat at 1/10th the cost of text-davinci-003)
gpt-4
gpt-4-32k    (up to 4x the context length)

Source(s):