import os import json import requests from collections import defaultdict from bs4 import BeautifulSoup from datetime import datetime, timezone from htb_client import HTBClient from trilium_py.client import ETAPI # Get the absolute path of the script's directory script_dir = os.path.dirname(os.path.abspath(__file__)) # Construct the full path to the config.json file config_path = os.path.join(script_dir, 'config.json') # Load configuration from the JSON file with open(config_path, 'r') as f: config = json.load(f) # Accessing config values htb_code = config['htb_code'] trilium_server_url = config['trilium_server_url'] trilium_token = config['trilium_token'] trilium_sherlocks_folder = config['trilium_sherlocks_folder'] trilium_sherlocks_template_id = config['trilium_sherlocks_template_id'] def get_timestamp(machine): # Parse the release date string into a datetime object release_str = machine['release_date'] dt = datetime.strptime(release_str, "%Y-%m-%dT%H:%M:%S.%fZ") # Set the timezone to UTC dt = dt.replace(tzinfo=timezone.utc) # Get the timestamp timestamp = dt.timestamp() return timestamp print("[+] connecting to HTB") client = HTBClient(password=htb_code) print("[+] connecting to trilium") ea = ETAPI(trilium_server_url, trilium_token) print("[i] version: ", ea.app_info()['appVersion']) print("[i] HTB User:", client.user['id'], "-", client.user['name']) print("[+] gathering sherlocks info") categories = defaultdict(list) sherlocks = client.get_all_sherlocks() sherlocks.sort(key=get_timestamp) total_completed = 0 # Variable to track total completed sherlocks print(f"[i] Retrieved {len(sherlocks)} sherlocks") # Group sherlocks by their categories for sherlock in sherlocks: categories[sherlock['category_name']].append(sherlock) if sherlock['progress'] == 100: total_completed += 1 # Increment total completed if challenge is owned # Print out the grouped sherlocks with the number of completed and total sherlocks in each category for category, grouped_sherlocks in categories.items(): total = len(grouped_sherlocks) completed = sum(1 for sherlock in grouped_sherlocks if sherlock['progress'] == 100) # Count completed challenges res = ea.search_note( search=f"note.title %= '{category}*'", ancestorNoteId=trilium_sherlocks_folder, ancestorDepth='eq1', limit=1, fastSearch=True, ) catId = "" if res['results'] and res['results'][0]['title'].lower() == category.lower()+" - "+str(completed)+" / "+str(total): # page exists - lets check if the details have changed ea.patch_note(noteId=res['results'][0]['noteId'], title=category+" - "+str(completed)+" / "+str(total)) catId = res['results'][0]['noteId'] print(f"[i] updated category: {category} - ({completed}/{total})") else: new_note = ea.create_note( parentNoteId=trilium_sherlocks_folder, type="text", title=category+" - "+str(completed)+" / "+str(total), content=" ", ) catId = new_note['note']['noteId'] print(f"[+] created category: {catId} {category} - ({completed}/{total})") for sherlock in grouped_sherlocks: #print(f" - ID: {challenge['id']}, Name: {challenge['name']}, Difficulty: {challenge['difficulty']}") res2 = ea.search_note( search=f"{sherlock['name']}", ancestorNoteId=catId, ancestorDepth='eq1', limit=1, fastSearch=True, ) # already exists update the values if res2['results'] and res2['results'][0]['title'].lower() == sherlock['name'].lower(): print(f"[i] found ID: {sherlock['id']}, Name: {sherlock['name']}, Difficulty: {sherlock['difficulty']}") for attribute in res2['results'][0]['attributes']: if attribute['name'] == "Difficulty": ea.patch_attribute(attributeId=attribute['attributeId'], value=sherlock['difficulty']) if attribute['name'] == "Released": release_str = sherlock['release_date'] release_date = datetime.strptime(release_str, "%Y-%m-%dT%H:%M:%S.%fZ") formatted_release_date = release_date.strftime("%d %B %Y") ea.patch_attribute(attributeId=attribute['attributeId'], value=formatted_release_date) if attribute['name'] == "Solved": ea.patch_attribute(attributeId=attribute['attributeId'], value=str(sherlock['progress'])) if attribute['name'] == "cssClass": # Determine the cssClass value based on the progress if sherlock['progress'] == 0: ea.patch_attribute(attributeId=attribute['attributeId'], value="todo") elif sherlock['progress'] == 100: ea.patch_attribute(attributeId=attribute['attributeId'], value="done") elif 0 < sherlock['progress'] < 100: ea.patch_attribute(attributeId=attribute['attributeId'], value="inprogress") else: # doesnt already exist, create page release_str = sherlock['release_date'] release_date = datetime.strptime(release_str, "%Y-%m-%dT%H:%M:%S.%fZ") formatted_release_date = release_date.strftime("%d %B %Y") new_note = ea.create_note( parentNoteId=catId, type="text", title=sherlock['name'], content=" ", ) ea.create_attribute(attributeId=None, isInheritable=False, noteId=new_note['note']['noteId'], type="relation", name="template", value=trilium_sherlocks_template_id) ea.create_attribute(attributeId=None, isInheritable=False, noteId=new_note['note']['noteId'], type="label", name="Difficulty", value=sherlock['difficulty']) ea.create_attribute(attributeId=None, isInheritable=False, noteId=new_note['note']['noteId'], type="label", name="Released", value=formatted_release_date) ea.create_attribute(attributeId=None, isInheritable=False, noteId=new_note['note']['noteId'], type="label", name="Solved", value=str(sherlock['progress'])) if sherlock['progress'] == 0: ea.create_attribute(attributeId=None, isInheritable=False, noteId=new_note['note']['noteId'], type="label", name="cssClass", value="todo") elif sherlock['progress'] == 100: ea.create_attribute(attributeId=None, isInheritable=False, noteId=new_note['note']['noteId'], type="label", name="cssClass", value="done") elif 0 < sherlock['progress'] < 100: ea.create_attribute(attributeId=None, isInheritable=False, noteId=new_note['note']['noteId'], type="label", name="cssClass", value="inprogress") print(f"[+] created ID: {sherlock['id']}, Name: {sherlock['name']}, Difficulty: {sherlock['difficulty']}") print("[+] updating folder name ") ea.patch_note(noteId=trilium_sherlocks_folder,title="Sherlocks - "+str(total_completed)+" / "+str(len(sherlocks)))