Skip to content

Commit

Permalink
V41
Browse files Browse the repository at this point in the history
  • Loading branch information
bmos committed Mar 13, 2024
1 parent 45ba876 commit 625f483
Show file tree
Hide file tree
Showing 9 changed files with 429 additions and 417 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ This Google Apps Script code simplifies some more labor-intensive Action Network

## Script Properties
To use these features, you must configure [Script Properties](https://developers.google.com/apps-script/reference/properties) in your Google Apps script.
- AN_API_KEY: Your Action Network API Key.
- AN_API_KEY: Your Action Network API Key (or a comma-separated list if you have multiple groups)
- AN_EMAIL_REPLY_TO: The email you want generated AN email drafts to use as the reply-to address.
- AN_EMAIL_SENDER: The name you want displayed as the sender for generated AN email.
- AN_EMAIL_WRAPPER: (OPTIONAL) The wrapper you want to use for the generated AN email drafts.
Expand Down
87 changes: 43 additions & 44 deletions action-network-event-data.gs
Original file line number Diff line number Diff line change
@@ -1,68 +1,67 @@
// This function gets the start time of an event, based on the event's location time zone.
const getStartTime = (event) => {
const start_date = new Date(event.start_date);
const output_date = new Date(start_date.toUTCString() + ' ' + dstOffset(start_date));
return output_date;
const start_date = new Date(event.start_date);
return new Date(start_date.toUTCString() + ' ' + dstOffset(start_date));
};

// This function gets the end time of an event, based on the event's location time zone.
const getEndTime = (event) => {
const start_date = getStartTime(event);
const end_date = new Date(event.end_date);
let output_date = new Date(end_date.toUTCString() + ' ' + dstOffset(end_date))
if (isNaN(output_date.getUTCFullYear())) {
output_date = new Date(start_date.getTime() + (60 * 1000 * default_length_mins))
}
return output_date;
const start_date = getStartTime(event);
const end_date = new Date(event.end_date);
let output_date = new Date(end_date.toUTCString() + ' ' + dstOffset(end_date))
if (isNaN(output_date.getUTCFullYear())) {
output_date = new Date(start_date.getTime() + (60 * 1000 * default_length_mins))
}
return output_date;
};

// This function returns the requested event ID if it is found in the Action Network event data.
const getEventIDFromAN = (contentJSON, search_id) => {
const identifiers = contentJSON.identifiers;
const search_id_full = `${search_id}:[^,]*`;
const identifiers = contentJSON.identifiers;
const search_id_full = `${search_id}:[^,]*`;

const regex_id = new RegExp(search_id_full)
.exec(identifiers);
if (regex_id === null) {
Logger.log(`${search_id} not found in Action Network event identifiers.`);
return null;
}
const regex_id = new RegExp(search_id_full)
.exec(identifiers);
if (regex_id === null) {
Logger.log(`${search_id} not found in Action Network event identifiers.`);
return null;
}

const found_id = regex_id[0].substring(search_id_full.indexOf('['));
Logger.log(`${search_id} found in Action Network event identifiers: ${found_id}`);
const found_id = regex_id[0].substring(search_id_full.indexOf('['));
Logger.log(`${search_id} found in Action Network event identifiers: ${found_id}`);

return found_id;
return found_id;
};

// This function returns all event data for an event ID from Action Network.
const getAllANEventData = (event_url) => {
return JSON.parse(UrlFetchApp.fetch(event_url, standard_api_params))
const getAllANEventData = (event_url, api_key) => {
return JSON.parse(UrlFetchApp.fetch(event_url, standard_api_params(api_key)))
};

// This function tags an Action Network event with the Google ID for its corresponding Google Calendar event
const tagANEvent = async (action_network_id, google_id) => {
// Check if the "AN_API_KEY" property is null
if (scriptProperties.getProperty("AN_API_KEY") === null) {
Logger.log('No Action Network API Key "AN_API_KEY" provided, cannot continue.');
return;
}
const tagANEvent = (action_network_id, google_id, api_key) => {
// Check if the "AN_API_KEY" property is null
if (api_key === null) {
Logger.log('No Action Network API Key "AN_API_KEY" provided, cannot continue.');
return;
}

Logger.log(`Tagging Action Network event ${action_network_id} with Google Calendar event ID ${google_id}`);
Logger.log(`Tagging Action Network event ${action_network_id} with Google Calendar event ID ${google_id}`);

// Create a payload for the PUT request to Action Network, adding the Google ID as an identifier on the event
const payload = JSON.stringify({
"identifiers": [`google_id:${google_id}`]
});
// Create a payload for the PUT request to Action Network, adding the Google ID as an identifier on the event
const payload = JSON.stringify({
"identifiers": [`google_id:${google_id}`]
});

// Set the options for the request and send it to Action Network, logging the response
const options = {
method: "put",
payload: payload,
headers: {
'Content-Type': 'application/json',
'OSDI-API-Token': scriptProperties.getProperty("AN_API_KEY")
}
};
// Set the options for the request and send it to Action Network, logging the response
const options = {
method: "put",
payload: payload,
headers: {
'Content-Type': 'application/json',
'OSDI-API-Token': api_key
}
};

UrlFetchApp.fetch(apiUrlAn + `events/${action_network_id}`, options);
UrlFetchApp.fetch(apiUrlAn + `events/${action_network_id}`, options);
};
62 changes: 32 additions & 30 deletions action-network-event-ids.gs
Original file line number Diff line number Diff line change
@@ -1,48 +1,50 @@
// This function returns event IDs from Action Network. If a filter is provided, it appends it to the API URL.
const getANEventIDs = (filter) => {
let url = `${apiUrlAn}events/`;
if (filter != null) {
Logger.log(`Finding upcoming events via filter query ${filter}.`);
url += filter;
}
const content = UrlFetchApp.fetch(url, standard_api_params);
return JSON.parse(content)["_links"]["osdi:events"];
const getANEventIDs = (filter, api_key) => {
let url = `${apiUrlAn}events/`;
if (filter != null) {
Logger.log(`Finding upcoming events via filter query ${filter}.`);
url += filter;
}
const content = UrlFetchApp.fetch(url, standard_api_params(api_key));
return JSON.parse(content)["_links"]["osdi:events"];
};

// This function sorts event IDs by date, based on the start time of the event.
// It is used by the getSortedANEventIDs function to sort the event IDs by the soonest event first.
const sortByDate = (a, b) => {
const startTimeA = getStartTime(getAllANEventData(a.href));
const startTimeB = getStartTime(getAllANEventData(b.href));
return startTimeA - startTimeB;
const startTimeA = getStartTime(a);
const startTimeB = getStartTime(b);
return startTimeA - startTimeB;
}

// This function returns upcoming event IDs from Action Network, sorted by the soonest event first.
// If a filter is provided, it appends it to the API URL.
const getSortedUpcomingANEventIDs = (extrafilters) => {
let filter = `?filter=start_date gt '${Utilities.formatDate(new Date(), "UTC", "yyyy-MM-dd")}'`;
if (extrafilters != null) {
for (let i = 0; i < extrafilters.length; i++) {
if (extrafilters[i] != null) {
filter += extrafilters[i];
}
const getSortedUpcomingANEventIDs = (extrafilters, api_key) => {
let filter = `?filter=start_date gt '${Utilities.formatDate(new Date(), "UTC", "yyyy-MM-dd")}'`;

if (extrafilters) {
extrafilters.forEach(extrafilter => {
if (extrafilter) {
filter += extrafilter;
}
});
}
}

const eventsByCreation = getANEventIDs(filter)
Logger.log("Sorting " + eventsByCreation.length + " Events By Soonest")
const eventsByCreation = getANEventIDs(filter, api_key).map(event => getAllANEventData(event.href, api_key));
console.log("Sorting " + eventsByCreation.length + " Events By Soonest");

return eventsByCreation.sort(sortByDate)
return eventsByCreation.sort(sortByDate);
};

// This function returns event IDs from Action Network for events modified since a certain number of days ago that have not started yet.
// It calculates the date to filter events by based on the current date and the number of days ago.
const getRecentlyModifiedEventIDs = (daysago) => {
const MILLIS_PER_DAY = 1000 * 60 * 60 * 24;
const now = new Date();
const lastWeek = new Date(now.getTime() - (MILLIS_PER_DAY * daysago));
const filter_date = Utilities.formatDate(lastWeek, "UTC", "yyyy-MM-dd");
return getANEventIDs(
`?filter=modified_date gt '${filter_date}' and start_date gt '${Utilities.formatDate(new Date(), "UTC", "yyyy-MM-dd")}'`
);
const getRecentlyModifiedEventIDs = (daysago, api_key) => {
const MILLIS_PER_DAY = 1000 * 60 * 60 * 24;
const now = new Date();
const lastWeek = new Date(now.getTime() - (MILLIS_PER_DAY * daysago));
const filter_date = Utilities.formatDate(lastWeek, "UTC", "yyyy-MM-dd");
return getANEventIDs(
`?filter=modified_date gt '${filter_date}' and start_date gt '${Utilities.formatDate(new Date(), "UTC", "yyyy-MM-dd")}'`,
api_key
);
};
115 changes: 57 additions & 58 deletions action-network-message-writer.gs
Original file line number Diff line number Diff line change
@@ -1,63 +1,62 @@
// Creates a new Action Network Email Message with the provided HTML-formatted text as the body.
// Values for Subject, Sender, Reply-To, Origin System, and Wrapper are obtained via defined Script Properties.
const draftANMessage = (doc) => {
const apiKey = scriptProperties.getProperty("AN_API_KEY");
const emailReplyTo = scriptProperties.getProperty("AN_EMAIL_REPLY_TO");
const emailSender = scriptProperties.getProperty("AN_EMAIL_SENDER");
const emailWrapper = scriptProperties.getProperty("AN_EMAIL_WRAPPER");
const emailCreator = scriptProperties.getProperty("AN_EMAIL_CREATOR");
const emailTarget = scriptProperties.getProperty("EMAIL_TARGET");
const emailSubject = scriptProperties.getProperty("EMAIL_SUBJECT");

if (!apiKey) {
Logger.log('No Action Network Api Key "AN_API_KEY" provided, cannot continue.');
return;
}
if (!emailReplyTo) {
Logger.log('No Email Reply-To Address "AN_EMAIL_REPLY_TO" provided, cannot continue.');
return;
}
if (!emailSubject) {
Logger.log('No Email Subject "EMAIL_SUBJECT" provided, cannot continue.');
return;
}

const subject = `🌹 ${emailSubject} for ${Utilities.formatDate(new Date(), "UTC", "yyyy-MM-dd")} 🌹`;

const payload = {
subject,
body: doc,
from: emailSender,
origin_system: "ActionNetworkEventSync",
reply_to: emailReplyTo,
_links: {}
};

payload.targets = emailTarget ? [{
href: `${apiUrlAn}queries/${emailTarget}`
}] :
undefined;

payload._links["osdi:wrapper"] = emailWrapper ? {
href: `${apiUrlAn}wrappers/${emailWrapper}`
} :
undefined;

payload._links["osdi:creator"] = emailCreator ? {
href: `${apiUrlAn}people/${emailCreator}`
} :
undefined;

const options = {
method: "post",
payload: JSON.stringify(payload),
headers: {
'Content-Type': 'application/json',
'OSDI-API-Token': apiKey
const draftANMessage = (doc, apiKey) => {
const emailReplyTo = scriptProperties.getProperty("AN_EMAIL_REPLY_TO");
const emailSender = scriptProperties.getProperty("AN_EMAIL_SENDER");
const emailWrapper = scriptProperties.getProperty("AN_EMAIL_WRAPPER");
const emailCreator = scriptProperties.getProperty("AN_EMAIL_CREATOR");
const emailTarget = scriptProperties.getProperty("EMAIL_TARGET");
const emailSubject = scriptProperties.getProperty("EMAIL_SUBJECT");

if (!apiKey) {
Logger.log('No Action Network Api Key "AN_API_KEY" provided, cannot continue.');
return;
}
if (!emailReplyTo) {
Logger.log('No Email Reply-To Address "AN_EMAIL_REPLY_TO" provided, cannot continue.');
return;
}
if (!emailSubject) {
Logger.log('No Email Subject "EMAIL_SUBJECT" provided, cannot continue.');
return;
}
};

const response = UrlFetchApp.fetch(`${apiUrlAn}messages/`, options);
const actionNetworkId = getEventIDFromAN(JSON.parse(response), "action_network");
Logger.log(`Created Action Network Message ${actionNetworkId} with subject ${subject}.`);
const subject = `🌹 ${emailSubject} for ${Utilities.formatDate(new Date(), "UTC", "yyyy-MM-dd")} 🌹`;

const payload = {
subject,
body: doc,
from: emailSender,
origin_system: "ActionNetworkEventSync",
reply_to: emailReplyTo,
_links: {}
};

payload.targets = emailTarget ? [{
href: `${apiUrlAn}queries/${emailTarget}`
}] :
undefined;

payload._links["osdi:wrapper"] = emailWrapper ? {
href: `${apiUrlAn}wrappers/${emailWrapper}`
} :
undefined;

payload._links["osdi:creator"] = emailCreator ? {
href: `${apiUrlAn}people/${emailCreator}`
} :
undefined;

const options = {
method: "post",
payload: JSON.stringify(payload),
headers: {
'Content-Type': 'application/json',
'OSDI-API-Token': apiKey
}
};

const response = UrlFetchApp.fetch(`${apiUrlAn}messages/`, options);
const actionNetworkId = getEventIDFromAN(JSON.parse(response), "action_network");
Logger.log(`Created Action Network Message ${actionNetworkId} with subject ${subject}.`);
};
Loading

0 comments on commit 625f483

Please sign in to comment.