meta-llama/llama-3.3-70b-instruct:freeดึงงานทั้งหมดจาก Google Calendar มาบันทึกใน Google Sheet เพื่อให้ AI สามารถอ่านข้อมูลได้ สามารถกดซ้ำได้โดยไม่มีข้อมูลซ้ำ
// ═══════════════════════════════════════════════════
// Google Apps Script — Event Organizer (Full Version)
// ═══════════════════════════════════════════════════
function doGet(e) {
var action = e.parameter.action;
var sheetId = e.parameter.sheetId;
try {
if (action === 'getEvents') return buildResponse({ status:'ok', events: getEventsFromSheet(sheetId) });
if (action === 'createEvent') return buildResponse(createCalendarAndSheetEvent({ event: JSON.parse(e.parameter.event), sheetId: sheetId, calendarName: e.parameter.calendarName||'primary' }));
if (action === 'importCalendar') return buildResponse(importCalendarToSheet(sheetId, parseInt(e.parameter.months)||12));
if (action === 'editEvent') return buildResponse(editEvent(sheetId, e.parameter.calEventId, JSON.parse(e.parameter.changes)));
if (action === 'deleteEvent') return buildResponse(deleteEvent(sheetId, e.parameter.calEventId));
return buildResponse({ status:'error', message:'Unknown action: '+action });
} catch(err) {
return buildResponse({ status:'error', message: err.message });
}
}
// SHEET COLUMNS (10 columns)
// 1:วันที่สร้าง 2:ชื่องาน 3:วันที่งาน 4:เวลาเริ่ม
// 5:วันที่สิ้นสุด 6:เวลาสิ้นสุด 7:สถานที่
// 8:รายละเอียด 9:งานย่อย 10:Calendar Event ID
function ensureHeader(sheet) {
if (sheet.getLastRow() === 0) {
sheet.appendRow(['วันที่สร้าง','ชื่องาน','วันที่งาน','เวลาเริ่ม',
'วันที่สิ้นสุด','เวลาสิ้นสุด','สถานที่',
'รายละเอียด','งานย่อย','Calendar Event ID']);
var h = sheet.getRange(1,1,1,10);
h.setFontWeight('bold');
h.setBackground('#2D6A4F');
h.setFontColor('#FFFFFF');
}
}
function getSheet(sheetId) {
return SpreadsheetApp.openById(sheetId).getActiveSheet();
}
function getCalendar(calName) {
if (!calName || calName === 'primary') return CalendarApp.getDefaultCalendar();
var cals = CalendarApp.getCalendarsByName(calName);
return cals.length > 0 ? cals[0] : CalendarApp.getDefaultCalendar();
}
// CREATE EVENT
function createCalendarAndSheetEvent(payload) {
var ev = payload.event;
var sheetId = payload.sheetId;
var calName = payload.calendarName || 'primary';
var calendar = getCalendar(calName);
var calEventId = '';
if (ev.date && ev.date !== 'null') {
var startDate, endDate, calEvent;
if (ev.isAllDay) {
startDate = new Date(ev.date + 'T00:00:00');
endDate = new Date((ev.endDate || ev.date) + 'T00:00:00');
endDate.setDate(endDate.getDate() + 1);
calEvent = calendar.createAllDayEvent(ev.title, startDate, endDate, {
description: (ev.description || ''),
location: (ev.location || '')
});
} else {
startDate = new Date(ev.date + 'T' + (ev.startTime || '09:00') + ':00');
endDate = new Date((ev.endDate || ev.date) + 'T' + (ev.endTime || ev.startTime || '10:00') + ':00');
if (endDate <= startDate) endDate.setHours(endDate.getHours() + 1);
var desc = (ev.description || '') + (ev.tasks && ev.tasks.length
? '\n\nงานย่อย:\n' + ev.tasks.map(function(t, i) { return (i+1) + '. ' + t; }).join('\n')
: '');
calEvent = calendar.createEvent(ev.title, startDate, endDate, {
description: desc.trim(),
location: (ev.location || '')
});
}
calEventId = calEvent.getId();
}
var sheet = getSheet(sheetId);
ensureHeader(sheet);
var now = Utilities.formatDate(new Date(), 'Asia/Bangkok', 'dd/MM/yyyy HH:mm');
sheet.appendRow([
now, ev.title || '', ev.date || '', ev.startTime || '',
ev.endDate || ev.date || '', ev.endTime || '',
ev.location || '', ev.description || '',
(ev.tasks || []).join(', '), calEventId
]);
return { status:'ok', message:'สร้างงานสำเร็จ', calendarEventId: calEventId };
}
// IMPORT FROM CALENDAR
function importCalendarToSheet(sheetId, monthsBack) {
var calendar = CalendarApp.getDefaultCalendar();
var startDate = new Date();
startDate.setMonth(startDate.getMonth() - (monthsBack || 12));
var endDate = new Date();
endDate.setMonth(endDate.getMonth() + 6);
var sheet = getSheet(sheetId);
ensureHeader(sheet);
var lastRow = sheet.getLastRow();
var existingIds = {};
if (lastRow > 1) {
var existingData = sheet.getRange(2, 10, lastRow - 1, 1).getValues();
for (var e = 0; e < existingData.length; e++) {
if (existingData[e][0]) existingIds[existingData[e][0]] = true;
}
}
var events = calendar.getEvents(startDate, endDate);
var added = 0;
var now = Utilities.formatDate(new Date(), 'Asia/Bangkok', 'dd/MM/yyyy HH:mm');
for (var j = 0; j < events.length; j++) {
var ev = events[j];
var id = ev.getId();
if (existingIds[id]) continue;
var isAllDay = ev.isAllDayEvent();
var start = ev.getStartTime();
var end = ev.getEndTime();
var startDateStr = Utilities.formatDate(start, 'Asia/Bangkok', 'yyyy-MM-dd');
var endDateStr = Utilities.formatDate(end, 'Asia/Bangkok', 'yyyy-MM-dd');
if (isAllDay) {
var endAdj = new Date(end.getTime() - 86400000);
endDateStr = Utilities.formatDate(endAdj, 'Asia/Bangkok', 'yyyy-MM-dd');
}
var startTime = isAllDay ? '' : Utilities.formatDate(start, 'Asia/Bangkok', 'HH:mm');
var endTime = isAllDay ? '' : Utilities.formatDate(end, 'Asia/Bangkok', 'HH:mm');
sheet.appendRow([
now, ev.getTitle(), startDateStr, startTime,
endDateStr, endTime,
ev.getLocation() || '', ev.getDescription() || '',
'', id
]);
existingIds[id] = true;
added++;
}
return { status:'ok', added: added, message:'นำเข้าสำเร็จ ' + added + ' รายการ' };
}
// EDIT EVENT
function editEvent(sheetId, calEventId, changes) {
var sheet = getSheet(sheetId);
var lastRow = sheet.getLastRow();
var rowIndex = -1;
var normalCalEventId = normalizeId(calEventId);
if (lastRow > 1) {
var ids = sheet.getRange(2, 10, lastRow - 1, 1).getValues();
for (var i = 0; i < ids.length; i++) {
if (normalizeId(ids[i][0]) === normalCalEventId) { rowIndex = i + 2; break; }
}
}
if (rowIndex === -1) return { status:'error', message:'ไม่พบงานใน Sheet (ID: ' + calEventId + ')' };
// Read ORIGINAL row BEFORE updating — use formatCellDate to get clean strings
var rawRow = sheet.getRange(rowIndex, 1, 1, 10).getValues()[0];
var origDate = formatCellDate(rawRow[2]);
var origStartTime = String(rawRow[3] || '');
var origEndDate = formatCellDate(rawRow[4]);
var origEndTime = String(rawRow[5] || '');
// Extract HH:MM from time fields in case they are Date objects
origStartTime = extractHHMM(origStartTime);
origEndTime = extractHHMM(origEndTime);
// Apply changes to row for Sheet update
var row = rawRow.slice();
if (changes.title) row[1] = changes.title;
if (changes.date) row[2] = changes.date;
if (changes.startTime) row[3] = changes.startTime;
if (changes.endDate) row[4] = changes.endDate;
if (changes.endTime) row[5] = changes.endTime;
if (changes.location) row[6] = changes.location;
if (changes.description) row[7] = changes.description;
sheet.getRange(rowIndex, 1, 1, 10).setValues([row]);
// Update Google Calendar using clean string values
if (calEventId) {
try {
var calEvent = CalendarApp.getDefaultCalendar().getEventById(calEventId);
if (!calEvent) {
var cals = CalendarApp.getAllCalendars();
for (var c = 0; c < cals.length; c++) {
calEvent = cals[c].getEventById(calEventId);
if (calEvent) break;
}
}
if (calEvent) {
if (changes.title) calEvent.setTitle(changes.title);
if (changes.location) calEvent.setLocation(changes.location);
if (changes.description) calEvent.setDescription(changes.description);
if (changes.date || changes.startTime || changes.endDate || changes.endTime) {
// Use changes first, fall back to ORIGINAL clean string values
var useDate = changes.date || origDate;
var useStartTime = changes.startTime || origStartTime || '09:00';
var useEndDate = changes.endDate || changes.date || origEndDate || useDate;
var useEndTime = changes.endTime || origEndTime || useStartTime;
// Validate date strings before building Date objects
if (useDate && useDate.match(/^\d{4}-\d{2}-\d{2}$/)) {
var newStart = new Date(useDate + 'T' + useStartTime + ':00');
var newEnd = new Date(useEndDate + 'T' + useEndTime + ':00');
if (isNaN(newStart.getTime()) || isNaN(newEnd.getTime())) {
// Invalid date — skip Calendar update to avoid deleting event
Logger.log('editEvent: invalid date constructed, skipping setTime');
} else {
if (newEnd <= newStart) newEnd.setHours(newEnd.getHours() + 1);
calEvent.setTime(newStart, newEnd);
}
}
}
}
} catch(e) {
Logger.log('editEvent Calendar error: ' + e.message);
// Sheet was updated successfully — Calendar update failed but event not deleted
}
}
return { status:'ok', message:'แก้ไขงานสำเร็จ' };
}
// Extract HH:MM from a time value that may be a full Date string or simple time string
function extractHHMM(val) {
if (!val) return '';
var s = String(val).trim();
if (!s || s === 'null') return '';
// Already HH:MM
var simple = s.match(/^(\d{1,2}):(\d{2})/);
if (simple) return simple[1].padStart(2,'0') + ':' + simple[2];
// Full date string — extract time portion
var full = s.match(/(\d{1,2}):(\d{2}):\d{2}/);
if (full) return full[1].padStart(2,'0') + ':' + full[2];
return '';
}
// DELETE EVENT
function deleteEvent(sheetId, calEventId) {
var sheet = getSheet(sheetId);
var lastRow = sheet.getLastRow();
var rowIndex = -1;
var normalDelId = normalizeId(calEventId);
if (lastRow > 1) {
var ids = sheet.getRange(2, 10, lastRow - 1, 1).getValues();
for (var i = 0; i < ids.length; i++) {
if (normalizeId(ids[i][0]) === normalDelId) { rowIndex = i + 2; break; }
}
}
if (rowIndex === -1) return { status:'error', message:'ไม่พบงานใน Sheet' };
sheet.deleteRow(rowIndex);
if (calEventId) {
try {
var calEvent = CalendarApp.getDefaultCalendar().getEventById(calEventId);
if (!calEvent) {
var cals = CalendarApp.getAllCalendars();
for (var c = 0; c < cals.length; c++) {
calEvent = cals[c].getEventById(calEventId);
if (calEvent) break;
}
}
if (calEvent) calEvent.deleteEvent();
} catch(e) { /* Calendar delete failed but sheet row was deleted */ }
}
return { status:'ok', message:'ลบงานสำเร็จ' };
}
// GET EVENTS
function getEventsFromSheet(sheetId) {
if (!sheetId) return [];
try {
var sheet = getSheet(sheetId);
var lastRow = sheet.getLastRow();
if (lastRow <= 1) return [];
var data = sheet.getRange(2, 1, lastRow - 1, 10).getValues();
var results = [];
for (var i = 0; i < data.length; i++) {
var row = data[i];
if (row[1]) {
results.push({
createdAt: formatCellDate(row[0]),
title: String(row[1] || ''),
date: formatCellDate(row[2]),
startTime: String(row[3] || ''),
endDate: formatCellDate(row[4]),
endTime: String(row[5] || ''),
location: String(row[6] || ''),
description:String(row[7] || ''),
tasks: row[8] ? String(row[8]).split(', ') : [],
calEventId: String(row[9] || '').trim()
});
}
}
return results;
} catch(e) { return []; }
}
// Safely format a cell value that may be a Date object or a string as YYYY-MM-DD
function formatCellDate(val) {
if (!val) return '';
if (val instanceof Date) {
return Utilities.formatDate(val, 'Asia/Bangkok', 'yyyy-MM-dd');
}
return String(val);
}
// Normalize Calendar Event ID — strip @google.com or any @domain suffix for comparison
function normalizeId(id) {
if (!id) return '';
return String(id).split('@')[0].trim();
}
function buildResponse(data) {
return ContentService.createTextOutput(JSON.stringify(data))
.setMimeType(ContentService.MimeType.JSON);
}
…/spreadsheets/d/ID-ตรงนี้/editsk-or-v1-… มาวางในช่อง "API Key"openrouter/free (ค่าเริ่มต้น)