// Zoho Cliq API Module // EU data center: https://cliq.zoho.eu // API: https://www.zoho.com/cliq/help/restapi/v2/ var API_BASE = 'https://cliq.zoho.eu'; function makeRequest(token, method, path, jsonBody, queryParams) { var url = API_BASE + path; if (queryParams) { var qs = []; for (var key in queryParams) { if (queryParams.hasOwnProperty(key) && queryParams[key] !== undefined) { qs.push(encodeURIComponent(key) + '=' + encodeURIComponent(queryParams[key])); } } if (qs.length > 0) { url += '?' + qs.join('&'); } } var headers = { 'Authorization': 'Zoho-oauthtoken ' + token }; var body = null; if (jsonBody) { body = JSON.stringify(jsonBody); headers['Content-Type'] = 'application/json;charset=UTF-8'; } var resp = shadowman.http.request(url, { method: method, body: body, headers: headers }); if (resp.status === 401) { return { _tokenExpired: true }; } if (resp.status >= 400) { var raw = resp.body; var msg; if (typeof raw === 'string') { try { var p = JSON.parse(raw); msg = p.error && p.error.message ? p.error.message : raw; } catch (e) { msg = raw; } } else if (raw && typeof raw === 'object') { msg = (raw.error && raw.error.message) || raw.message || raw.description || JSON.stringify(raw); } else { msg = String(raw); } return { error: 'Zoho Cliq API error (' + resp.status + '): ' + msg }; } if (resp.body && typeof resp.body === 'object') { return resp.body; } if (!resp.body || (typeof resp.body === 'string' && resp.body.trim() === '')) { return { success: true }; } try { return JSON.parse(resp.body); } catch (e) { return { error: 'Invalid JSON response: ' + resp.body }; } } function apiCall(token, method, path, jsonBody, queryParams) { var result = makeRequest(token, method, path, jsonBody, queryParams); if (result && result._tokenExpired) { var auth = require('./auth'); var newToken = auth.getValidToken(); if (!newToken) { return { error: 'Authentication expired and refresh failed. Re-run setup_auth.' }; } return makeRequest(newToken, method, path, jsonBody, queryParams); } return result; } // --- List chats (channels + direct chats) --- // GET /api/v2/chats function listChats(token) { return apiCall(token, 'GET', '/api/v2/chats'); } // --- List channels --- // GET /api/v2/channels function listChannels(token) { return apiCall(token, 'GET', '/api/v2/channels'); } // --- Get messages from a chat --- // GET /api/v2/chats/{chat_id}/messages function getMessages(token, chatId) { return apiCall(token, 'GET', '/api/v2/chats/{CHATID}/messages' .replace('{CHATID}', chatId)); } // --- Post message to a chat --- // POST /api/v2/chats/{chat_id}/message (singular!) function sendMessage(token, chatId, message) { var url = API_BASE + '/api/v2/chats/' + chatId + '/message'; var body = JSON.stringify({ text: message }); var headers = { 'Authorization': 'Zoho-oauthtoken ' + token, 'Content-Type': 'application/json' }; return makeRawRequest(token, 'POST', url, body, headers); } // --- Post message to a channel --- // POST /api/v2/channelsbyname/{channel_unique_name}/message function postToChannel(token, channelName, message) { var url = API_BASE + '/api/v2/channelsbyname/' + channelName + '/message'; var body = JSON.stringify({ text: message }); var headers = { 'Authorization': 'Zoho-oauthtoken ' + token, 'Content-Type': 'application/json' }; return makeRawRequest(token, 'POST', url, body, headers); } function makeRawRequest(token, method, url, body, headers) { var resp = shadowman.http.request(url, { method: method, body: body, headers: headers }); if (resp.status >= 400) { var raw = resp.body; var msg; if (typeof raw === 'string') { try { var p = JSON.parse(raw); msg = p.error && p.error.message ? p.error.message : raw; } catch (e) { msg = raw; } } else if (raw && typeof raw === 'object') { msg = (raw.error && raw.error.message) || raw.message || raw.description || JSON.stringify(raw); } else { msg = String(raw); } return { error: 'Zoho Cliq API error (' + resp.status + '): ' + msg }; } if (resp.body && typeof resp.body === 'object') { return resp.body; } if (!resp.body || (typeof resp.body === 'string' && resp.body.trim() === '')) { return { success: true }; } try { return JSON.parse(resp.body); } catch (e) { return { error: 'Invalid JSON response: ' + resp.body }; } } module.exports = { listChats: listChats, listChannels: listChannels, getMessages: getMessages, sendMessage: sendMessage, postToChannel: postToChannel };