├── README.md └── index.gs /README.md: -------------------------------------------------------------------------------- 1 |
9 | 10 | # Habitica Google Apps Script - Calendar Integration 11 | 12 | This script aims to take advantage of all the abstraction contained within the use of the [Google Apps Script](https://developers.google.com/apps-script) platform, that makes easier to create and publish add-ons in an online store for Google Calendar, Docs, Slides, and Forms, to turn my calendar events into tasks in my favorite gamefied task tracking app: Habitica. 13 | 14 | ## About 15 | 16 | [Habitica](https://habitica.com) is an open source habit building program which treats your life like a Role Playing Game. Level up as you succeed, lose HP as you fail, earn money to buy weapons and armor. 17 | 18 | Thanks to Habitica's public API, it is possible to: 19 | 20 | - Transform each new event on a calendar of your choice, into a Habitica `task`, so you will earn gold and EXP when you complete it 21 | - Whenever a new event is created, Google's own trigger is responsible for triggering a POST request for Habitica's API and creating your `task` with the information you want, and even with an image! 22 | - Still in the Google Script panel, you can define a verification routine so that your daily Habitica tasks also keep up with your schedule changes 23 | 24 | ## Usage 25 | ``` 26 | Just copy, paste & run! 27 | ``` 28 | 29 | If you don't know Google App Scripts and have no idea how to use the script above, check the video below to know more, then click [here](https://www.google.com/script/start/) to start one: 30 | 31 | 32 | [](https://www.youtube.com/watch?v=JJgmU_JUsug) 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /index.gs: -------------------------------------------------------------------------------- 1 | const CALENDAR_NAME = "Your calendar name goes here"; 2 | const HABITICA_TOKEN = "Your habitica token goes here"; 3 | const HABITICA_ID = "Your habitica id goes here"; 4 | 5 | function syncToHabbitica() { 6 | const habTaskURL = "https://habitica.com/api/v3/tasks/"; 7 | 8 | const today = new Date(); 9 | const agenda = CalendarApp.getCalendarsByName(CALENDAR_NAME)[0]; 10 | const events = agenda.getEventsForDay(today); 11 | 12 | console.log(events[0]); 13 | 14 | const templateParams = { 15 | _post: { 16 | method: "post", 17 | headers: { "x-api-user": HABITICA_ID, "x-api-key": HABITICA_TOKEN }, 18 | }, 19 | _get: { 20 | contentType: "application/json", 21 | method: "get", 22 | headers: { "x-api-user": HABITICA_ID, "x-api-key": HABITICA_TOKEN }, 23 | }, 24 | _delete: { 25 | method: "delete", 26 | headers: { "x-api-user": HABITICA_ID, "x-api-key": HABITICA_TOKEN }, 27 | }, 28 | }; 29 | 30 | const newTasks = []; 31 | const existingTasks = fetchExistingTasks(habTaskURL, templateParams); 32 | const completedTasksContent = fetchTodayCompletedTasks( 33 | habTaskURL, 34 | templateParams, 35 | today 36 | ); 37 | 38 | deleteCalendarTasks(habTaskURL, existingTasks, templateParams); 39 | 40 | for (i = 0; i < events.length; i++) { 41 | if (newTasks.indexOf(events[i].getTitle()) === -1) { 42 | newTasks.push(events[i].getTitle()); 43 | 44 | const params = templateParams._post; 45 | params["payload"] = { 46 | text: ":calendar: " + events[i].getTitle(), 47 | notes: createTaskNote(events[i].getTitle(), events[i].getLocation()), 48 | type: "daily", 49 | priority: "2", 50 | date: "today", 51 | repeat: "false", 52 | }; 53 | 54 | const paramsText = completedTasksContent.indexOf(params.payload.text); 55 | 56 | if (completedTasksContent.indexOf(params.payload.text) === -1) { 57 | UrlFetchApp.fetch(habTaskURL + "user", params); 58 | } 59 | } 60 | } 61 | } 62 | 63 | function fetchExistingTasks(habTaskURL, templateParams) { 64 | const response = UrlFetchApp.fetch( 65 | habTaskURL + "user?type=dailys", 66 | templateParams._get 67 | ); 68 | return JSON.parse(response.getContentText()); 69 | } 70 | 71 | function deleteCalendarTasks(habTaskURL, habTasks, templateParams) { 72 | for (j = 0; j < habTasks.data.length; j++) { 73 | if (habTasks.data[j].text.indexOf(":calendar: ") > -1) { 74 | UrlFetchApp.fetch( 75 | habTaskURL + habTasks.data[j].id, 76 | templateParams._delete 77 | ); 78 | } 79 | } 80 | } 81 | 82 | function fetchTodayCompletedTasks(habTaskURL, templateParams, today) { 83 | const tasksContent = []; 84 | const response = UrlFetchApp.fetch( 85 | habTaskURL + "user?type=dailys", 86 | templateParams._get 87 | ); 88 | const tasks = JSON.parse(response.getContentText()); 89 | 90 | for (i = 0; i < tasks.data.length; i++) { 91 | if (tasks.data[i].text.indexOf(":calendar: ") > -1) { 92 | const taskDate = new Date(tasks.data[i].createdAt).getDate(); 93 | if (taskDate + 12 !== today.getDate()) { 94 | tasksContent.push(tasks.data[i].text); 95 | } 96 | } 97 | } 98 | return tasksContent; 99 | } 100 | 101 | //optional call, remove if you dont't need it 102 | function createTaskNote(title, location) { 103 | if (location) { 104 | if (location.indexOf("Some recurrent event title you want to filter in") > -1) { 105 | return ( 106 | location + "" //markdown sintax 107 | ); 108 | } else if (title.indexOf("Another recurrent event title you want to filter in") > -1) { 109 | return location + ""; //markdown sintax 110 | } else { 111 | return location; 112 | } 113 | } else { 114 | return ""; 115 | } 116 | } 117 | --------------------------------------------------------------------------------