| | import os |
---|
| | import json |
---|
| | import requests |
---|
| | import re |
---|
| | import unicodedata |
---|
| | from collections import defaultdict |
---|
| | from bs4 import BeautifulSoup |
---|
| | from datetime import datetime, timezone |
---|
| | from htb_client import HTBClient |
---|
| |
---|
| | total_completed = 0 # Variable to track total completed challenges |
---|
| | |
---|
| | print(f"[i] Retrieved {len(challenges)} challenges") |
---|
| | |
---|
| | def normalise_title(title): |
---|
| | # Strip accents, smart quotes, etc. |
---|
| | title = unicodedata.normalize("NFKD", title) |
---|
| | # Replace fancy punctuation |
---|
| | title = title.replace("’", "'").replace("`", "'").replace("–", "-").replace("“", '"').replace("”", '"') |
---|
| | # Remove excess whitespace |
---|
| | title = re.sub(r'\s+', ' ', title) |
---|
| | return title.strip().lower() |
---|
| | |
---|
| | # Group challenges by their categories |
---|
| | for challenge in challenges: |
---|
| | categories[challenge['category_name']].append(challenge) |
---|
| | if challenge['is_owned']: |
---|
| |
---|
| | 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}*'", |
---|
| | 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})") |
---|
| | if res['results']: |
---|
| | matched_note = res['results'][0] |
---|
| | existing_category = " - ".join(matched_note['title'].split(" - ")[:-1]) # Extract category portion |
---|
| | if existing_category.strip().lower() == category.lower(): |
---|
| | ea.patch_note( |
---|
| | noteId=matched_note['noteId'], |
---|
| | title=f"{category} - {completed} / {total}" |
---|
| | ) |
---|
| | catId = matched_note['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), |
---|
| | title=f"{category} - {completed} / {total}", |
---|
| | content=" ", |
---|
| | ) |
---|
| | catId = new_note['note']['noteId'] |
---|
| | print(f"[+] created category: {catId} {category} - ({completed}/{total})") |
---|
| | print(f"[+] Created category: {catId} {category} - ({completed}/{total})") |
---|
| | |
---|
| | for challenge in grouped_challenges: |
---|
| | #print(f" - ID: {challenge['id']}, Name: {challenge['name']}, Difficulty: {challenge['difficulty']}") |
---|
| | escaped_name = challenge['name'].replace("'", "\\'").replace(",", "\\,") |
---|
| | res2 = ea.search_note( |
---|
| | search=f"{challenge['name']}", |
---|
| | search=f"note.title = '{escaped_name}'", |
---|
| | ancestorNoteId=catId, |
---|
| | ancestorDepth='eq1', |
---|
| | orderBy=["title"], |
---|
| | 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") |
---|
| | |
---|
| | if res2['results']: |
---|
| | note_title = normalise_title(res2['results'][0]['title']) |
---|
| | challenge_title = normalise_title(challenge['name']) |
---|
| | |
---|
| | if note_title == challenge_title: |
---|
| | |
---|
| | 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") |
---|
| |
---|
| | |