Skip to content

Commit

Permalink
✨ feature: get user timezone from google calendar (#111)
Browse files Browse the repository at this point in the history
See issues: 111
  • Loading branch information
lucasvtiradentes committed Jan 28, 2024
1 parent 4a2e142 commit aa0346d
Show file tree
Hide file tree
Showing 14 changed files with 155 additions and 70 deletions.
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ function getConfigs() {
const configs = {
settings: {
sync_function: 'sync', // function name to run every x minutes
timezone_correction: -3, // hour difference from your timezone to utc timezone | https://www.utctime.net/
timezone_offset_correction: 0, // hour correction to match maybe a daylight saving difference (if you want the events 1 hour "before", then put -1)
update_frequency: 5, // wait time between sync checks (must be multiple of 5: 10, 15, etc)
skip_mode: false, // if set to true, it will skip every sync (useful for not messing up your data if any bug occurs repeatedly)
per_day_emails: {
Expand Down Expand Up @@ -378,8 +378,6 @@ if you want to change the google event color, you can choose from 12 options:

### General tips

- if you don't want to sync ticktick tasks, remove the `ticktick_sync` object on the configs;
- if you don't want to sync github commits, remove the `github_sync` object on the configs;
- in case of deleted ticktick tasks (that means, you dont intend to do it anymore) that are in gcal, make sure to delete in gcal as well. If not, they will be moved to its corresponding completed calendar;
- it is not necessary to generate a github token in order to sync commits, it is only required if you want to sync your contributions to private repos as well;
- every update in ticktick may take 5 minutes to propagate to its ics calendars;
Expand Down
74 changes: 55 additions & 19 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
name: 'gcal-sync',
github_repository: 'lucasvtiradentes/gcal-sync',
version: '1.10.0',
build_date_time: '25/01/2024 22:36:15'
build_date_time: '28/01/2024 18:56:48'
};

const mergeArraysOfArrays = (arr) => arr.reduce((acc, val) => acc.concat(val), []);
Expand Down Expand Up @@ -318,6 +318,30 @@
const specifiedStamp = Number(timeArr[0]) * 60 + Number(timeArr[1]);
return curStamp >= specifiedStamp;
}
function getCurrentDateInSpecifiedTimezone(timeZone) {
const date = new Date();
const formatter = new Intl.DateTimeFormat('en-CA', {
timeZone: timeZone,
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
});
const parts = formatter.formatToParts(date);
const findPart = (type) => parts.find((part) => part.type === type).value;
const isoDate = `${findPart('year')}-${findPart('month')}-${findPart('day')}T${findPart('hour')}:${findPart('minute')}:${findPart('second')}.000`;
return isoDate;
}
function getTimezoneOffset(timezone) {
const date = new Date();
const utcDate = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds()));
const tzDate = new Date(date.toLocaleString('en-US', { timeZone: timezone }));
const offset = (Number(tzDate) - Number(utcDate)) / (1000 * 60 * 60);
return offset;
}

function getTodayStats() {
const todayStats = {
Expand Down Expand Up @@ -379,7 +403,7 @@
const sessionEmail = getSessionEmail(userEmail, sessionData);
sendEmail(sessionEmail);
}
const isNowTimeAfterDailyEmails = isCurrentTimeAfter(extendedConfigs.configs.settings.per_day_emails.time_to_send, extendedConfigs.configs.settings.timezone_correction);
const isNowTimeAfterDailyEmails = isCurrentTimeAfter(extendedConfigs.configs.settings.per_day_emails.time_to_send, extendedConfigs.timezone_offset);
const alreadySentTodaySummaryEmail = extendedConfigs.today_date === getGASProperty(GAS_PROPERTIES_ENUM.last_daily_email_sent_date);
if (isNowTimeAfterDailyEmails && extendedConfigs.configs.settings.per_day_emails.email_daily_summary && !alreadySentTodaySummaryEmail) {
updateGASProperty(GAS_PROPERTIES_ENUM.last_daily_email_sent_date, extendedConfigs.today_date);
Expand Down Expand Up @@ -549,6 +573,10 @@
return curString;
}

// =============================================================================
const getCurrentTimezoneFromGoogleCalendar = () => {
return CalendarApp.getDefaultCalendar().getTimeZone();
};
const createMissingCalendars = (allGcalendarsNames) => {
let createdCalendar = false;
allGcalendarsNames.forEach((calName) => {
Expand Down Expand Up @@ -657,6 +685,7 @@
return filteredRepos;
}
function syncGithub(configs) {
logger.info(`syncing github commits`);
const info = {
githubCommits: getAllGithubCommits(configs[githubConfigsKey].username, configs[githubConfigsKey].personal_token),
githubGcalCommits: getTasksFromGoogleCalendars([configs[githubConfigsKey].commits_configs.commits_calendar])
Expand Down Expand Up @@ -802,7 +831,7 @@
return newStr.slice(0, newStr.search(substr2));
};

const getIcsCalendarTasks = (icsLink, timezoneCorrection) => {
const getIcsCalendarTasks = (icsLink, timezone_offset) => {
const parsedLink = icsLink.replace('webcal://', 'https://');
const urlResponse = UrlFetchApp.fetch(parsedLink, { validateHttpsCertificates: false, muteHttpExceptions: true });
const data = urlResponse.getContentText() || '';
Expand Down Expand Up @@ -833,7 +862,7 @@
return [...acc, eventObj];
}, []);
const allEventsParsedArr = allEventsArr.map((item) => {
const parsedDateTime = getParsedIcsDatetimes(item.DTSTART, item.DTEND, item.TZID, timezoneCorrection);
const parsedDateTime = getParsedIcsDatetimes(item.DTSTART, item.DTEND, item.TZID, timezone_offset);
return {
id: item.UID,
name: item.SUMMARY,
Expand All @@ -845,7 +874,7 @@
});
return allEventsParsedArr;
};
function getParsedIcsDatetimes(dtstart, dtend, timezone, timezoneCorrection) {
function getParsedIcsDatetimes(dtstart, dtend, timezone, timezone_offset) {
let finalDtstart = dtstart;
let finalDtend = dtend;
finalDtstart = finalDtstart.slice(finalDtstart.search(':') + 1);
Expand All @@ -866,7 +895,7 @@
}
return `${fixer < 0 ? '-' : '+'}${String(Math.abs(fixer)).padStart(2, '0')}:00`;
};
const timezoneFixedString = getTimeZoneFixedString(timezoneCorrection);
const timezoneFixedString = getTimeZoneFixedString(timezone_offset);
finalDtstart = {
dateTime: `${startDateObj.year}-${startDateObj.month}-${startDateObj.day}T${startDateObj.hours}:${startDateObj.minutes}:${startDateObj.seconds}${timezoneFixedString}`,
timeZone: timezone
Expand All @@ -882,10 +911,11 @@
};
}

function syncTicktick(configs) {
const icsCalendarsConfigs = configs[ticktickConfigsKey].ics_calendars;
function syncTicktick(extendedConfigs) {
logger.info(`syncing ticktick tasks`);
const icsCalendarsConfigs = extendedConfigs.configs[ticktickConfigsKey].ics_calendars;
const info = {
ticktickTasks: getAllTicktickTasks(icsCalendarsConfigs, configs.settings.timezone_correction),
ticktickTasks: getAllTicktickTasks(icsCalendarsConfigs, extendedConfigs.timezone_offset),
ticktickGcalTasks: getTasksFromGoogleCalendars([...new Set(icsCalendarsConfigs.map((item) => item.gcal))])
};
const resultInfo = Object.assign(Object.assign({}, addAndUpdateTasksOnGcal(info)), moveCompletedTasksToDoneGcal(info));
Expand Down Expand Up @@ -952,23 +982,23 @@
];
return resultArr.filter((item) => item.hasChanged).map((item) => item.field);
}
function getTicktickTasks(icsCalendarsArr, timezoneCorrection) {
function getTicktickTasks(icsCalendarsArr, timezone_offset) {
const extendedTasks = [];
for (const icsCal of icsCalendarsArr) {
const tasks = getIcsCalendarTasks(icsCal.link, timezoneCorrection);
const tasks = getIcsCalendarTasks(icsCal.link, timezone_offset);
const extendedItem = tasks.map((item) => (Object.assign(Object.assign({}, item), icsCal)));
extendedTasks.push(extendedItem);
}
return mergeArraysOfArrays(extendedTasks);
}
function getAllTicktickTasks(icsCalendars, timezoneCorrection) {
const taggedTasks = getTicktickTasks(icsCalendars.filter((icsCal) => icsCal.tag), timezoneCorrection);
const ignoredTaggedTasks = getTicktickTasks(icsCalendars.filter((icsCal) => icsCal.ignoredTags), timezoneCorrection).filter((item) => {
function getAllTicktickTasks(icsCalendars, timezone_offset) {
const taggedTasks = getTicktickTasks(icsCalendars.filter((icsCal) => icsCal.tag), timezone_offset);
const ignoredTaggedTasks = getTicktickTasks(icsCalendars.filter((icsCal) => icsCal.ignoredTags), timezone_offset).filter((item) => {
const ignoredTasks = taggedTasks.map((it) => `${it.tag}${it.id}`);
const shouldIgnoreTask = item.ignoredTags.some((ignoredTag) => ignoredTasks.includes(`${ignoredTag}${item.id}`));
return shouldIgnoreTask === false;
});
const commonTasks = getTicktickTasks(icsCalendars.filter((icsCal) => !icsCal.tag && !icsCal.ignoredTags), timezoneCorrection);
const commonTasks = getTicktickTasks(icsCalendars.filter((icsCal) => !icsCal.tag && !icsCal.ignoredTags), timezone_offset);
return [...taggedTasks, ...ignoredTaggedTasks, ...commonTasks];
}
function addAndUpdateTasksOnGcal({ ticktickGcalTasks, ticktickTasks }) {
Expand Down Expand Up @@ -1067,7 +1097,7 @@
settings: {
sync_function: '',
skip_mode: false,
timezone_correction: -3,
timezone_offset_correction: 0,
update_frequency: 4,
per_day_emails: {
time_to_send: '15:00',
Expand Down Expand Up @@ -1126,6 +1156,8 @@
class GcalSync {
constructor(configs) {
this.extended_configs = {
timezone: '',
timezone_offset: 0,
today_date: '',
user_email: '',
configs: {}
Expand All @@ -1136,8 +1168,12 @@
if (!isRunningOnGAS()) {
throw new Error(ERRORS.production_only);
}
const timezone = getCurrentTimezoneFromGoogleCalendar();
this.extended_configs.timezone = timezone;
this.extended_configs.timezone_offset = getTimezoneOffset(timezone) + configs.settings.timezone_offset_correction * -1;
const todayFixedByTimezone = getCurrentDateInSpecifiedTimezone(timezone);
this.extended_configs.today_date = todayFixedByTimezone.split('T')[0];
this.extended_configs.user_email = getUserEmail();
this.extended_configs.today_date = getDateFixedByTimezone(configs.settings.timezone_correction).toISOString().split('T')[0];
this.extended_configs.configs = configs;
logger.info(`${APP_INFO.name} is running at version ${APP_INFO.version}!`);
}
Expand Down Expand Up @@ -1175,7 +1211,7 @@
return logger.logs;
}
getTicktickTasks() {
return getAllTicktickTasks(this.extended_configs.configs[ticktickConfigsKey].ics_calendars, this.extended_configs.configs.settings.timezone_correction);
return getAllTicktickTasks(this.extended_configs.configs[ticktickConfigsKey].ics_calendars, this.extended_configs.timezone_offset);
}
getGoogleEvents() {
return getTasksFromGoogleCalendars([...new Set(this.extended_configs.configs[ticktickConfigsKey].ics_calendars.map((item) => item.gcal))]);
Expand Down Expand Up @@ -1219,7 +1255,7 @@
commits_tracked_to_be_added: [],
commits_tracked_to_be_deleted: []
};
const sessionData = Object.assign(Object.assign(Object.assign({}, emptySessionData), (shouldSyncTicktick && syncTicktick(this.extended_configs.configs))), (shouldSyncGithub && syncGithub(this.extended_configs.configs)));
const sessionData = Object.assign(Object.assign(Object.assign({}, emptySessionData), (shouldSyncTicktick && syncTicktick(this.extended_configs))), (shouldSyncGithub && syncGithub(this.extended_configs.configs)));
const parsedSessionData = handleSessionData(this.extended_configs, sessionData);
return parsedSessionData;
}
Expand Down
2 changes: 1 addition & 1 deletion dist/index.min.js

Large diffs are not rendered by default.

Loading

0 comments on commit aa0346d

Please sign in to comment.