McRUN Race Code¶
About¶
McRUN Race Code is a Google Apps Script codebase that manages McGill Students Running Club (McRUN) race registrations, automates payment verification (online/Interac), and maintains robust data flows between Google Sheets and Gmail.
It streamlines the process of receiving, validating, and formatting race registrations, integrating with email and payment platforms for automation and oversight.
Purpose:
- Automate import, formatting, and verification of race registrations.
- Integrate payment confirmation (Zeffy, Stripe, Interac) with Gmail.
- Enable custom menu actions, triggers, and streamlined workflows for admins.
Files¶
- Github Repo: mcrace-code
- Google Sheets: McRUN Race Registrations Sheet
- Apps Script Project: McRUN Race Code Apps Script (Accessible via Extensions > Apps Script in the Google Sheet)
Key Features¶
- Custom Google Sheets menu for admin workflows.
- Import and process race registrations (manual or triggered).
- Automated payment verification via Gmail (Zeffy, Stripe, Interac).
- Time-based and event-based triggers for payment and registration processing.
- Advanced formatting (checkboxes, banding, phone/date formats).
- Robust logging and error handling for all critical operations.
Tools Used¶
- Google Apps Script (JavaScript)
- Google Sheets
- Gmail API (via Apps Script)
- Apps Script Triggers (time-based, onOpen, onChange)
- Zeffy/Stripe/Interac payment integration via email search
Function Docs¶
This section is divided by project file (alphabetical order).
Each file lists its functions and provides a detailed reference for each.
Note: Only a selection of functions may be shown below due to search result limits.
See all code/functions in GitHub
# Formatting.gs ¶
- removeDiacritics(str)β Remove diacritics (accents) from a string
- formatSpecificColumns()β Apply formatting to the registration sheet
## removeDiacritics(str) ¶
Removes diacritics (accents) from the given string.
| Name | Type | Description | 
|---|---|---|
| str | String | Input string to format | 
Output: String (normalized, accents removed)
## formatSpecificColumns() ¶
Applies formatting to the registration sheet: freezes panes, wraps text, sets alignment, phone/date formats, checkboxes, and banding.
Output: None (side effect: formats the sheet)
Pitfalls: Relies on sheet structure and column names.
# Import.gs ¶
- appendToImport(reg)β Append a registration to the import sheet
- processLastImport()β Process the last imported registration
- onChange(e)β Event handler for changes in the import sheet
## appendToImport(reg) ¶
Appends a registration object (stringified) to the import sheet.
| Name | Type | Description | 
|---|---|---|
| reg | String | Registration data string | 
Output: Integer (row number where appended)
Pitfalls: Import sheet must be accessible and not locked.
## processLastImport() ¶
Processes the last imported registration (parsing, registering, formatting, payment check).
Output: None
Pitfalls: Throws if the target row is invalid or missing.
## onChange(e) ¶
Triggered when a change occurs in the spreadsheet; processes new imports when rows are inserted.
| Name | Type | Description | 
|---|---|---|
| e | Object | Sheets event object | 
Output: None
Pitfalls: Only processes 'INSERT_ROW' changes on the import sheet.
# Inbox.gs ¶
- getGmailLabel(labelName)β Retrieve a Gmail label by name
- getGmailSearchString(sender, offset)β Build Gmail search string for sender/date
- cleanUpMatchedThread(thread, label)β Archive, mark read, and label Gmail thread
## getGmailLabel(labelName) ¶
Retrieves a Gmail label object by its name.
| Name | Type | Description | 
|---|---|---|
| labelName | String | Name of the Gmail label | 
Output: GmailLabel
Pitfalls: Throws if label does not exist.
## getGmailSearchString(sender, offset) ¶
Builds a Gmail search string for a sender and minimum date.
| Name | Type | Description | 
|---|---|---|
| sender | String | Sender email address | 
| offset | Integer | Time offset in ms to calculate minimum search date | 
Output: String (Gmail search query)
## cleanUpMatchedThread(thread, label) ¶
Marks a Gmail thread as read, archives it, and adds the provided label.
| Name | Type | Description | 
|---|---|---|
| thread | GmailThread | Gmail thread object | 
| label | GmailLabel | Gmail label object | 
Output: None
Pitfalls: Throws if thread or label is invalid.
# Menu.gs ¶
- onOpen()β Add custom menu to the sheet
- helpUI()β Show help message for menu
- confirmAndRunUserChoice(functionName, additionalMsg, funcArg)β Confirm and run a user-selected menu function
## onOpen() ¶
Creates a custom menu in the sheet UI for McRace admin actions.
Output: None
## helpUI() ¶
Displays a help dialog with info on menu actions and contact.
Output: None
## confirmAndRunUserChoice(functionName, additionalMsg, funcArg) ¶
Displays a confirmation dialog and runs the selected function with an optional argument.
| Name | Type | Description | 
|---|---|---|
| functionName | String | Name of function to execute | 
| additionalMsg | String | Custom message for confirmation (default: "") | 
| funcArg | String | Optional argument to pass (default: "") | 
Output: Return value of the executed function
# Payment.gs ¶
- checkPayment(member)β Check payment status for a member
- checkOnlinePayment(member)β Check for online payment
- checkInteracPayment(member)β Check for Interac payment
## checkPayment(member) ¶
Checks payment status for a member depending on their method.
| Name | Type | Description | 
|---|---|---|
| member | Object | Member info (name, email, paymentMethod) | 
Output: Boolean (true if paid)
Pitfalls: Method must be known ("CC" or "Interac").
## checkOnlinePayment(member) ¶
Checks for online payment (Zeffy, Stripe) via Gmail.
| Name | Type | Description | 
|---|---|---|
| member | Object | Member info | 
Output: Boolean (true if payment found)
## checkInteracPayment(member) ¶
Checks for Interac payment via Gmail.
| Name | Type | Description | 
|---|---|---|
| member | Object | Member info | 
Output: Boolean (true if payment found)
# Registration.gs ¶
- getLastRowInReg()β Get last non-empty registration row
- onNewRegistration({newRow, member})β Process new registration
- addNewRegistration(registrationObj)β Add new registration to sheet
## getLastRowInReg() ¶
Returns the last non-empty row in the registration sheet.
Output: Integer
## onNewRegistration({newRow, member}) ¶
Processes a new registration (extract payment, verify, format sheet).
| Name | Type | Description | 
|---|---|---|
| newRow | Int | Row where new data added | 
| member | Array | Formatted member data | 
Output: None
## addNewRegistration(registrationObj) ¶
Formats and adds a new registration to the sheet. Returns the new row and member data.
| Name | Type | Description | 
|---|---|---|
| registrationObj | Object | Registration data | 
Output: Object { newRow, member }
# Triggers.gs ¶
- createNewFeeTrigger(row, feeDetails)β Create a time-based trigger for fee check
- runFeeChecker()β Handler for time-based fee triggers
## createNewFeeTrigger(row, feeDetails) ¶
Creates a time-based trigger to check for a memberβs fee payment.
| Name | Type | Description | 
|---|---|---|
| row | Int | Registration row | 
| feeDetails | Object | Member's fee/payment info | 
Output: None
Pitfalls: Only works if Script Properties are writable.
## runFeeChecker() ¶
Processes all fee check triggers, verifying payments and sending notifications as needed.
Output: None
# Variables.gs ¶
- getCurrentUserEmail()β Returns current userβs email
## getCurrentUserEmail() ¶
Returns the email address of the current user executing the script.
Output: String (email address)
Triggers¶
Types of Triggers¶
- onOpen: Adds the custom admin menu for registrations and formatting.
- onChange: Processes new imports when rows are inserted (Import.gs).
- Time-based triggers:
- For fee/payment checking (Triggers.gs), periodically checking if payment has been received for a registration.
- Configured via createNewFeeTrigger(row, feeDetails).
Purpose:
- Automates admin workflows, payment verification, and keeps the registration sheet up-to-date with minimal manual intervention.
Troubleshooting & FAQ¶
| Issue/Error | Likely Cause | Solution | 
|---|---|---|
| "Failed to retrieve Gmail label." | Label does not exist | Create label in Gmail | 
| "Failed to append registration to import sheet." | Sheet locked, invalid, or missing | Check permissions and sheet names | 
| "Payment not found" | Email not received or not matched | Wait and retry; check sender and search terms | 
| "Error cleaning up Gmail thread." | Gmail API error | Verify permissions, thread existence | 
| Formatting is off | Sheet structure changed | Update column indices and formatting logic | 
See Also¶
- McRUN Attendance
- McRUN Points System
- Google Apps Script Triggers
- Google Sheets API
- McRUN Club GitHub
Last updated: 2025-06-12