// This code is licensed under the same terms as Habitica: // https://raw.githubusercontent.com/HabitRPG/habitrpg/develop/LICENSE /* ========================================== */ /* [Users] Required script data to fill in */ /* ========================================== */ const USER_ID = "PasteYourUserIdHere" const API_TOKEN = "PasteYourApiTokenHere" // Do not share this to anyone const WEB_APP_URL = "PasteGeneratedWebAppUrlHere" /* ========================================== */ /* [Users] Required customizations to fill in */ /* ========================================== */ // Which version do you want to use? // If you want to heal a set number when leveling up (such as 4.5 HP), select 2. // If you want to heal a percent of your remaining health (such as 25%), select 3. const VERSION = 2 // How much HP do you want to gain each level-up? Fill in the correct one (set value or percent of remaining health) const HP_SET_VALUE_PER_LEVEL = 4.5 // Enter a number between 0 and 50. const HP_PERCENT_PER_LEVEL = 50.0 // Do not put a percent sign after the number here. Enter a number between 0 and 100. /* ========================================== */ /* [Users] Optional customizations to fill in */ /* ========================================== */ // Do you want a button that lets you enter/exit Partial Healing Mode? If yes, change the 0 to a 1 below. // If you don't have a button, you can still enter/exit the Mode by manually running the function "togglePartialHealingMode" const PARTIAL_HEALING_TOGGLE_BUTTON = 0 /* ========================================== */ /* [Users] Do not edit code below this line */ /* ========================================== */ const AUTHOR_ID = "0034eb14-b4d8-494e-8386-d3f33cff7922" const SCRIPT_NAME = "Partial Healing Mode" const HEADERS = { "x-client" : AUTHOR_ID + " - " + SCRIPT_NAME, "x-api-user" : USER_ID, "x-api-key" : API_TOKEN, } const scriptProperties = PropertiesService.getScriptProperties(); // Constants can have properties changed // Button title and notes change based on whether you are in Partial Healing Mode or not const PARTIAL_HEALING_MODE_TEXT_START = "Click to " const PARTIAL_HEALING_MODE_TEXT_END = " Partial Healing Mode" const PARTIAL_HEALING_MODE_NOTES_START = "You are " const PARTIAL_HEALING_MODE_NOTES_MID = " in Partial Healing Mode. Click this button to " const PARTIAL_HEALING_MODE_NOTES_END = " that Mode." const PARTIAL_HEALING_MODE_TEXT_EXIT = PARTIAL_HEALING_MODE_TEXT_START + "exit" + PARTIAL_HEALING_MODE_TEXT_END const PARTIAL_HEALING_MODE_TEXT_ENTER = PARTIAL_HEALING_MODE_TEXT_START + "enter" + PARTIAL_HEALING_MODE_TEXT_END const PARTIAL_HEALING_MODE_NOTES_EXIT = PARTIAL_HEALING_MODE_NOTES_START + "currently" + PARTIAL_HEALING_MODE_NOTES_MID + "exit" + PARTIAL_HEALING_MODE_NOTES_END const PARTIAL_HEALING_MODE_NOTES_ENTER = PARTIAL_HEALING_MODE_NOTES_START + "not currently" + PARTIAL_HEALING_MODE_NOTES_MID + "enter" + PARTIAL_HEALING_MODE_NOTES_END const PARTIAL_HEALING_MODE_ALIAS = "PARTIAL_HEALING_TOGGLE" const PARTIAL_HEALING_MODE_VALUE = "0" // Button initializes assuming they are in Partial Healing Mode const PARTIAL_HEALING_MODE_BUTTON = { "text": PARTIAL_HEALING_MODE_TEXT_EXIT, "type": "reward", "alias": PARTIAL_HEALING_MODE_ALIAS, "notes": PARTIAL_HEALING_MODE_NOTES_EXIT, "value": PARTIAL_HEALING_MODE_VALUE, } const HP_KEY = "HP_KEY" const LVL_KEY = "LVL_KEY" const PARTIAL_HEALING_MODE_ACTIVE_KEY = "PARTIAL_HEALING_MODE_ACTIVE_KEY" var hpKey = "" var lvlKey = "" var partialHealingModeActiveKey = "" function doOneTimeSetup() { // Create button if user indicated they want it if (PARTIAL_HEALING_TOGGLE_BUTTON == 1) { api_createNewTaskForUser([PARTIAL_HEALING_MODE_BUTTON]) } // create the webhook const options = { "scored" : true, } const payload = { "url" : WEB_APP_URL, "label" : SCRIPT_NAME + " Webhook", "type" : "taskActivity", "options" : options, } apiMult_createNewWebhookNoDuplicates(payload) // set script properties so they carry over to next session initScriptProperties() } // do things when the webhook runs function doPost(e) { const dataContents = JSON.parse(e.postData.contents) const type = dataContents.type const task = dataContents.task if (type == "scored") { // Sanitize task alias let sanitizedAlias = "sanitized" // This will be the value if undefined, null, or blank if ( (task.alias != undefined) && (task.alias != null) && (task.alias != "") ) { sanitizedAlias = task.alias } var partialHealingModeActiveKey = PARTIAL_HEALING_MODE_ACTIVE_KEY var partialHealingModeActive = Number(scriptProperties.getProperty(partialHealingModeActiveKey)) // If they press the toggle button, do so if (sanitizedAlias == PARTIAL_HEALING_MODE_ALIAS) { togglePartialHealingMode() } else { // If they didn't press the toggle button, do things as normal if they are in Partial Healing Mode if (partialHealingModeActive == 1) { var hpKey = HP_KEY var lvlKey = LVL_KEY var prevHp = Number(scriptProperties.getProperty(hpKey)) var prevLvl = Number(scriptProperties.getProperty(lvlKey)) const response = api_getAuthenticatedUserProfile("stats") user = JSON.parse(response).data let currentHp = user.stats.hp let currentLvl = user.stats.lvl // See if they've leveled up if (currentLvl > prevLvl) { // If yes, run correct version of Partial Healing if ( VERSION == 2 ) { // Value to heal must be between 0 and 50 let valueToHeal = sanitizeInput(HP_SET_VALUE_PER_LEVEL, 0, 50) // New HP must be between 0 and 50 let newHp = sanitizeInput(valueToHeal + prevHp,0, 50) // Heal the correct amount. api_updateUser({"stats.hp" : newHp}) // Save new HP scriptProperties.setProperty(hpKey, newHp) } else if ( VERSION == 3 ) { // Percent to heal must be between 0 and 100 let percentToHeal = sanitizeInput (HP_PERCENT_PER_LEVEL, 0, 100) let healing = ( percentToHeal / 100 ) * ( 50 - prevHp ) // New HP must be between 0 and 50 let newHpPercent = sanitizeInput(healing + prevHp,0, 50) // Heal the correct amount. api_updateUser({"stats.hp" : newHpPercent}) // Save new HP scriptProperties.setProperty(hpKey, newHpPercent) } // Irrespective of version, save new level scriptProperties.setProperty(lvlKey, currentLvl) } // If they haven't leveled up since the last time the script ran... else { // If previous and current HP are the same, no need to re-save. Otherwise, do. if (prevHp != currentHp) { scriptProperties.setProperty(hpKey, currentHp) } } } } } return HtmlService.createHtmlOutput() } // Enters or exits from Partial Healing Mode function togglePartialHealingMode(){ var partialHealingModeActiveKey = PARTIAL_HEALING_MODE_ACTIVE_KEY var partialHealingModeActive = Number(scriptProperties.getProperty(partialHealingModeActiveKey)) // If not in Partial Healing Mode, enter it. if (partialHealingModeActive == 0) { scriptProperties.setProperty(partialHealingModeActiveKey, 1) // Save HP and level initiatePartialHealingValues() // Update button if the user created it if (PARTIAL_HEALING_TOGGLE_BUTTON == 1) { api_updateTask(PARTIAL_HEALING_MODE_ALIAS, {"text": PARTIAL_HEALING_MODE_TEXT_EXIT, "notes": PARTIAL_HEALING_MODE_NOTES_EXIT,}) } } // If in Partial Healing Mode, exit it. else if (partialHealingModeActive == 1) { scriptProperties.setProperty(partialHealingModeActiveKey, 0) // Update button if the user created it if (PARTIAL_HEALING_TOGGLE_BUTTON == 1) { api_updateTask(PARTIAL_HEALING_MODE_ALIAS, {"text": PARTIAL_HEALING_MODE_TEXT_ENTER, "notes": PARTIAL_HEALING_MODE_NOTES_ENTER,}) } } } // Create custom reward buttons function api_createNewTaskForUser(payload) { var params = { "method" : "post", "headers" : HEADERS, "contentType" : "application/json", "payload" : JSON.stringify(payload), // Rightmost button goes on top "muteHttpExceptions" : true, } var url = "https://habitica.com/api/v3/tasks/user" UrlFetchApp.fetch(url, params) } // Create a webhook if no duplicate exists function apiMult_createNewWebhookNoDuplicates(payload) { const response = api_getWebhooks() const webhooks = JSON.parse(response).data var duplicateExists = 0 for (var i in webhooks) { if (webhooks[i].label == payload.label) { duplicateExists = 1 } } // If webhook to be created doesn't exist yet if (!duplicateExists) { api_createNewWebhook(payload) } } // Used to see existing webhooks, and therefore if there's a duplicate function api_getWebhooks() { const params = { "method" : "get", "headers" : HEADERS, "muteHttpExceptions" : true, } const url = "https://habitica.com/api/v3/user/webhook" return UrlFetchApp.fetch(url, params) } // Creates a webhook (as part of the "don't make it if there's a duplicate" function) function api_createNewWebhook(payload) { const params = { "method" : "post", "headers" : HEADERS, "contentType" : "application/json", "payload" : JSON.stringify(payload), "muteHttpExceptions" : true, } const url = "https://habitica.com/api/v3/user/webhook" return UrlFetchApp.fetch(url, params) } // Gets user info so I can use it, especially stats like mana, experience, and level function api_getAuthenticatedUserProfile(userFields) { const params = { "method" : "get", "headers" : HEADERS, "muteHttpExceptions" : true, } var url = "https://habitica.com/api/v3/user" if (userFields != "") { url += "?userFields=" + userFields } return UrlFetchApp.fetch(url, params) } // Sets initial properties that will be used/saved later. function initScriptProperties() { scriptProperties.setProperty(PARTIAL_HEALING_MODE_ACTIVE_KEY, 1) // Also save HP and level; it's in a separate function on purpose. initiatePartialHealingValues() } // Saves initial HP and Level values function initiatePartialHealingValues(){ var hpKey = HP_KEY var lvlKey = LVL_KEY const responseInit = api_getAuthenticatedUserProfile("stats") user = JSON.parse(responseInit).data var currentHp = user.stats.hp var currentLvl = user.stats.lvl scriptProperties.setProperty(hpKey, currentHp) scriptProperties.setProperty(lvlKey, currentLvl) } // Ensures that the input is between the minimum and maximum. Can use "none" in either argument to indicate if there is either no minimum or no maximum. function sanitizeInput(input, minimum, maximum) { if (minimum == "none"){ // Condition for if there is no minimum if ( input > maximum ) { return maximum } else { return input } } else if (maximum == "none"){ // Condition for if there is no maximum if ( input < minimum ) { return minimum } else { return input } } else { // Condition where there is both minimum and maximum if ( input < minimum ) { return minimum; } else if ( input > maximum ) { return maximum } else { return input } } } function api_updateUser(payload) { const params = { "method" : "put", "headers" : HEADERS, "contentType" : "application/json", "payload" : JSON.stringify(payload), "muteHttpExceptions" : true, } const url = "https://habitica.com/api/v3/user" return UrlFetchApp.fetch(url, params) } // Update a task function api_updateTask(taskIdOrAlias, payload) { const params = { "method" : "put", "headers" : HEADERS, "contentType" : "application/json", "payload" : JSON.stringify(payload), "muteHttpExceptions" : true, } const url = "https://habitica.com/api/v3/tasks/" + taskIdOrAlias return UrlFetchApp.fetch(url, params) } // Send a notification as a private message regardless of if they're enabled function api_sendPrivateMessageAlways(payload) { const params = { "method" : "post", "headers" : HEADERS, "contentType" : "application/json", "payload" : JSON.stringify(payload), "muteHttpExceptions" : true, } const url = "https://habitica.com/api/v3/members/send-private-message" return UrlFetchApp.fetch(url, params) } // FUNCTIONS FOR DEBUGGING. SCRIPT DOES NOT USE THEM, THEY MUST BE TRIGGERED MANUALLY // In case the script saved the wrong value for HP and level, use FCV to set them to the correct amounts and then run the function below to save them function debugResetSavedHpAndLevel() { const responseInit = api_getAuthenticatedUserProfile("stats") user = JSON.parse(responseInit).data let currentHp = user.stats.hp let currentLvl = user.stats.lvl scriptProperties.setProperty(HP_KEY, currentHp) scriptProperties.setProperty(LVL_KEY, currentLvl) } // Retrieves saved values and sends them in a private message function debugGetSavedHpAndLevel(){ let savedHp = Number(scriptProperties.getProperty(HP_KEY)) let savedLvl = scriptProperties.getProperty(LVL_KEY) // saving these will make my life easier let MSG_JOINER_BEFORE = " is `" let MSG_JOINER_AFTER = "`, " api_sendPrivateMessageAlways({"message" : "Saved values are as follows: savedHp is `" + savedHp + MSG_JOINER_AFTER + "savedLvl" + MSG_JOINER_BEFORE + savedLvl + "`", "toUserId" : USER_ID}) } // Mannually edits and saves the selected value (in case it saved incorrectly) function debugManuallyEditSavedValue() { // Fill them in below. savedHp is shown as a sample. let newValue = 0 let key = HP_KEY // Save scriptProperties.setProperty(key, newValue) }