|
/** |
|
* GroupMe Event Management Script - Main File |
|
* |
|
* This script manages events for GroupMe using their API. |
|
* It can create new events and update existing ones based on spreadsheet data. |
|
*/ |
|
|
|
// Global configuration object |
|
const CONFIG = { |
|
GROUP_ID: PropertiesService.getScriptProperties().getProperty('GROUP_ID') || '', |
|
ACCESS_TOKEN: PropertiesService.getScriptProperties().getProperty('ACCESS_TOKEN') || '', |
|
SHEET_NAME: PropertiesService.getScriptProperties().getProperty('SHEET_NAME') || '', |
|
// Default values for the API |
|
DEFAULTS: { |
|
is_all_day: false, |
|
timezone: "America/Indianapolis", |
|
scheduled_call: false |
|
} |
|
}; |
|
|
|
/** |
|
* Add custom menu to the spreadsheet when it opens |
|
*/ |
|
function onOpen() { |
|
const ui = SpreadsheetApp.getUi(); |
|
ui.createMenu('GroupMe Events') |
|
.addItem('Configuration', 'showConfigDialog') |
|
.addItem('Sync Events', 'syncEvents') |
|
.addSeparator() |
|
.addItem('Delete All Events', 'confirmDeleteAllEvents') |
|
.addToUi(); |
|
} |
|
|
|
/** |
|
* Main function to sync events with GroupMe |
|
*/ |
|
function syncEvents() { |
|
// Create a lock to prevent concurrent executions |
|
const lock = LockService.getScriptLock(); |
|
try { |
|
// Try to obtain a lock with a 10-second timeout |
|
if (!lock.tryLock(10000)) { |
|
return; |
|
} |
|
|
|
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(CONFIG.SHEET_NAME); |
|
if (!sheet) { |
|
return; |
|
} |
|
|
|
// Get all data |
|
const range = sheet.getDataRange(); |
|
const values = range.getValues(); |
|
|
|
// Check header row and find column indexes |
|
const headers = values[0]; |
|
const colIndexes = { |
|
date: headers.indexOf('Date'), |
|
startTime: headers.indexOf('Start Time'), |
|
endTime: headers.indexOf('End Time'), |
|
name: headers.indexOf('Name'), |
|
pitch: headers.indexOf('Pitch'), |
|
locationName: headers.indexOf('Location Name'), |
|
locationAddress: headers.indexOf('Location Address'), |
|
reminders: headers.indexOf('Reminders'), |
|
eventId: headers.indexOf('Event ID'), |
|
status: headers.indexOf('Status'), |
|
hash: headers.indexOf('Hash') |
|
}; |
|
|
|
// Verify required columns exist |
|
const requiredColumns = ['Date', 'Start Time', 'End Time', 'Name', 'Pitch', 'Location Name', 'Location Address', 'Event ID', 'Status', 'Hash']; |
|
const missingColumns = requiredColumns.filter(col => { |
|
const propName = col.toLowerCase().replace(' ', ''); |
|
return colIndexes[propName] === -1; |
|
}); |
|
|
|
if (missingColumns.length > 0) { |
|
return; |
|
} |
|
|
|
// Count rows to process (skip header) |
|
const rowsToProcess = values.filter((row, index) => |
|
index > 0 && row[colIndexes.name] && row[colIndexes.date] |
|
).length; |
|
|
|
if (rowsToProcess === 0) { |
|
return; |
|
} |
|
|
|
// Process each row (skip header) |
|
let processed = 0; |
|
let created = 0; |
|
let updated = 0; |
|
let skipped = 0; |
|
let errors = 0; |
|
|
|
for (let i = 1; i < values.length; i++) { |
|
const row = values[i]; |
|
|
|
// Skip empty rows |
|
if (!row[colIndexes.name] || !row[colIndexes.date]) continue; |
|
|
|
try { |
|
// Get data from row |
|
const eventData = { |
|
name: row[colIndexes.name], |
|
description: row[colIndexes.pitch] || '', |
|
location: { |
|
name: row[colIndexes.locationName] || '', |
|
address: row[colIndexes.locationAddress] || '' |
|
}, |
|
is_all_day: CONFIG.DEFAULTS.is_all_day, |
|
timezone: CONFIG.DEFAULTS.timezone, |
|
reminders: processReminders(row[colIndexes.reminders]), |
|
scheduled_call: CONFIG.DEFAULTS.scheduled_call |
|
}; |
|
|
|
// Format dates |
|
const date = row[colIndexes.date]; |
|
const startTime = row[colIndexes.startTime]; |
|
const endTime = row[colIndexes.endTime]; |
|
|
|
if (date && startTime && endTime) { |
|
// Format as ISO strings with the correct timezone offset |
|
eventData.start_at = formatDateTimeForGroupMe(date, startTime); |
|
eventData.end_at = formatDateTimeForGroupMe(date, endTime); |
|
} else { |
|
// Skip rows with incomplete date/time information |
|
sheet.getRange(i + 1, colIndexes.status + 1).setValue("ERROR: Missing date/time"); |
|
errors++; |
|
continue; |
|
} |
|
|
|
// Calculate a hash for the current event data to track changes |
|
const currentHash = calculateEventHash(eventData); |
|
const previousHash = row[colIndexes.hash] || ''; |
|
const eventId = row[colIndexes.eventId]; |
|
let result; |
|
|
|
// Check if this is a new event or an existing one that has changed |
|
if (!eventId || eventId.trim() === '') { |
|
// New event - create it |
|
result = createGroupMeEvent(eventData); |
|
|
|
if (result && result.response && result.response.event && result.response.event.event_id) { |
|
sheet.getRange(i + 1, colIndexes.eventId + 1).setValue(result.response.event.event_id); |
|
sheet.getRange(i + 1, colIndexes.status + 1).setValue(`Created: ${new Date().toLocaleString()}`); |
|
sheet.getRange(i + 1, colIndexes.hash + 1).setValue(currentHash); |
|
created++; |
|
} else { |
|
errors++; |
|
} |
|
} else if (currentHash !== previousHash) { |
|
// Existing event that has changed - update it |
|
result = updateGroupMeEvent(eventId, eventData); |
|
|
|
if (result && !result.error) { |
|
sheet.getRange(i + 1, colIndexes.status + 1).setValue(`Updated: ${new Date().toLocaleString()}`); |
|
sheet.getRange(i + 1, colIndexes.hash + 1).setValue(currentHash); |
|
updated++; |
|
} else { |
|
errors++; |
|
} |
|
} else { |
|
// No changes detected - skip update |
|
sheet.getRange(i + 1, colIndexes.status + 1).setValue(`No changes: ${new Date().toLocaleString()}`); |
|
skipped++; |
|
} |
|
|
|
// Log any errors |
|
if (result && result.error) { |
|
sheet.getRange(i + 1, colIndexes.status + 1).setValue(`ERROR: ${result.error}`); |
|
errors++; |
|
} |
|
|
|
// Small delay to avoid rate limits |
|
Utilities.sleep(500); |
|
|
|
} catch (error) { |
|
sheet.getRange(i + 1, colIndexes.status + 1).setValue(`ERROR: ${error.toString()}`); |
|
errors++; |
|
} |
|
} |
|
|
|
// Show summary |
|
SpreadsheetApp.getUi().alert( |
|
'Sync Complete', |
|
`Created: ${created}, Updated: ${updated}, Skipped: ${skipped}, Errors: ${errors}`, |
|
SpreadsheetApp.getUi().ButtonSet.OK |
|
); |
|
|
|
} catch (error) { |
|
// Handle any unexpected errors |
|
SpreadsheetApp.getUi().alert( |
|
'Error during sync', |
|
error.toString(), |
|
SpreadsheetApp.getUi().ButtonSet.OK |
|
); |
|
Logger.log(`Sync error: ${error.toString()}`); |
|
} finally { |
|
// Always release the lock |
|
lock.releaseLock(); |
|
} |
|
} |