- 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_challenges_folder = config['trilium_challenges_folder']
- trilium_challenges_template_id = config['trilium_challenges_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 challenges info")
- categories = defaultdict(list)
- challenges = client.get_all_challenges()
- challenges.sort(key=get_timestamp)
- total_completed = 0 # Variable to track total completed challenges
-
- print(f"[i] Retrieved {len(challenges)} challenges")
-
- # Group challenges by their categories
- for challenge in challenges:
- categories[challenge['category_name']].append(challenge)
- if challenge['is_owned']:
- total_completed += 1 # Increment total completed if challenge is owned
-
- # Print out the grouped challenges with the number of completed and total challenges in each category
- for category, grouped_challenges in categories.items():
- total = len(grouped_challenges)
- completed = sum(1 for challenge in grouped_challenges if challenge['is_owned']) # Count completed challenges
-
- res = ea.search_note(
- search=f"note.title %= '{category}*'",
- ancestorNoteId=trilium_challenges_folder,
- ancestorDepth='eq1',
- limit=1,
- fastSearch=True,
- )
-
- catId = ""
- if res['results'] and res['results'][0]['title'].split(' - ')[0].strip().lower() == category.lower():
- # 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_challenges_folder,
- type="text",
- title=category+" - "+str(completed)+" / "+str(total),
- content=" ",
- )
- catId = new_note['note']['noteId']
- print(f"[+] created category: {catId} {category} - ({completed}/{total})")
-
- for challenge in grouped_challenges:
- #print(f" - ID: {challenge['id']}, Name: {challenge['name']}, Difficulty: {challenge['difficulty']}")
- res2 = ea.search_note(
- search=f"{challenge['name']}",
- ancestorNoteId=catId,
- ancestorDepth='eq1',
- limit=1,
- fastSearch=True,
- )
- # already exists update the values
- if res2['results'] and res2['results'][0]['title'].lower() == challenge['name'].lower():
- print(f"[i] found ID: {challenge['id']}, Name: {challenge['name']}, Difficulty: {challenge['difficulty']}")
- #print(f"Search response for challenge '{challenge['name']}': {res2}")
- for attribute in res2['results'][0]['attributes']:
- if attribute['name'] == "Difficulty":
- ea.patch_attribute(attributeId=attribute['attributeId'], value=challenge['difficulty'])
- if attribute['name'] == "Released":
- release_str = challenge['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":
- if challenge['is_owned']:
- ea.patch_attribute(attributeId=attribute['attributeId'], value="done")
- else:
- ea.patch_attribute(attributeId=attribute['attributeId'], value=" ")
- if attribute['name'] == "cssClass":
- if challenge['is_owned']:
- ea.patch_attribute(attributeId=attribute['attributeId'], value="done")
- else:
- ea.patch_attribute(attributeId=attribute['attributeId'], value="todo")
-
- else: # doesnt already exist, create page
- release_str = challenge['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=challenge['name'],
- content=" ",
- )
- ea.create_attribute(attributeId=None, isInheritable=False, noteId=new_note['note']['noteId'], type="relation", name="template", value=trilium_challenges_template_id)
- ea.create_attribute(attributeId=None, isInheritable=False, noteId=new_note['note']['noteId'], type="label", name="Difficulty", value=challenge['difficulty'])
- ea.create_attribute(attributeId=None, isInheritable=False, noteId=new_note['note']['noteId'], type="label", name="Released", value=formatted_release_date)
- if challenge['is_owned']:
- ea.create_attribute(attributeId=None, isInheritable=False, noteId=new_note['note']['noteId'], type="label", name="cssClass", value="done")
- ea.create_attribute(attributeId=None, isInheritable=False, noteId=new_note['note']['noteId'], type="label", name="Solved", value="done")
- else:
- ea.create_attribute(attributeId=None, isInheritable=False, noteId=new_note['note']['noteId'], type="label", name="cssClass", value="todo")
- ea.create_attribute(attributeId=None, isInheritable=False, noteId=new_note['note']['noteId'], type="label", name="Solved", value=" ")
-
- print(f"[+] created ID: {challenge['id']}, Name: {challenge['name']}, Difficulty: {challenge['difficulty']}")
-
- print("[+] updating folder name ")
- ea.patch_note(noteId=trilium_challenges_folder,title="Challenges - "+str(total_completed)+" / "+str(len(challenges)))