Last active
August 5, 2020 20:12
-
-
Save allandequeiroz/2ac0461cf254b30a72a7dd443db0c816 to your computer and use it in GitHub Desktop.
Revisions
-
Allan de Queiroz revised this gist
Aug 5, 2020 . 1 changed file with 79 additions and 25 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -20,6 +20,7 @@ Maintainer: Allan de Queiroz */ // ------------------------------------------------------------------------------- // ------------------------------------------------------------------------------- function Activity(tempo) { this.tempo = tempo; @@ -30,6 +31,9 @@ function Activity(tempo) { this.getName = function () { return this.name; } this.getType = function () { return this.type; } this.getTags = function () { return this.tags; } @@ -54,6 +58,9 @@ function Activity(tempo) { this.getFormatedDueDate = function () { return this.tempo.formatDate(this.dueDate); } this.getParent = function () { return this.parent; } // SET this.setId = function (id) { @@ -62,6 +69,9 @@ function Activity(tempo) { this.setName = function (name) { this.name = name; } this.setType = function (type) { this.type = type; } this.setTags = function (tags) { tags.forEach(function (tag) { addTag(tag) @@ -79,6 +89,9 @@ function Activity(tempo) { this.setDueDate = function (dueDate) { this.dueDate = dueDate; } this.setParent = function (parent) { this.parent = parent; } // MISC this.addTag = function (tag) { @@ -276,24 +289,35 @@ const HabiticaActivity = (habiticaTag, habiticaRequest, tempo) => { let activeTasks = new Map(); let completedTasks = new Set(); const computeIfTraceable = (todo, activeTasksLocal) => { if (todo["tags"] && todo["tags"].includes(habiticaTag.getDefaultTagId())) { let activeActivity = new Activity(tempo); activeActivity.setId(todo["id"]); activeActivity.setName(todo["text"]); activeActivity.setType(todo['type']); activeActivity.setCreatedAt(new Date(todo["createdAt"])); if (todo["tags"]) { todo["tags"].forEach(function (tag) { activeActivity.addTag(tag); }); } if (todo["date"]) { activeActivity.setDueDate(new Date(todo["date"])); } if (todo['notes']) { activeActivity.setNotes(todo["notes"]); } activeTasksLocal.set(activeActivity.getName(), activeActivity); } } const fetchActivities = () => { const activeTasksLocal = new Map(); habiticaRequest.performGet("https://habitica.com/api/v3/tasks/user?type=todos").forEach(function (todo) { computeIfTraceable(todo, activeTasksLocal); }); habiticaRequest.performGet("https://habitica.com/api/v3/tasks/user?type=dailys").forEach(function (todo) { computeIfTraceable(todo, activeTasksLocal); }); activeTasks = activeTasksLocal; } @@ -330,7 +354,7 @@ const HabiticaActivity = (habiticaTag, habiticaRequest, tempo) => { // ------------------------------------------------------------------------------- const GoogleActivity = (habiticaTag, html2Markdown, tempo, observedCalendars, habiticaActive, habiticaCompleted) => { let calendarTasks = new Set(); let calendarEvents = new Set(); @@ -348,15 +372,17 @@ const GoogleActivity = (habiticaTag, html2Markdown, tempo, observedCalendars) => if (tempo.getTodayString() === taskActivityDue) { let taskActivity = new Activity(tempo); taskActivity.setName(task.title); taskActivity.setType(taskList.name === "Dailies" ? "daily" : "todo"); taskActivity.addTag(habiticaTag.getDefaultTagId()); taskActivity.addTag(habiticaTag.getTagIdByName(taskList.title)); taskActivity.setNotes(html2Markdown.convert(task.notes)); taskActivity.setDueDate(new Date(task.due)); taskActivity.setParent(taskList.id); calendarTasksLocal.add(taskActivity); if (habiticaCompleted.has(task.title)) { task.setStatus("completed"); let result = Tasks.Tasks.update(task, taskList.id, task.id); } } } @@ -376,6 +402,7 @@ const GoogleActivity = (habiticaTag, html2Markdown, tempo, observedCalendars) => for (const event of calendarEvents.entries()) { let calendarActivity = new Activity(tempo); calendarActivity.setName(event[1].getTitle()); calendarActivity.setType(calendarName === "Dailies" ? "daily" : "todo"); calendarActivity.addTag(habiticaTag.getDefaultTagId()); calendarActivity.addTag(habiticaTag.getTagIdByName(calendarName)); calendarActivity.setNotes(html2Markdown.convert(event[1].getDescription())); @@ -401,19 +428,21 @@ const GoogleActivity = (habiticaTag, html2Markdown, tempo, observedCalendars) => // ------------------------------------------------------------------------------- function SynchronizeActivities() { const observedCalendars = new Set(["Birthdays", "Birthdays External", "Appointments", "Work", "Personal", "Dailies", "Gantter", "2020"]); const habiticaRequest = HabiticaRequest(); const habiticaTag = HabiticaTag(habiticaRequest, observedCalendars); const html2Markdown = Html2Markdown(); const tempo = Tempo(); const habiticaActivity = HabiticaActivity(habiticaTag, habiticaRequest, tempo); const habiticaActive = habiticaActivity.getActiveTasks(); const habiticaCompleted = habiticaActivity.getCompletedTasks(); const googleActivity = GoogleActivity(habiticaTag, html2Markdown, tempo, observedCalendars, habiticaActive, habiticaCompleted); const calendarEvents = googleActivity.getGoogleActivities(); const visitedEvents = new Map(); @@ -433,17 +462,36 @@ function HabiticaReminders() { "text": activity.getName(), "notes": activity.getNotes(), "date": activity.getDueDate(), "type": activity.getType(), "priority": "1.5" } collectedEvents.add(payload); } collectedEvents = Array.from(collectedEvents).sort((a, b) => (a.date - b.date)).reverse(); for (let activity of collectedEvents) { const notesTasks = new Set() if (activity["notes"]) { activity["notes"].split("\n").forEach(function (note) { if (note.trim().startsWith("*")) { const payload = { "text": note.trim().replace(/^(\s+)?\*(\s+)?/g, "") } notesTasks.add(payload) activity["notes"] = activity["notes"].replace(note, "") } }); activity["notes"] = activity["notes"].trim() } let taskId = habiticaRequest.performPost("https://habitica.com/api/v3/tasks/user", activity)["id"]; for (let notesTask of notesTasks) { habiticaRequest.performPost("https://habitica.com/api/v3/tasks/" + taskId + "/checklist/", notesTask); Utilities.sleep(5 * 1000) } if (visitedEvents.get(activity.text).getTags()) { visitedEvents.get(activity.text).getTags().forEach(function (tagId) { habiticaRequest.performPost("https://habitica.com/api/v3/tasks/" + taskId + "/tags/" + tagId); @@ -459,3 +507,9 @@ function HabiticaReminders() { } } } function CleanUpActivities() { const habiticaRequest = HabiticaRequest(); habiticaRequest.performPost("https://habitica.com/api/v3/tasks/clearCompletedTodos", {}); } -
Allan de Queiroz revised this gist
Jul 26, 2020 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -356,7 +356,7 @@ const GoogleActivity = (habiticaTag, html2Markdown, tempo, observedCalendars) => if (habiticaCompleted.has(task.title)) { task.setStatus("completed"); Tasks.Tasks.update(task, taskList.id, task.id); } } } -
Allan de Queiroz revised this gist
Jul 26, 2020 . 1 changed file with 5 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -353,6 +353,11 @@ const GoogleActivity = (habiticaTag, html2Markdown, tempo, observedCalendars) => taskActivity.setNotes(html2Markdown.convert(task.notes)); taskActivity.setDueDate(new Date(task.due)); calendarTasksLocal.add(taskActivity); if (habiticaCompleted.has(task.title)) { task.setStatus("completed"); let result = Tasks.Tasks.update(task, taskList.id, task.id); } } } }) -
Allan de Queiroz revised this gist
Jul 24, 2020 . 1 changed file with 17 additions and 10 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -359,7 +359,7 @@ const GoogleActivity = (habiticaTag, html2Markdown, tempo, observedCalendars) => } }) } calendarTasks = calendarTasksLocal; } const getCalendarActivities = () => { @@ -379,7 +379,7 @@ const GoogleActivity = (habiticaTag, html2Markdown, tempo, observedCalendars) => } } }); calendarEvents = calendarEventsLocal; } const getGoogleActivities = () => { @@ -396,10 +396,9 @@ const GoogleActivity = (habiticaTag, html2Markdown, tempo, observedCalendars) => // ------------------------------------------------------------------------------- function HabiticaReminders() { const observedCalendars = new Set(["Birthdays", "Birthdays External", "Appointments", "Morning", "Work", "Personal", "Gantter", "2020", "After Work"]); const habiticaRequest = HabiticaRequest(); const habiticaTag = HabiticaTag(habiticaRequest, observedCalendars); @@ -411,10 +410,13 @@ function HabiticaReminders() { const habiticaActive = habiticaActivity.getActiveTasks(); const habiticaCompleted = habiticaActivity.getCompletedTasks(); const calendarEvents = googleActivity.getGoogleActivities(); const visitedEvents = new Map(); let collectedEvents = new Set(); for (let activity of calendarEvents) { visitedEvents.set(activity.getName(), activity); if (habiticaActive.has(activity.getName()) || habiticaCompleted.has(activity.getName())) { @@ -430,10 +432,15 @@ function HabiticaReminders() { "priority": "1.5" } collectedEvents.add(payload); } collectedEvents = Array.from(collectedEvents).sort((a, b) => (a.date - b.date)); for (let activity of collectedEvents) { let taskId = habiticaRequest.performPost("https://habitica.com/api/v3/tasks/user", activity)["id"]; if (visitedEvents.get(activity.text).getTags()) { visitedEvents.get(activity.text).getTags().forEach(function (tagId) { habiticaRequest.performPost("https://habitica.com/api/v3/tasks/" + taskId + "/tags/" + tagId); Utilities.sleep(5 * 1000) }); @@ -446,4 +453,4 @@ function HabiticaReminders() { habiticaRequest.performDelete("https://habitica.com/api/v3/tasks/" + activity[1].getId()); } } } -
Allan de Queiroz revised this gist
Jul 22, 2020 . 1 changed file with 0 additions and 5 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -416,11 +416,6 @@ function HabiticaReminders() { for (let activity of calendarEvents) { visitedEvents.add(activity.getName()); if (habiticaActive.has(activity.getName()) || habiticaCompleted.has(activity.getName())) { -
Allan de Queiroz revised this gist
Jul 22, 2020 . 1 changed file with 417 additions and 165 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -3,24 +3,6 @@ Maintainer: Allan de Queiroz --- ## Installation 1. Replace by habId(line 27) and habToken(line 28) with your ones, you can find them [Here](https://habitica.com/user/settings/api) @@ -35,168 +17,438 @@ The script will check the existing tasks at the active inbox and validate agains * Select type of time based trigger: Minutes timer * Select minute interval: Every 5 minutes * Failure notification settings: Notify me imeediately */ // ------------------------------------------------------------------------------- function Activity(tempo) { this.tempo = tempo; // GET this.getId = function () { return this.id; } this.getName = function () { return this.name; } this.getTags = function () { return this.tags; } this.getNotes = function () { return this.notes; } this.getCreatedAt = function () { return this.createdAt; } this.getFormatedCreatedAt = function () { return this.tempo.formatDate(this.createdAt); } this.getDateCompleted = function () { return this.dateCompleted; } this.getFormatedDateCompleted = function () { return this.tempo.formatDate(this.dateCompleted); } this.getDueDate = function () { return this.dueDate; } this.getFormatedDueDate = function () { return this.tempo.formatDate(this.dueDate); } // SET this.setId = function (id) { this.id = id; } this.setName = function (name) { this.name = name; } this.setTags = function (tags) { tags.forEach(function (tag) { addTag(tag) }); } this.setNotes = function (notes) { this.notes = notes; } this.setCreatedAt = function (createdAt) { this.createdAt = createdAt; } this.setDateCompleted = function (dateCompleted) { this.dateCompleted = dateCompleted; } this.setDueDate = function (dueDate) { this.dueDate = dueDate; } // MISC this.addTag = function (tag) { if (!this.tags) { this.tags = []; } this.tags.push(tag) } } // ------------------------------------------------------------------------------- const HabiticaRequest = () => { const habId = ""; const habToken = ""; const headers = { "x-api-user": habId, "x-api-key": habToken } const getRequest = { "method": "get", "headers": headers } const postRequest = { "method": "post", "headers": headers } const deleteRequest = { "method": "delete", "headers": headers } const performGet = (url) => { return performRequest(url, getRequest); } const performPost = (url, payload) => { const postPayload = postRequest; postPayload["payload"] = payload; return performRequest(url, postPayload); } const performDelete = (url) => { return performRequest(url, deleteRequest); } const performRequest = (url, options) => { return JSON.parse(UrlFetchApp.fetch(url, options).getContentText())["data"]; } return { performGet, performPost, performDelete }; } // ------------------------------------------------------------------------------- const HabiticaTag = (habiticaRequest, observedCalendars) => { const defaultTag = "gCal"; const nameIdTags = new Map() const idNameTags = new Map() const loadTagsMaps = () => { habiticaRequest.performGet("https://habitica.com/api/v3/tags").forEach(function (tag) { idNameTags.set(tag["id"], tag["name"]); nameIdTags.set(tag["name"], tag["id"]); }); } const createTagIfAbsent = (tagName) => { if (!nameIdTags.has(tagName)) { const payload = { "name": tagName } habiticaRequest.performPost("https://habitica.com/api/v3/tags", payload); return true; } else { return false; } } const createTagsIfAbsent = () => { createTagIfAbsent(defaultTag); for (let calendar of observedCalendars) { createTagIfAbsent(calendar); } const taskLists = Tasks.Tasklists.list(); if (taskLists.items) { taskLists.items.forEach(function (taskList) { if (!observedCalendars.has(taskList.title)) { createTagIfAbsent(taskList.title); } }) } loadTagsMaps(); } const getDefaultTag = () => { return defaultTag; } const getDefaultTagId = () => { return getTagIdByName(getDefaultTag()); } const getTagIdByName = (tagName) => { return nameIdTags.get(tagName); } const hasTagNamed = (tagName) => { return nameIdTags.has(tagName); } const getTagNameById = (tagId) => { return idNameTags.get(tagId); } const hasTagId = (tagId) => { return idNameTags.has(tagId); } loadTagsMaps(); createTagsIfAbsent(); return { getDefaultTag, getDefaultTagId, getTagIdByName, hasTagNamed, getTagNameById, hasTagId } } // ------------------------------------------------------------------------------- const Html2Markdown = () => { const convert = function (html) { if (html) { var params = { "method": "post", "payload": { "html": html } } return UrlFetchApp.fetch("http://fuckyeahmarkdown.com/go/", params).getContentText(); } } return {convert}; } // ------------------------------------------------------------------------------- const Tempo = () => { const formatDate = (date) => { var dd = String(date.getDate()).padStart(2, '0'); var mm = String(date.getMonth() + 1).padStart(2, '0'); var yyyy = date.getFullYear(); return yyyy + '/' + mm + '/' + dd; } const todayDate = new Date(); const todayString = formatDate(todayDate); const getTodayDate = () => { return todayDate; } const getTodayString = () => { return todayString; } return { formatDate, getTodayDate, getTodayString }; } // ------------------------------------------------------------------------------- const HabiticaActivity = (habiticaTag, habiticaRequest, tempo) => { let activeTasks = new Map(); let completedTasks = new Set(); const fetchActivities = () => { const activeTasksLocal = new Map(); habiticaRequest.performGet("https://habitica.com/api/v3/tasks/user?type=todos").forEach(function (todo) { if (todo["tags"] && todo["tags"].includes(habiticaTag.getDefaultTagId())) { let activeActivity = new Activity(tempo); activeActivity.setId(todo["id"]); activeActivity.setName(todo["text"]); activeActivity.setCreatedAt(new Date(todo["createdAt"])); if (todo["tags"]) { todo["tags"].forEach(function (tag) { activeActivity.addTag(tag); }); } if (todo["date"]) { activeActivity.setDueDate(new Date(todo["date"])); } activeTasksLocal.set(activeActivity.getName(), activeActivity); } }); activeTasks = activeTasksLocal; } const fetchCompletedActivities = () => { const completedTasksLocal = new Set(); habiticaRequest.performGet("https://habitica.com/api/v3/tasks/user?type=completedTodos").forEach(function (completedTodo) { if (completedTodo["tags"] && completedTodo["tags"].includes(habiticaTag.getDefaultTagId())) { let dateCompleted = tempo.formatDate(new Date(completedTodo["dateCompleted"])); if (tempo.getTodayString() === dateCompleted) { completedTasksLocal.add(completedTodo["text"]); } } }); completedTasks = completedTasksLocal; } const getActiveTasks = () => { return activeTasks; } const getCompletedTasks = () => { return completedTasks; } fetchActivities(); fetchCompletedActivities(); return { getActiveTasks, getCompletedTasks }; } // ------------------------------------------------------------------------------- const GoogleActivity = (habiticaTag, html2Markdown, tempo, observedCalendars) => { let calendarTasks = new Set(); let calendarEvents = new Set(); const getTasksActivities = () => { const calendarTasksLocal = new Set() const taskLists = Tasks.Tasklists.list(); if (taskLists.items) { taskLists.items.forEach(function (taskList) { var tasks = Tasks.Tasks.list(taskList.id); if (tasks.items) { tasks.items.forEach(function (task) { if (task.title) { const taskActivityDue = tempo.formatDate(new Date(task.due)); if (tempo.getTodayString() === taskActivityDue) { let taskActivity = new Activity(tempo); taskActivity.setName(task.title); taskActivity.addTag(habiticaTag.getDefaultTagId()); taskActivity.addTag(habiticaTag.getTagIdByName(taskList.title)); taskActivity.setNotes(html2Markdown.convert(task.notes)); taskActivity.setDueDate(new Date(task.due)); calendarTasksLocal.add(taskActivity); } } }) } }) } calendarTasks = new Set(Array.from(calendarTasksLocal).reverse()); } const getCalendarActivities = () => { const calendarEventsLocal = new Set() CalendarApp.getAllCalendars().forEach(function (calendar) { const calendarName = calendar.getName(); if (observedCalendars.has(calendarName)) { const calendarEvents = calendar.getEventsForDay(tempo.getTodayDate()); for (const event of calendarEvents.entries()) { let calendarActivity = new Activity(tempo); calendarActivity.setName(event[1].getTitle()); calendarActivity.addTag(habiticaTag.getDefaultTagId()); calendarActivity.addTag(habiticaTag.getTagIdByName(calendarName)); calendarActivity.setNotes(html2Markdown.convert(event[1].getDescription())); calendarActivity.setDueDate(new Date(event[1].getStartTime())); calendarEventsLocal.add(calendarActivity); } } }); calendarEvents = new Set(Array.from(calendarEventsLocal).reverse()); } const getGoogleActivities = () => { return new Set([...calendarTasks, ...calendarEvents]) } getTasksActivities(); getCalendarActivities(); return { getGoogleActivities }; } // ------------------------------------------------------------------------------- function HabiticaReminders() { const observedCalendars = new Set(["Gantter", "Birthdays", "Appointments", "Work", "..."]); const habiticaRequest = HabiticaRequest(); const habiticaTag = HabiticaTag(habiticaRequest, observedCalendars); const html2Markdown = Html2Markdown(); const tempo = Tempo(); const habiticaActivity = HabiticaActivity(habiticaTag, habiticaRequest, tempo); const googleActivity = GoogleActivity(habiticaTag, html2Markdown, tempo, observedCalendars); const habiticaActive = habiticaActivity.getActiveTasks(); const habiticaCompleted = habiticaActivity.getCompletedTasks(); const calendarEvents = googleActivity.getGoogleActivities(); const visitedEvents = new Set(); for (let activity of calendarEvents) { visitedEvents.add(activity.getName()); Logger.log( '\nactivity -> ' + activity.getName() + '\nactive: ' + habiticaActive.has(activity.getName()) + '\ncompleted: ' + habiticaCompleted.has(activity.getName())); if (habiticaActive.has(activity.getName()) || habiticaCompleted.has(activity.getName())) { continue; } const payload = { "text": activity.getName(), "notes": activity.getNotes(), "date": activity.getDueDate(), "type": "todo", "priority": "1.5" } let taskId = habiticaRequest.performPost("https://habitica.com/api/v3/tasks/user", payload)["id"]; if (activity.getTags()) { activity.getTags().forEach(function (tagId) { habiticaRequest.performPost("https://habitica.com/api/v3/tasks/" + taskId + "/tags/" + tagId); Utilities.sleep(5 * 1000) }); } } for (const activity of habiticaActive.entries()) { if (!visitedEvents.has(activity[1].getName()) && activity[1].getFormatedCreatedAt() === tempo.getTodayString()) { habiticaRequest.performDelete("https://habitica.com/api/v3/tasks/" + activity[1].getId()); } } } -
Allan de Queiroz revised this gist
Jul 19, 2020 . 1 changed file with 2 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -56,6 +56,7 @@ const formatDate = function(date) { const habId = ""; const habToken = ""; const defaultTag = "gCal"; const calendarName = "HabiticaReminders"; const now = new Date(); const date = formatDate(now); const headers = { @@ -139,7 +140,7 @@ const getCompletedTodosMap = function() { // Fetch the current calendar events const getCalendarEventsSet = function() { let calendarEventsSet = new Set() let calendarEvents = CalendarApp.getCalendarsByName(calendarName)[0].getEventsForDay(now); for (const event of calendarEvents.entries()) { let entry = { "title" : event[1].getTitle() -
Allan de Queiroz revised this gist
Jul 19, 2020 . 1 changed file with 27 additions and 7 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -141,7 +141,23 @@ const getCalendarEventsSet = function() { let calendarEventsSet = new Set() let calendarEvents = CalendarApp.getCalendarsByName("HabiticaReminders")[0].getEventsForDay(now); for (const event of calendarEvents.entries()) { let entry = { "title" : event[1].getTitle() } // If there's a description at gCalendar, convert to Markdown // before sending to Habitica var description = event[1].getDescription(); if(description) { var params = postRequest; params["payload"] = { "html" : description } let markdown = UrlFetchApp.fetch("http://fuckyeahmarkdown.com/go/", params).getContentText(); entry.description = markdown; } calendarEventsSet.add(entry); } return calendarEventsSet; } @@ -151,19 +167,23 @@ function scheduleToDos() { let todosMap = getTodosMap(); let completedTodosMap = getCompletedTodosMap(); let calendarEventsSet = getCalendarEventsSet(); let calendarTitleEventsSet = new Set(); for (let event of calendarEventsSet) { calendarTitleEventsSet.add(event.title); if(todosMap.has(event.title) || completedTodosMap.has(event.title) && date === completedTodosMap.get(event.title)) { continue; } var params = postRequest; params.muteHttpExceptions = false; params["payload"] = { "text" : event.title, "notes" : event.description, "type" : "todo", "priority" : "1.5", "tags" : nameIdTags.get(defaultTag) @@ -174,8 +194,8 @@ function scheduleToDos() { for (const todo of todosMap.entries()) { let key = todo[0]; let value = todo[1]; if(!calendarTitleEventsSet.has(key)) { UrlFetchApp.fetch("https://habitica.com/api/v3/tasks/" + value, deleteRequest); } } } -
Allan de Queiroz created this gist
Jul 19, 2020 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,181 @@ /* Maintainer: Allan de Queiroz --- This script aims to provide synchronism between Habitica's inbox Tasks and Google Calendar. It was heavily inspired by Snickersnacker's one. The usage of this one is quite similar to the difference that it adds a few more functionalities. This version of the script enables frequent scheduling of synchronization (if you need of course), handling the creation and also removal of tasks if required. __Disclaimer:__ All the tasks handled by this script will be tagged with a gCal tag so that the script won't interfere with the manually created ones. The script will perform the following validations/actions: ## Tasks Creation 1. The script will check if a gCalendar event is present at the active list of tasks on Habitica. If the task is already there, it will not add a new one. (that's why it is safe to schedule the executions as often as you want). 2. If there's no task at the active list, the script will look into the complete list, if the script finds the same task there, it validates the date, if the task were completed today it would not create a new one. Assuming that the task was added to Habitica's list, and tackled already. 1. In the other hand, when looking into the complete tasks, if the script finds a task with the same title but completed in previous days, it assumes that this is a recurring task, so it creates the new one for that day. (to handle recurring tasks). 2. One observation here about recurring tasks. By design, no new task will be created at the active inbox if there's already one present, doesn't matter if they were created at previous days. The previous day's rule is only taken into consideration when checking the completed tasks inbox. ## Tasks Removal The script will check the existing tasks at the active inbox and validate against the ones present at gCalendar, if a task managed by the script is present on Habitica but not on gCalendar anymore; this task will be automatically removed from Habitica's active tasks inbox. The idea here is to keep the tasks inbox fully synchronized with gCalendar, if you remove from your calendar, it will be removed from the tasks lists. ## Installation 1. Replace by habId(line 27) and habToken(line 28) with your ones, you can find them [Here](https://habitica.com/user/settings/api) 2. Go to [https://script.google.com/](https://script.google.com/) 1. Create a new project, give some nice name to it and the script as well, I liked the one from the inspirational script HabiticaReminders. 2. Paste this script at the large blank area 3. Edit ( Menu ) -> Current Project's Triggers -> Add Trigger (bottom right): # Here is up to you, this are the ones I've chosen * Choose which function to run: scheduleToDos * Choose which deployment should run: Head * Select event source: Time-driven * Select type of time based trigger: Minutes timer * Select minute interval: Every 5 minutes * Failure notification settings: Notify me imeediately ## References * [The original idea](https://habitica.fandom.com/wiki/Thread:48017) * [Gist](https://gist.github.com/allandequeiroz/8a369534aaa58e1a2bcf3c3135120d85) * [Google Scripts](https://script.google.com/) * [Habitica's API personal credentials](https://habitica.com/user/settings/api) */ // Turn the Date into a nice format for comparison purposes const formatDate = function(date) { var dd = String(date.getDate()).padStart(2, '0'); var mm = String(date.getMonth() + 1).padStart(2, '0'); var yyyy = date.getFullYear(); return yyyy + '/' + mm + '/' + dd; } // Basic requirements to fetch the required data const habId = ""; const habToken = ""; const defaultTag = "gCal"; const now = new Date(); const date = formatDate(now); const headers = { "x-api-user" : habId, "x-api-key" : habToken } const getRequest = { "method" : "get", "headers" : headers } const postRequest = { "method" : "post", "headers" : headers } const deleteRequest = { "method" : "delete", "headers" : headers } // Fetch a list of existing tags from Habitica const getTagsMaps = function() { let tags = JSON.parse(UrlFetchApp.fetch("https://habitica.com/api/v3/tags", getRequest).getContentText())["data"] let idName = new Map() let nameId = new Map() for (i = 0; i < tags.length; i++) { idName.set(tags[i]["id"], tags[i]["name"]); nameId.set(tags[i]["name"], tags[i]["id"]); } return { "idName" : idName, "nameId" : nameId }; } // Preparing tags for future validations const tags = getTagsMaps(); const nameIdTags = tags["nameId"]; const idNameTags = tags["idName"]; /* Validate if the tag used to indentify the tasks managed by this script exists, if not, creates it before moving on */ const createDefaultTagIfAbsent = function() { if(!nameIdTags.has(defaultTag)) { var params = postRequest; params["payload"] = { "name" : defaultTag } UrlFetchApp.fetch("https://habitica.com/api/v3/tags", params).toString() } } createDefaultTagIfAbsent(); // Fetch a list of open todos from Habitica const getTodosMap = function() { let todos = JSON.parse(UrlFetchApp.fetch("https://habitica.com/api/v3/tasks/user?type=todos", getRequest).getContentText())["data"] let todosMap = new Map() for (i = 0; i < todos.length; i++) { if(todos[i]["tags"] && todos[i]["tags"].includes(nameIdTags.get(defaultTag))) { todosMap.set(todos[i]["text"], todos[i]["id"]); } } return todosMap; } // Fetch a list of closed todos from Habitica const getCompletedTodosMap = function() { let completedTodos = JSON.parse(UrlFetchApp.fetch("https://habitica.com/api/v3/tasks/user?type=completedTodos", getRequest).getContentText())["data"] let completedTodosMap = new Map() for (i = 0; i < completedTodos.length; i++) { if(completedTodos[i]["tags"] && completedTodos[i]["tags"].includes(nameIdTags.get(defaultTag))) { let dateCompleted = new Date(completedTodos[i]["dateCompleted"]); completedTodosMap.set(completedTodos[i]["text"], formatDate(dateCompleted)); } } return completedTodosMap; } // Fetch the current calendar events const getCalendarEventsSet = function() { let calendarEventsSet = new Set() let calendarEvents = CalendarApp.getCalendarsByName("HabiticaReminders")[0].getEventsForDay(now); for (const event of calendarEvents.entries()) { calendarEventsSet.add(event[1].getTitle()); } return calendarEventsSet; } // Adds or Removes tasks according to the rules described at the begining of the script function scheduleToDos() { let todosMap = getTodosMap(); let completedTodosMap = getCompletedTodosMap(); let calendarEventsSet = getCalendarEventsSet(); for (let event of calendarEventsSet) { if(todosMap.has(event) || completedTodosMap.has(event) && date === completedTodosMap.get(event)) { continue; } var params = postRequest; params.muteHttpExceptions = false; params["payload"] = { "text" : event, "type" : "todo", "priority" : "1.5", "tags" : nameIdTags.get(defaultTag) } UrlFetchApp.fetch("https://habitica.com/api/v3/tasks/user", params) } for (const todo of todosMap.entries()) { let key = todo[0]; let value = todo[1]; if(!calendarEventsSet.has(key)) { UrlFetchApp.fetch("https://habitica.com/api/v3/tasks/" + value, deleteRequest); } } }