/** * MOREMINIMORE - Contact Form Backend (Google Apps Script) * Per plan 2026-06-13 round 2 #4: * Form submit → Apps Script doPost() → Google Sheet (log) * → Email to contact@moreminimore.com * → LINE Notify * * USER DEPLOYS THIS THEMSELVES (see README.md in same folder). * 1. Create new Apps Script project at https://script.google.com * 2. Paste this entire file into Code.gs * 3. Set Script Properties (Project Settings → Script Properties): * SHEET_ID — Google Sheet ID (from Sheet URL) * LINE_NOTIFY_TOKEN — from https://notify-bot.line.me * RECIPIENT_EMAIL — contact@moreminimore.com (or any email) * 4. Create Google Sheet with these headers in row 1: * timestamp | name | phone | email | service | message | variant | userAgent * 5. Deploy → New deployment → Type: Web app * Execute as: Me * Who has access: Anyone * Copy the deployment URL. * 6. Add to moreminimore-astroreal/.env: * PUBLIC_CONTACT_ENDPOINT= */ /* ------------------------------------------------------------------ */ /* CONFIG — read from Script Properties (set in Project Settings) */ /* ------------------------------------------------------------------ */ function getConfig() { const props = PropertiesService.getScriptProperties(); return { SHEET_ID: props.getProperty('SHEET_ID'), LINE_NOTIFY_TOKEN: props.getProperty('LINE_NOTIFY_TOKEN'), RECIPIENT_EMAIL: props.getProperty('RECIPIENT_EMAIL') || 'contact@moreminimore.com', }; } /* ------------------------------------------------------------------ */ /* HANDLER — POST /exec (or /dev) */ /* ------------------------------------------------------------------ */ /** * Handle form submission. Expects JSON body: * { * name, phone, email, service, message, variant, userAgent, submittedAt * } */ function doPost(e) { try { const data = JSON.parse(e.postData.contents); const config = getConfig(); // 1. Log to Google Sheet let rowId = null; if (config.SHEET_ID) { const sheet = SpreadsheetApp.openById(config.SHEET_ID).getActiveSheet(); const row = sheet.appendRow([ new Date(), // timestamp data.name || '', data.phone || '', data.email || '', data.service || '', data.message || '', data.variant || 'full', data.userAgent || '', ]); rowId = row.getRange().getRow(); } // 2. Send email const subject = `[moreminimore contact] ${data.service || 'general'} — ${data.name || data.phone || 'unknown'}`; const body = formatEmailBody(data); MailApp.sendEmail({ to: config.RECIPIENT_EMAIL, subject: subject, body: body, replyTo: data.email || undefined, }); // 3. Send LINE Notify if (config.LINE_NOTIFY_TOKEN) { const lineMessage = formatLineMessage(data); UrlFetchApp.fetch('https://notify-api.line.me/api/notify', { method: 'POST', headers: { 'Authorization': 'Bearer ' + config.LINE_NOTIFY_TOKEN, 'Content-Type': 'application/x-www-form-urlencoded', }, payload: { message: lineMessage }, muteHttpExceptions: true, }); } return ContentService .createTextOutput(JSON.stringify({ ok: true, id: rowId })) .setMimeType(ContentService.MimeType.JSON); } catch (err) { return ContentService .createTextOutput(JSON.stringify({ ok: false, error: err.toString() })) .setMimeType(ContentService.MimeType.JSON); } } /* ------------------------------------------------------------------ */ /* FORMATTERS */ /* ------------------------------------------------------------------ */ function formatEmailBody(data) { return [ 'New contact form submission', '', '---', 'Name: ' + (data.name || '-'), 'Phone: ' + (data.phone || '-'), 'Email: ' + (data.email || '-'), 'Service: ' + (data.service || '-'), 'Variant: ' + (data.variant || 'full'), '---', '', 'Message:', data.message || '(empty)', '', '---', 'Submitted: ' + (data.submittedAt || new Date().toISOString()), 'UserAgent: ' + (data.userAgent || 'unknown'), ].join('\n'); } function formatLineMessage(data) { const lines = [ '🔔 moreminimore contact', '', '👤 ' + (data.name || data.phone || 'unknown'), '📞 ' + (data.phone || '-'), '✉ ' + (data.email || '-'), '🎯 ' + (data.service || 'general'), ]; if (data.message) { lines.push(''); lines.push(data.message.length > 200 ? data.message.slice(0, 200) + '...' : data.message); } return lines.join('\n'); } /* ------------------------------------------------------------------ */ /* TEST (optional — call testDoPost() from Apps Script editor) */ /* ------------------------------------------------------------------ */ function testDoPost() { const mockEvent = { postData: { contents: JSON.stringify({ name: 'Test User', phone: '080-123-4567', email: 'test@example.com', service: 'webdev', message: 'This is a test message', variant: 'full', userAgent: 'test', submittedAt: new Date().toISOString(), }), }, }; Logger.log(doPost(mockEvent).getContent()); }