- '''
- ########################
- # SETUP
- ########################
- > pip3 install trilium-py
- > pip3 install pyhackthebox
-
- - get trilium token in trilium options->ETAPI and "create new token"
- - get trilium_htb_folder click on note to become folder -> "note info". Note ID is there.
- - set the folder's owned attributes to:
- #label:user=promoted,single,text #label:root=promoted,single,text #label:respect=promoted,single,text #user=0 #root=0 #respect=0
-
- - create a note named "HTBMachineTemplate" and set it's "owned attributes" to:
- #template #label:User=promoted,single,text #label:Root=promoted,single,text #label:Tags=promoted,single,text
-
- - get this page's ID and put in "trilium_template_id"
- - to get "in progress" todo colour - add to taskmanager/implementation/CSS:
-
- span.fancytree-node.inprogress .fancytree-title {
- color: orange !important;
- }
- '''
-
- ########################
- # IGNORE THESE
- ########################
-
- import requests
- from bs4 import BeautifulSoup
- from datetime import timezone
- from hackthebox import HTBClient
- from trilium_py.client import ETAPI
-
- ########################
- # EDIT BELOW HERE
- ########################
-
- htb_email = ''
- htb_pass = ''
- trilium_server_url = ''
- trilium_token = ''
- trilium_htb_folder = ''
- trilium_template_id = ''
-
- ########################
- # LEAVE BELOW HERE
- ########################
-
- def generate_newpage(machine):
- no = "<span style=\"color:hsl(0,75%,60%);\">No</span>"
- yes = "<span style=\"color:hsl(120,75%,60%);\">Yes</span>"
- user_colour = no
- if machine.user_owned:
- user_colour = yes
- root_colour = no
- if machine.root_owned:
- root_colour = yes
-
- status = "Active"
- if machine.retired:
- status = "Retired"
-
- html = """
- <figure class="image image-style-align-left image_resized" style="width:12.28%;">
- <img src="https://www.hackthebox.com{avatar}">
- </figure>
- <p>
- <strong>OS:</strong> {os}<br>
- <strong>Difficulty:</strong> {difficulty}<br>
- <strong>Rating:</strong> {rating} / 5<br>
- <strong>Points:</strong> {points}<br>
- <strong>User / Root: </strong> {user_colour} / {root_colour}<br>
- <strong>Released:</strong> {release_date}<br>
- <strong>State:</strong> {status}
- </p>
- <hr>
- <h2>Notes</h2>
- <p> </p>
- """.format(
- os=machine.os,
- difficulty=machine.difficulty,
- rating=machine.stars,
- points=machine.points,
- release_date=machine.release_date.strftime("%d %B %Y"),
- user_colour=user_colour,
- root_colour=root_colour,
- status = status,
- avatar = machine.avatar,
- )
-
- return html
-
- def get_timestamp(machine):
- dt = machine.release_date
- timestamp = dt.replace(tzinfo=timezone.utc).timestamp()
- return timestamp
-
- print("[+] connecting to HTB")
- client = HTBClient(email=htb_email, password=htb_pass)
- print("[+] connecting to trilium")
- ea = ETAPI(trilium_server_url, trilium_token)
-
- print("[i] HTB User:",client.user.id, "-", client.user.name)
- print("[i] user owns:", client.user.user_owns, "| Root owns:", client.user.root_owns, "| Respect:", client.user.respects)
-
- master_folder = ea.get_note(trilium_htb_folder)
- for attribute in master_folder['attributes']:
- if attribute['name'] == "user":
- if attribute['value'] != str(client.user.user_owns):
- print("[+] updating user owns (folder attribute)")
- ea.patch_attribute(attributeId=attribute['attributeId'], value=str(client.user.user_owns))
- if attribute['name'] == "root":
- if attribute['value'] != str(client.user.root_owns):
- print("[+] updating root owns (folder attribute)")
- ea.patch_attribute(attributeId=attribute['attributeId'], value=str(client.user.root_owns))
- if attribute['name'] == "respect":
- if attribute['value'] != str(client.user.respects):
- print("[+] updating respect (folder attribute)")
- ea.patch_attribute(attributeId=attribute['attributeId'], value=str(client.user.respects))
-
- print("[+] gathering machines info")
-
- machines = client.get_machines()
- machines += client.get_machines(None, True)
- machines.sort(key=get_timestamp)
- # starts with oldest, ends with newest...
-
- machine_count = 0
- completed_count = 0
- for machine in machines:
- machine_count += 1
- print('processing: ',machine_count, "/", len(machines), "("+machine.name+") " , end='\r')
-
- res = ea.search_note(
- search="\""+machine.name+"\"",
- ancestorNoteId=trilium_htb_folder,
- ancestorDepth='eq1',
- limit=1,
- fastSearch=True,
- )
-
- if res['results'] and machine.name == res['results'][0]['title']:
- # page exists - lets check if the details have changed
-
- current_html = ea.get_note_content(noteId=res['results'][0]['noteId'])
- current_soup = BeautifulSoup(current_html, 'html.parser')
- current_paragraph = current_soup.find_all('p')[0].text
-
- new_html = generate_newpage(machine)
- new_soup = BeautifulSoup(new_html, 'html.parser')
- new_paragraph = new_soup.find_all('p')[0].text
-
- # current page contains first paragraph of "blank" (useful for when it doesnt create or find the note properly.. shouldnt get here)
- if current_paragraph == "blank":
- ea.update_note_content(noteId=res['results'][0]['noteId'], content=new_html)
- ea.create_attribute(attributeId=None, isInheritable=False, noteId=res['results'][0]['noteId'], type="relation", name="template", value=trilium_template_id)
- if machine.user_owned and machine.root_owned:
- ea.create_attribute(attributeId=None, isInheritable=False, noteId=res['results'][0]['noteId'], type="label", name="cssClass", value="done")
- else:
- if machine.user_owned or machine.root_owned:
- ea.create_attribute(attributeId=None, isInheritable=False, noteId=res['results'][0]['noteId'], type="label", name="cssClass", value="inprogress")
- else:
- ea.create_attribute(attributeId=None, isInheritable=False, noteId=res['results'][0]['noteId'], type="label", name="cssClass", value="todo")
- # re-get the current content
- current_html = ea.get_note_content(noteId=res['results'][0]['noteId'])
- current_soup = BeautifulSoup(current_html, 'html.parser')
-
- if current_paragraph != new_paragraph:
-
- # details have updated!
- print("[+] updating page:",machine.name, "-> "+res['results'][0]['title']+" ")
- replacement = current_soup.find('p')
- replacement.replace_with( new_soup.find_all('p')[0] )
- ea.update_note_content(noteId=res['results'][0]['noteId'], content=current_soup)
-
- # now to update the label
- for attribute in res['results'][0]['attributes']:
- if attribute['name'] == "cssClass":
- if machine.user_owned and machine.root_owned:
- ea.patch_attribute(attributeId=attribute['attributeId'], value="done")
- else:
- if machine.user_owned or machine.root_owned:
- ea.patch_attribute(attributeId=attribute['attributeId'], value="inprogress")
- else:
- ea.patch_attribute(attributeId=attribute['attributeId'], value="todo")
-
- else:
- # title does not exist - create the note
- html = generate_newpage(machine)
- new_note = ea.create_note(
- parentNoteId=trilium_htb_folder,
- type="text",
- title=machine.name,
- content=html,
- )
- print("[+] created note:", machine.name, " ")
-
- ea.create_attribute(attributeId=None, isInheritable=False, noteId=new_note['note']['noteId'], type="relation", name="template", value=trilium_template_id)
- if machine.user_owned and machine.root_owned:
- ea.create_attribute(attributeId=None, isInheritable=False, noteId=new_note['note']['noteId'], type="label", name="cssClass", value="done")
- else:
- if machine.user_owned or machine.root_owned:
- ea.create_attribute(attributeId=None, isInheritable=False, noteId=new_note['note']['noteId'], type="label", name="cssClass", value="inprogress")
- else:
- ea.create_attribute(attributeId=None, isInheritable=False, noteId=new_note['note']['noteId'], type="label", name="cssClass", value="todo")
-
- if machine.user_owned and machine.root_owned:
- completed_count += 1
-
- print("[+] updating folder name ")
- ea.patch_note(noteId=trilium_htb_folder,title="Machines - "+str(completed_count)+" / "+str(len(machines)))
-
- print("[=] processed", machine_count)
-