/* ========================= Google Business Profile → Reviews proxy ========================= */ // --- CONFIG --- const GBP_V4 = 'https://mybusiness.googleapis.com/v4'; // Reviews live on v4 function doGet(e) { const p = e && e.parameter ? e.parameter : {}; const action = (p.action || '').toLowerCase(); const cb = p.callback || ''; // JSONP callback, optional // OAuth callback if (action === 'authcallback') return authCallback(e); if (action === 'listreviews') { const accountId = p.accountId; const locationId = p.locationId; const max = Math.min(parseInt(p.max || '5000', 10), 10000); if (!accountId || !locationId) { return out({ error: 'Missing accountId or locationId' }, cb); } const service = getService(); if (!service.hasAccess()) { // Show consent UI in JSON mode, or return link string if JSONP const html = getConsentHtml(service); if (cb) return out({ authorize: html }, cb); return HtmlService.createHtmlOutput(html); } try { const reviews = listAllReviews(service, accountId, locationId, max); return out({ reviews }, cb); } catch (err) { return out({ error: String(err) }, cb); } } return out({ ok: true }, cb); } // Fetch all reviews (paged) from GBP v4 function listAllReviews(service, accountId, locationId, max) { let pageToken = ''; const outArr = []; const headers = { Authorization: 'Bearer ' + service.getAccessToken() }; do { const url = GBP_V4 + `/accounts/${encodeURIComponent(accountId)}` + `/locations/${encodeURIComponent(locationId)}/reviews` + `?orderBy=updateTime desc&pageSize=100` + (pageToken ? `&pageToken=${encodeURIComponent(pageToken)}` : ''); const res = UrlFetchApp.fetch(url, { method: 'get', headers, muteHttpExceptions: true, }); const code = res.getResponseCode(); if (code !== 200) { throw new Error(`GBP error ${code}: ${res.getContentText()}`); } const body = JSON.parse(res.getContentText()); const items = body.reviews || []; items.forEach((r) => { outArr.push({ rating: r.starRating ? starToNumber(r.starRating) : r.rating, comment: r.comment || '', createTime: r.createTime, updateTime: r.updateTime, relativePublishTimeDescription: r.relativePublishTimeDescription, reviewer: { displayName: (r.reviewer && r.reviewer.displayName) || 'Google User', profilePhotoUrl: (r.reviewer && r.reviewer.profilePhotoUrl) || '', }, }); }); pageToken = body.nextPageToken || ''; } while (pageToken && outArr.length < max); return outArr.slice(0, max); } function starToNumber(s) { const map = { ONE: 1, TWO: 2, THREE: 3, FOUR: 4, FIVE: 5 }; return map[s] || 0; } /* ---------- Output helpers: JSON / JSONP ---------- */ function out(obj, callback) { if (callback) { // JSONP const js = `${callback}(${JSON.stringify(obj)})`; return ContentService.createTextOutput(js).setMimeType( ContentService.MimeType.JAVASCRIPT ); } // Plain JSON (no custom headers) return ContentService.createTextOutput(JSON.stringify(obj)).setMimeType( ContentService.MimeType.JSON ); } /* ---------- OAuth2 ---------- */ function getService() { const scriptId = ScriptApp.getScriptId(); return OAuth2.createService('GBP') .setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth') .setTokenUrl('https://oauth2.googleapis.com/token') .setClientId( PropertiesService.getScriptProperties().getProperty('OAUTH_CLIENT_ID') ) .setClientSecret( PropertiesService.getScriptProperties().getProperty( 'OAUTH_CLIENT_SECRET' ) ) .setCallbackFunction('authCallback') // matches action=authCallback above .setPropertyStore(PropertiesService.getUserProperties()) .setScope('https://www.googleapis.com/auth/business.manage') .setParam('access_type', 'offline') .setParam('prompt', 'consent') .setParam('login_hint', Session.getActiveUser().getEmail()) .setCache(CacheService.getUserCache()) .setRedirectUri( `https://script.google.com/macros/d/${scriptId}/usercallback` ); } function authCallback(request) { const service = getService(); const ok = service.handleCallback(request); return HtmlService.createHtmlOutput( ok ? 'Authorisation successful. You may close this tab.' : 'Authorisation denied.' ); } function getConsentHtml(service) { const url = service.getAuthorizationUrl(); return `

This widget needs permission to read your Google Business Profile reviews.

Authorise GBP Access

`; }