/** * DalalAI — Google Sheets connector * ================================= * Pull live DalalAI analytics straight into Google Sheets with a custom * =DALALAI() formula, backed by the DalalAI Developer API. * * SETUP (one time): * 1. In your Google Sheet: Extensions ▸ Apps Script * 2. Delete any boilerplate, paste this whole file, click Save. * 3. Reload the sheet. A new "DalalAI" menu appears. * 4. DalalAI ▸ Set API key — paste the key from * https://dalalai.com ▸ Developer API ▸ Generate Key. * (The key is stored in private Script Properties, never in a cell.) * * USAGE (in any cell): * =DALALAI("convergence") → full convergence table * =DALALAI("convergence-scores", "RELIANCE") → one symbol * =DALALAI("predictions", , "Symbol,Score") → only chosen columns * =DALALAI("convergence", , , 25) → cap to 25 rows * * Data auto-refreshes whenever the sheet recalculates. To force a refresh, * use DalalAI ▸ Refresh now (re-runs all DALALAI formulas). * * Endpoints: any /api/v1/{endpoint} listed at https://dalalai.com/api-docs * (e.g. convergence, convergence-scores, predictions, smart-money, * regime, sectors, valuation). Free tier = 1,000 calls/month. */ var DALALAI_BASE = 'https://dalalai.com/api/v1/'; var DALALAI_KEY_PROP = 'DALALAI_API_KEY'; /** * Custom function: fetch a DalalAI endpoint as a table. * * @param {string} endpoint API endpoint name, e.g. "convergence". * @param {string=} symbol Optional ticker filter, e.g. "RELIANCE". * @param {string=} fields Optional comma-separated columns to keep. * @param {number=} limit Optional max number of rows. * @return {Array>} 2-D range (header row + data rows). * @customfunction */ function DALALAI(endpoint, symbol, fields, limit) { if (!endpoint) { throw new Error('DALALAI: first argument (endpoint) is required, e.g. "convergence".'); } var key = _dalalaiKey_(); if (!key) { throw new Error('DALALAI: no API key set. Use the DalalAI ▸ Set API key menu.'); } var url = DALALAI_BASE + encodeURIComponent(String(endpoint).trim()) + '?format=csv'; if (symbol) { url += '&symbol=' + encodeURIComponent(String(symbol).trim()); } if (fields) { url += '&fields=' + encodeURIComponent(String(fields).replace(/\s+/g, '')); } if (limit) { url += '&limit=' + encodeURIComponent(String(parseInt(limit, 10) || '')); } var resp = UrlFetchApp.fetch(url, { method: 'get', muteHttpExceptions: true, headers: { 'X-API-Key': key, 'Accept': 'text/csv' } }); var code = resp.getResponseCode(); var text = resp.getContentText(); if (code === 401 || code === 403) { throw new Error('DALALAI: API key rejected (HTTP ' + code + '). Re-set the key via the DalalAI menu.'); } if (code === 429) { throw new Error('DALALAI: rate limit / quota exceeded (HTTP 429). Upgrade tier or retry later.'); } if (code >= 400) { throw new Error('DALALAI: request failed (HTTP ' + code + '). Check the endpoint name.'); } var rows = _dalalaiParseCsv_(text); if (!rows.length) { return [['No data returned for "' + endpoint + '".']]; } return rows; } /** Menu bootstrap. */ function onOpen() { SpreadsheetApp.getUi() .createMenu('DalalAI') .addItem('Set API key', 'dalalaiSetKey') .addItem('Refresh now', 'dalalaiRefresh') .addSeparator() .addItem('Test connection', 'dalalaiTest') .addToUi(); } /** Prompt for and store the API key in private Script Properties. */ function dalalaiSetKey() { var ui = SpreadsheetApp.getUi(); var res = ui.prompt('DalalAI API key', 'Paste your DalalAI Developer API key:', ui.ButtonSet.OK_CANCEL); if (res.getSelectedButton() !== ui.Button.OK) { return; } var key = (res.getResponseText() || '').trim(); if (!key) { ui.alert('No key entered.'); return; } PropertiesService.getScriptProperties().setProperty(DALALAI_KEY_PROP, key); ui.alert('DalalAI API key saved. Try =DALALAI("convergence") in a cell.'); } /** Force every DALALAI() formula to recalculate. */ function dalalaiRefresh() { SpreadsheetApp.flush(); var ss = SpreadsheetApp.getActiveSpreadsheet(); // Toggle an unused named-value to bust the custom-function cache. var p = PropertiesService.getScriptProperties(); p.setProperty('DALALAI_NONCE', String(Date.now())); ss.toast('DalalAI formulas refreshed.', 'DalalAI', 3); } /** Quick connectivity check. */ function dalalaiTest() { var ui = SpreadsheetApp.getUi(); try { var rows = DALALAI('regime'); ui.alert('Connected ✓ Received ' + rows.length + ' row(s).'); } catch (e) { ui.alert('Connection failed: ' + e.message); } } /** @return {string} stored API key or empty string. */ function _dalalaiKey_() { return PropertiesService.getScriptProperties().getProperty(DALALAI_KEY_PROP) || ''; } /** * Minimal RFC-4180 CSV parser (handles quoted fields, commas, newlines). * @param {string} text CSV text. * @return {Array>} */ function _dalalaiParseCsv_(text) { var out = []; if (!text) { return out; } var row = []; var field = ''; var inQuotes = false; for (var i = 0; i < text.length; i++) { var ch = text[i]; if (inQuotes) { if (ch === '"') { if (text[i + 1] === '"') { field += '"'; i++; } else { inQuotes = false; } } else { field += ch; } } else if (ch === '"') { inQuotes = true; } else if (ch === ',') { row.push(_dalalaiCoerce_(field)); field = ''; } else if (ch === '\n') { row.push(_dalalaiCoerce_(field)); field = ''; out.push(row); row = []; } else if (ch === '\r') { // ignore; \n handles row break } else { field += ch; } } if (field.length || row.length) { row.push(_dalalaiCoerce_(field)); out.push(row); } return out; } /** Convert numeric-looking strings to numbers so Sheets can chart/sum them. */ function _dalalaiCoerce_(v) { if (v === '' || v == null) { return ''; } var n = Number(v); return (!isNaN(n) && /^-?\d*\.?\d+$/.test(v)) ? n : v; }