diff --git a/costume.py b/costume.py new file mode 100755 index 0000000..b8b8f7a --- /dev/null +++ b/costume.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python3 +import argparse +import json +import os +import sys +import requests + +API_URL = "https://habitica.com/api/v3/user" + +def get_habitica_headers(): + user_id = os.getenv("HABITICA_USER_ID") + api_token = os.getenv("HABITICA_API_TOKEN") + + if not user_id or not api_token: + print("Error: Environment variables HABITICA_USER_ID and HABITICA_API_TOKEN must be set.", file=sys.stderr) + sys.exit(1) + + return { + "x-api-user": user_id, + "x-api-key": api_token, + "x-client": f"{user_id}-CustomizationScript" + } + +def fetch_habitica_gear(): + """Fetches only the costume, background, pet, and mount from the user profile.""" + try: + response = requests.get(API_URL, headers=get_habitica_headers()) + response.raise_for_status() + except requests.exceptions.RequestException as e: + print(f"Error communicating with Habitica API: {e}", file=sys.stderr) + sys.exit(1) + + data = response.json().get("data", {}) + + # Extract specific fields + items = data.get("items", {}) + gear = items.get("gear", {}) + costume = gear.get("costume", {}) + background = data.get("preferences", {}).get("background", "default") + pet = items.get("currentPet", "") + mount = items.get("currentMount", "") + + customizations = { + "costume": costume, + "background": background, + "pet": pet, + "mount": mount + } + + print(customizations) + return customizations + +def save_costume(filename): + # print("Fetching equipment and costume data from Habitica...") + gear_data = fetch_habitica_gear() + with open(filename, "w", encoding="utf-8") as f: + json.dump(gear_data, f, indent=4) + + +def equip(type, name): + try: + response = requests.post(f"{API_URL}/equip/{type}/{name}", + headers=get_habitica_headers()) + response.raise_for_status() + print(f"Successfully equiped '{name}'.") + except requests.exceptions.RequestException as e: + print(f"Error communicating with Habitica API: {e}", file=sys.stderr) + if hasattr(e, 'response') and e.response is not None: + print(f"Server response: {e.response.text}", file=sys.stderr) + sys.exit(1) + +def set_background(name): + bg_payload = { + "preferences.background": name + } + try: + res = requests.put(f"{API_URL}", + headers=get_habitica_headers(), json=bg_payload) + res.raise_for_status() + print(f"Successfully set background to: {name}") + except requests.exceptions.RequestException as e: + print(f"Error setting background: {e}", file=sys.stderr) + if hasattr(e, 'response') and e.response is not None: + print(f"Server response: {e.response.text}", file=sys.stderr) + +def set_costume(data): + for k,v in data.get("costume").items(): + if v and not v.endswith("base_0"): + equip("costume", v) + + if "pet" in data: + equip("pet", data.get("pet")) + + # if "mount" in data: + # equip("mount", data.get("mount")) + + # TODO: Not sure how to set the background! + if "background" in data: + set_background(data["background"]) + +def load_costume(filename): + if os.path.exists(filename): + with open(filename, "r", encoding="utf-8") as f: + gear_data = json.load(f) + # Update defaults with values from the file + set_costume(gear_data) + +def store_all(filename): + try: + response = requests.get(API_URL, headers=get_habitica_headers()) + response.raise_for_status() + except requests.exceptions.RequestException as e: + print(f"Error communicating with Habitica API: {e}", file=sys.stderr) + sys.exit(1) + + data = response.json().get("data", {}) + with open(filename, "w", encoding="utf-8") as f: + json.dump(data, f, indent=4) + +def parse_arguments(): + parser = argparse.ArgumentParser(description="Costume configuration script") + parser.add_argument( + "--load", + type=str, + help="Path to a JSON file containing saved costume values" + ) + parser.add_argument( + "--save", + type=str, + help="Path to a JSON file to save your costume values" + ) + parser.add_argument( + "-a", "--all", + action=argparse.BooleanOptionalAction, + help="Download and store all data in 'all.json'" + ) + return parser.parse_args() + +def main(): + args = parse_arguments() + + if args.save: + save_costume(args.save) + elif args.load: + load_costume(args.load) + elif args.all: + store_all("all.json") + +if __name__ == "__main__": + main() diff --git a/costumes/autumn_wood_elf.json b/costumes/autumn_wood_elf.json new file mode 100644 index 0000000..4929faa --- /dev/null +++ b/costumes/autumn_wood_elf.json @@ -0,0 +1,15 @@ +{ + "costume": { + "weapon": "weapon_armoire_jeweledArcherBow", + "armor": "armor_armoire_woodElfArmor", + "head": "head_armoire_woodElfHelm", + "shield": "shield_base_0", + "back": "back_base_0", + "headAccessory": "headAccessory_base_0", + "eyewear": "eyewear_special_blackTopFrame", + "body": "body_special_summerMage" + }, + "background": "vegetable_garden", + "pet": "", + "mount": "" +} \ No newline at end of file diff --git a/costumes/beholder.json b/costumes/beholder.json new file mode 100644 index 0000000..f1081d3 --- /dev/null +++ b/costumes/beholder.json @@ -0,0 +1,15 @@ +{ + "costume": { + "weapon": "weapon_special_fall2022Healer", + "armor": "armor_special_fall2022Healer", + "head": "head_special_fall2022Healer", + "shield": "shield_special_fall2022Healer", + "back": "back_base_0", + "headAccessory": "headAccessory_base_0", + "eyewear": "eyewear_base_0", + "body": "body_base_0" + }, + "background": "magic_door_in_forest", + "pet": "", + "mount": "" +} \ No newline at end of file diff --git a/costumes/mystic_forest.json b/costumes/mystic_forest.json new file mode 100644 index 0000000..a836679 --- /dev/null +++ b/costumes/mystic_forest.json @@ -0,0 +1,15 @@ +{ + "costume": { + "weapon": "weapon_special_spring2019Mage", + "armor": "armor_special_spring2019Mage", + "head": "head_special_spring2019Mage", + "shield": "shield_base_0", + "back": "back_base_0", + "headAccessory": "headAccessory_base_0", + "eyewear": "eyewear_special_redHalfMoon", + "body": "body_base_0" + }, + "background": "magic_door_in_forest", + "pet": "", + "mount": "" +} \ No newline at end of file diff --git a/costumes/on_the_streets.json b/costumes/on_the_streets.json new file mode 100644 index 0000000..938a10d --- /dev/null +++ b/costumes/on_the_streets.json @@ -0,0 +1,15 @@ +{ + "costume": { + "weapon": "weapon_mystery_301404", + "armor": "armor_armoire_plagueDoctorOvercoat", + "head": "head_mystery_301405", + "shield": "shield_base_0", + "back": "back_base_0", + "headAccessory": "headAccessory_base_0", + "eyewear": "eyewear_special_blackTopFrame", + "body": "body_armoire_cozyScarf" + }, + "background": "sunny_street_with_shops", + "pet": "Gryphon-Base", + "mount": "" +} \ No newline at end of file diff --git a/costumes/the_reader.json b/costumes/the_reader.json new file mode 100644 index 0000000..df4073e --- /dev/null +++ b/costumes/the_reader.json @@ -0,0 +1,15 @@ +{ + "costume": { + "weapon": "weapon_base_0", + "armor": "armor_wizard_3", + "head": "head_base_0", + "shield": "shield_mystery_201709", + "back": "back_mystery_201709", + "headAccessory": "headAccessory_base_0", + "eyewear": "eyewear_special_blackHalfMoon", + "body": "body_special_summerMage" + }, + "background": "cozy_library", + "pet": "", + "mount": "" +} \ No newline at end of file