Skip to content

Instantly share code, notes, and snippets.

@allandequeiroz
Last active August 5, 2020 20:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save allandequeiroz/2ac0461cf254b30a72a7dd443db0c816 to your computer and use it in GitHub Desktop.
Save allandequeiroz/2ac0461cf254b30a72a7dd443db0c816 to your computer and use it in GitHub Desktop.
/*
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