Last active
August 5, 2020 20:12
-
-
Save allandequeiroz/2ac0461cf254b30a72a7dd443db0c816 to your computer and use it in GitHub Desktop.
This file contains 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 characters
/* | |
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) | |
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 | |
*/ | |
// ------------------------------------------------------------------------------- | |
// ------------------------------------------------------------------------------- | |
function Activity(tempo) { | |
this.tempo = tempo; | |
// GET | |
this.getId = function () { | |
return this.id; | |
} | |
this.getName = function () { | |
return this.name; | |
} | |
this.getType = function () { | |
return this.type; | |
} | |
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); | |
} | |
this.getParent = function () { | |
return this.parent; | |
} | |
// SET | |
this.setId = function (id) { | |
this.id = id; | |
} | |
this.setName = function (name) { | |
this.name = name; | |
} | |
this.setType = function (type) { | |
this.type = type; | |
} | |
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; | |
} | |
this.setParent = function (parent) { | |
this.parent = parent; | |
} | |
// 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 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; | |
} | |
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, habiticaActive, habiticaCompleted) => { | |
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.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); | |
} | |
} | |
} | |
}) | |
} | |
}) | |
} | |
calendarTasks = calendarTasksLocal; | |
} | |
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.setType(calendarName === "Dailies" ? "daily" : "todo"); | |
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 = calendarEventsLocal; | |
} | |
const getGoogleActivities = () => { | |
return new Set([...calendarTasks, ...calendarEvents]) | |
} | |
getTasksActivities(); | |
getCalendarActivities(); | |
return { | |
getGoogleActivities | |
}; | |
} | |
// ------------------------------------------------------------------------------- | |
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(); | |
let collectedEvents = new Set(); | |
for (let activity of calendarEvents) { | |
visitedEvents.set(activity.getName(), activity); | |
if (habiticaActive.has(activity.getName()) || | |
habiticaCompleted.has(activity.getName())) { | |
continue; | |
} | |
const payload = { | |
"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); | |
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()); | |
} | |
} | |
} | |
function CleanUpActivities() { | |
const habiticaRequest = HabiticaRequest(); | |
habiticaRequest.performPost("https://habitica.com/api/v3/tasks/clearCompletedTodos", {}); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment