diff --git a/.gitignore b/.gitignore | |
index 03c88fd..6e83ad8 100644 | |
--- a/.gitignore | |
+++ b/.gitignore | |
@@ -1 +1,4 @@ | |
.htaccess | |
+ctf-diary-generator/ctf-diary/ | |
+ctf/ | |
+/ctf.html | |
\ No newline at end of file | |
diff --git a/canvas.png b/canvas.png | |
new file mode 100644 | |
index 0000000..6be62f8 | |
Binary files /dev/null and b/canvas.png differ | |
diff --git a/ctf-diary-generator/generator.py b/ctf-diary-generator/generator.py | |
new file mode 100644 | |
index 0000000..b386f47 | |
--- /dev/null | |
+++ b/ctf-diary-generator/generator.py | |
@@ -0,0 +1,206 @@ | |
+import yaml, os, re | |
+from datetime import datetime | |
+from markdown import markdown | |
+from bs4 import BeautifulSoup, Comment | |
+#also requires pymdown-extensions | |
+ | |
+#note to self: to convert existing ctf diaries, check whether `\n([0-9a-zA-Z]+)` (or `^((?:[a-zA-Z0-9] ?)+)\n- ` for some messier writeups) looks like a chall name, then replace with `\n### $1` | |
+#remove all first level -s that i use for paragraph into 2 \ns | |
+#push all ```s to the left without spaces | |
+ | |
+#TODO aggregate stats (total ctfs, total solves, solve and co-solve amt, category solves breakdown, avg chall per ctf?, avg points?, avg solve count??) | |
+#exclude organized events | |
+ | |
+ | |
+CWD = os.path.dirname(os.path.realpath(__file__)) | |
+ | |
+alphanum = re.compile(r'[^A-Za-z0-9\- ]+') | |
+merge_space = re.compile(r'\s+') | |
+remove_formatting = lambda str: merge_space.sub('-', alphanum.sub('', str).strip()).lower() #for name formats | |
+ | |
+objects = {} | |
+ | |
+comments = {} | |
+ | |
+#read special events | |
+with open(f'{CWD}/ctf-diary/special.yml', encoding='utf-8') as s: | |
+ objects.update(yaml.load(s, Loader=yaml.Loader)) | |
+ | |
+#read all ctfs in record | |
+skipped_cmts = [] | |
+for file in os.listdir(f'{CWD}/ctf-diary/ctfs'): | |
+ if file.endswith('.yml'): | |
+ name = file[:-4] | |
+ #read metadata into objects | |
+ with open(f'{CWD}/ctf-diary/ctfs/{file}', encoding='utf-8') as s: | |
+ objects.update({name: yaml.load(s, Loader=yaml.Loader)}) | |
+ | |
+ #read comments; split according to headers ('### ') which should only be used by chall names, where the - is the start of comments | |
+ #the 3 ### should be good enough to differentiate headers from code comments, we will do sanity check when we actually map the comments anyway | |
+ try: | |
+ with open(f'{CWD}/ctf-diary/ctfs/comments/{name}.md', encoding='utf-8') as c: | |
+ assert c.read(4) == '### ' #the first line should already have a challenge, also discard the header | |
+ comments.update({name: {remove_formatting((sp:=v.split('\n', 1))[0]): sp[1] for v in c.read().split('\n### ')}}) | |
+ except Exception as e: | |
+ skipped_cmts.append(name) | |
+ if 'challenges' in objects[name] and not all('writeup-url' in chall and chall['writeup-url'] for chall in objects[name]['challenges']): #only print if ctf should have comments | |
+ print(f'Cannot read comments for {file} ({type(e).__name__}), skipping...') | |
+ | |
+#date is already datetime.date (thanks pyyaml) | |
+objects = dict(sorted(objects.items(), key=lambda item: item[1]['date'])) | |
+ | |
+#add year objects | |
+year = None | |
+templist = [] | |
+for k, v in objects.items(): | |
+ if year != v['date'].year: | |
+ year = v['date'].year | |
+ if templist: #ensure templist is not empty before inserting a year (since that always happen) | |
+ #no need to str(year) since .format nudge it into string anyway | |
+ templist.append((year, {'style': 'timeline-year', 'content': year, 'date': datetime.fromisoformat(f'{year}-01-01')})) | |
+ | |
+ templist.append((k,v)) | |
+ | |
+#apparently printing dicts autosorts it for you but iterating is fine | |
+templist.reverse() | |
+objects = dict(templist) | |
+ | |
+ | |
+with open(f'{CWD}/templates/ctf.html', encoding='utf-8') as cf: | |
+ ctf = cf.read() | |
+with open(f'{CWD}/templates/special.html', encoding='utf-8') as sf: | |
+ special = sf.read() | |
+with open(f'{CWD}/templates/diary.html', encoding='utf-8') as df: | |
+ diary = df.read() | |
+ | |
+ | |
+#generation helpers | |
+ | |
+added = [] | |
+def get_comment(chall, ctf): | |
+ name = remove_formatting(next(iter(chall.values()))) | |
+ if 'writeup-url' in chall and chall['writeup-url']: | |
+ return f'href="{chall["writeup-url"]}"' | |
+ elif ctf in comments and name in comments[ctf]: | |
+ added.append(f'{ctf}-{name}') | |
+ return f'data-comment="{ctf}-{name}"' | |
+ else: | |
+ if ctf not in skipped_cmts: #only print if the file is read, otherwise its just redundant | |
+ print(f"{ctf}-{name} has no comments, name mismatch?") | |
+ return '' | |
+ | |
+ | |
+ranks = {1: 'first', 2: 'second', 3: 'third'} | |
+def get_ordinal(n): | |
+ if n < 0: | |
+ return '<div>N/A</div>' | |
+ else: | |
+ formatted = f"{n}<sup>{'th' if 11 <= (n % 100) <= 13 else ['th', 'st', 'nd', 'rd', 'th'][min(n % 10, 4)]}</sup>" | |
+ if n <= 3: | |
+ return '<div class="text-' + ranks[n] + '">' + formatted + '</div>' | |
+ else: | |
+ return '<div>' + formatted + '</div>' | |
+ | |
+ | |
+special_field_names = { | |
+ 'name': '<th scope="col">Challenge <a class="text-light" data-toggle="modal" data-target="#help" href="#help" role="button"><i class="fa fa-question-circle"></i></a></th>', | |
+ 'writeup-url': '<th scope="col">Full writeup</th>', | |
+ #special fields that are not actually fields | |
+ 'first-blood': '', | |
+ 'writeup-prize': '', | |
+} | |
+ | |
+chall_decor = {'first-blood': '๐ฉธ', 'writeup-prize': '๐'} | |
+ | |
+special_fields = { | |
+ #for now instead of bolding first item in row, just bold these 2 since i dont think ill need another name for the challenges anyway | |
+ #we are still assuming the name is in the first column though - commenting breaks if not | |
+ 'name': lambda v,c: f'<th scope="row">{v} {" ".join([v for k, v in chall_decor.items() if k in c])}</th>', | |
+ 'challenge-written': lambda v,_: f'<th scope="row">{v}</th>', | |
+ 'writeup-url': lambda v,_: '<td>' + ('โ๏ธ' if v else 'โ') + '</td>', | |
+ 'solve-status': lambda v,_: '<td>' + {'solved': 'โ๏ธ', 'co-solved': '๐ค', 'sniped': '๐ค', 'unsolved': '๐'}[v] + '</td>', | |
+ #special fields that are not actually fields | |
+ 'first-blood': lambda v,_: '', | |
+ 'writeup-prize': lambda v,_: '', | |
+} | |
+ | |
+ | |
+ | |
+#generate the page following the template formats | |
+html = diary.format( | |
+ ctfs="".join([ | |
+ #special | |
+ special.format(style=v['style'], content=v['content']) | |
+ if 'content' in v else | |
+ #actual ctfs | |
+ ctf.format( | |
+ style='timeline-organized' if v['organizer'] else '', | |
+ #class="headings" to have same style but not clickable if url is null, otherwise link | |
+ #also link is non w3c compliant hack around interactive elements inside buttons; who complies anyway :) | |
+ url=f'class="nav-link" onclick="window.location=\'{v["url"]}\'; event.stopPropagation()"' if v['url'] else 'class="headings"', | |
+ name=v['name'], | |
+ #isoformat is the one we want for datetime, but we only need date not time; actual format should be <month-abbr>. <day> | |
+ date=f'<time datetime="{v["date"].isoformat().split("T")[0]}">{v["date"].strftime("%b. %d")}</time>', | |
+ duration=f'{v["duration"]}h', #currently we are hardcoding hours, but we can always parse | |
+ type=v['type'], | |
+ team=v['team'], | |
+ rank='<div class="text-success">Organizer</div>' | |
+ if v['organizer'] else | |
+ (('<div>' + v['rank'] + '</div>' if isinstance(v['rank'], str) else get_ordinal(v['rank'])) + | |
+ ('<span data-toggle="tooltip" title="All challenges cleared">โจ</span>' if v['full-clear'] else '')), | |
+ #use the first challenge as header definition | |
+ challengeheader='<thead><tr>' | |
+ + ''.join([ | |
+ #normal fields that are named as expected so we can just use it as the headers | |
+ '<th scope="col">' + name.replace('-', ' ').capitalize() + '</th>' | |
+ if name not in special_field_names else | |
+ #special fields that needs renaming | |
+ special_field_names[name] | |
+ for name in v['challenges'][0].keys()]) | |
+ + '</tr></thead>' | |
+ if 'challenges' in v else "<div class='text-center'>No specific challenges have been logged; It's all a team effort!</div>", #allow no challenge specified (e.g. A/D ctfs where its basically fully team effort so no specific challs that i wouldve fully solved) | |
+ challenges=''.join(['<tbody><tr ' + get_comment(chall, k) + '>' | |
+ #assume every chall object follows the same format as the first, or else header mismatches | |
+ + ''.join([ | |
+ f'<td>{field}</td>' | |
+ if name not in special_fields else | |
+ special_fields[name](field, chall) | |
+ for name, field in chall.items()]) | |
+ + '</tr><tbody>' | |
+ for chall in v['challenges']]) | |
+ if 'challenges' in v else '', | |
+ ) | |
+ for k, v in objects.items()]) | |
+) | |
+ | |
+ | |
+#lint output | |
+soup = BeautifulSoup(html, 'html.parser') | |
+ | |
+for comment in soup.findAll(text=lambda text:isinstance(text, Comment)): | |
+ comment.extract() | |
+ | |
+with open('ctf.html', 'w', encoding="utf-8") as out: | |
+ out.write(str(soup)) #minify | |
+ | |
+ | |
+#write comments into their respective files | |
+if not os.path.exists('ctf'): | |
+ os.mkdir('ctf') | |
+ | |
+for ctf, challs in comments.items(): | |
+ for name, cmt in challs.items(): | |
+ with open(f'ctf/{ctf}-{name}.html', 'w', encoding="utf-8") as out: | |
+ #out.write(markdown(cmt, extensions=['fenced_code', 'codehilite'], extension_configs={'codehilite': {'noclasses': True}})) | |
+ out.write(markdown(cmt, | |
+ extensions=['pymdownx.highlight', 'pymdownx.superfences', 'pymdownx.tilde', 'pymdownx.inlinehilite', 'pymdownx.emoji', 'pymdownx.magiclink'], | |
+ extension_configs={ | |
+ 'pymdownx.highlight': { | |
+ 'guess_lang': 'block', | |
+ 'pygments_lang_class': True, | |
+ }, | |
+ })) | |
+ | |
+ #sanity check if we missed any challs in the yml by comparing against added comments | |
+ if f'{ctf}-{name}' not in added: | |
+ print(f"{ctf}-{name} has a comment but doesn't exist in {ctf}.yml - missed definition?") | |
\ No newline at end of file | |
diff --git a/ctf-diary-generator/templates/ctf.html b/ctf-diary-generator/templates/ctf.html | |
new file mode 100644 | |
index 0000000..ea017fc | |
--- /dev/null | |
+++ b/ctf-diary-generator/templates/ctf.html | |
@@ -0,0 +1,32 @@ | |
+<!--row needed to correctly align ctf-table--> | |
+<div class="text-right info-subsegment ctf-info row"> | |
+ <div class="btn-group timeline-anchor {style}"> | |
+ <!--dropdown-toggle solely for the arrow--> | |
+ <button type="button" class="btn text-white text-left dropdown-toggle" data-toggle="collapse" aria-expanded="false"> | |
+ <div class="ctf-title"> | |
+ <div {url}> | |
+ {name} | |
+ </div> | |
+ </div> | |
+ <div class="ctf-metadata"> | |
+ <i>{date} <div class='big-sep'></div> {duration} <div class='big-sep'></div> {type}</i> | |
+ </div> | |
+ </button> | |
+ | |
+ <div class="ctf-details"> | |
+ {rank} | |
+ <div class='big-sep'></div> | |
+ <div>{team}</div> | |
+ </div> | |
+ </div> | |
+ | |
+ <div class="collapse ctf-table"> | |
+ <div class="table-responsive"> | |
+ <table class="table table-hover text-left table-dark"> | |
+ <col style="width:40%"> <!--widen challenge name--> | |
+ {challengeheader} | |
+ {challenges} | |
+ </table> | |
+ </div> | |
+ </div> | |
+</div> | |
\ No newline at end of file | |
diff --git a/ctf-diary-generator/templates/diary.html b/ctf-diary-generator/templates/diary.html | |
new file mode 100644 | |
index 0000000..c297c26 | |
--- /dev/null | |
+++ b/ctf-diary-generator/templates/diary.html | |
@@ -0,0 +1,112 @@ | |
+<!doctype html> | |
+<html lang="en"> | |
+ <head> | |
+ <meta charset="utf-8"> | |
+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> | |
+ | |
+ <link rel="preload" href="canvas.png" as="image"> | |
+ | |
+ <link href="https://fonts.googleapis.com/css2?family=Source+Code+Pro&display=swap" rel="stylesheet"> | |
+ <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous"> | |
+ <link rel="stylesheet" href="https://unpkg.com/aos@next/dist/aos.css" /> | |
+ <script src="https://kit.fontawesome.com/c2944013c3.js" crossorigin="anonymous"></script> | |
+ <link rel="stylesheet" href="main.css"> | |
+ <link rel="stylesheet" href="pygments-native.css"> | |
+ | |
+ <title>desp's CTF diary</title> | |
+ </head> | |
+ | |
+ <body class="text-center text-white"> | |
+ <div class="d-flex w-100 h-auto p-3 flex-column"> | |
+ <header> | |
+ <nav class="nav nav-mast head justify-content-center float-md-right"> | |
+ <a class="nav-link" href="/">Home</a> | |
+ <a class="nav-link" href="/projects.html">Projects</a> | |
+ <a class="nav-link" href="https://stash.despawningbone.me">Codestash</a> | |
+ <a class="nav-link active" aria-current="page" href="#">CTF diary</a> | |
+ </nav> | |
+ </header> | |
+ <div class="headings pt-5 my-auto mx-auto w-75"> | |
+ <div id="mainline">CTF diary<br></div> | |
+ <div style="font-size:0.8em"> | |
+ A timeline on the CTFs I've played in, challenges I've solved, and comments on the challenges. | |
+ </div> | |
+ </div> | |
+ </div> | |
+ | |
+ | |
+ <div class="info-segment timeline-content"> | |
+ | |
+ <div class="timeline col"></div> | |
+ | |
+ <div class="col pr-5"> | |
+ | |
+ <div class="w-100 mx-auto p-5-flex divider"></div> | |
+ | |
+ {ctfs} | |
+ | |
+ <div class="w-100 mx-auto p-5-flex divider"></div> | |
+ | |
+ </div> | |
+ </div> | |
+ | |
+ <!--help popup--> | |
+ <div class="modal fade" id="help" aria-hidden="true" aria-labelledby="helpTitle" tabindex="-1"> | |
+ <div class="modal-dialog modal-dialog-centered modal-lg"> | |
+ <div class="modal-content"> | |
+ <div class="modal-header"> | |
+ <h5 class="modal-title" id="helpTitle">Glossary</h5> | |
+ <button type="button" class="close text-white" data-dismiss="modal" aria-label="Close"> | |
+ <span aria-hidden="true">×</span> | |
+ </button> | |
+ </div> | |
+ <div class="modal-body text-left"> | |
+ Icons next to challenge names: <br> | |
+ ๐ฉธ - First blood <br> | |
+ ๐ - Writeup prize won <br> | |
+ <br> | |
+ Solve status: <br> | |
+ โ๏ธ - solved during the ctf <br> | |
+ ๐ค - co-solved <br> | |
+ ๐ค - got sniped (solved after teammates solved during the ctf) <br> | |
+ ๐ - didn't solve during the ctf, but worth mentioning (e.g. solved afterwards) <br> | |
+ <br> | |
+ Most of these challenges are ordered chronologically by when it's solved.<br> | |
+ Click on the row to see my comments<sup>*</sup> / solve scripts (if any) for the challenge!<br> | |
+ <hr> | |
+ <div style="font-size:0.7em">* - The comments are usually in the form of unedited draft writeups - they might contain anything from unverified information to straight up incoherent babbles that can only be understood by me; be warned!</div> | |
+ </div> | |
+ </div> | |
+ </div> | |
+ </div> | |
+ | |
+ <!--comments popup; requires some nudging from js to display content on demand--> | |
+ <div class="modal fade" id="comment" aria-hidden="true" aria-labelledby="commentTitle" tabindex="-1"> | |
+ <div class="modal-dialog modal-dialog-centered modal-xl"> <!--xl since there's gonna be a *lot* of code--> | |
+ <div class="modal-content"> | |
+ <div class="modal-header"> | |
+ <h5 class="modal-title" id="commentTitle"> <!--dynamically generated--> </h5> | |
+ <button type="button" class="close text-white" data-dismiss="modal" aria-label="Close"> | |
+ <span aria-hidden="true">×</span> | |
+ </button> | |
+ </div> | |
+ <div class="modal-body text-left ctf-comment"> | |
+ <!--dynamically loaded--> | |
+ </div> | |
+ </div> | |
+ </div> | |
+ </div> | |
+ | |
+ | |
+ <footer class="d-flex flex-column w-100 mt-5"> | |
+ <p class="large-content mx-auto p-2 pt-3">Made from scratch by despawningbone with Bootstrap and AOS - view in <a href="https://stash.despawningbone.me/diffusion/WEB/" class="nav-link inline">codestash</a></p> | |
+ </footer> | |
+ | |
+ <script src="https://code.jquery.com/jquery-3.6.3.min.js" integrity="sha256-pvPw+upLPUjgMXY0G+8O0xUf+/Im1MZjXxxgOcBQBXU=" crossorigin="anonymous"></script> | |
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script> | |
+ <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script> | |
+ <script src="https://unpkg.com/aos@next/dist/aos.js"></script> | |
+ <script src="main.js"></script> | |
+ </body> | |
+</html> | |
+ | |
diff --git a/ctf-diary-generator/templates/special.html b/ctf-diary-generator/templates/special.html | |
new file mode 100644 | |
index 0000000..b362d3d | |
--- /dev/null | |
+++ b/ctf-diary-generator/templates/special.html | |
@@ -0,0 +1,5 @@ | |
+<div class="w-100 mx-auto text-left align-middle divider timeline-anchor {style}"> | |
+ <div class="text-muted"> | |
+ {content} | |
+ </div> | |
+</div> | |
\ No newline at end of file | |
diff --git a/main.css b/main.css | |
index 874217b..a46282e 100644 | |
--- a/main.css | |
+++ b/main.css | |
@@ -1,123 +1,375 @@ | |
html, body { | |
- height: 145%; | |
- font-family: 'Source Code Pro', monospace; | |
+ font-family: 'Varela Round', monospace; | |
+ background: url('canvas.png'), #121212; | |
+ background-size: cover; | |
+ /*animation: delaybg 1s;*/ | |
+} | |
+ | |
+/* @keyframes delaybg { | |
+ 0%, 99% { background: #121212; height: 100%; overflow: hidden;} | |
+ 100% { background: url('canvas5.5.png'), #121212; height: auto; overflow: auto; } | |
+} | |
+ | |
+#cover { | |
+ animation: fadein ease 1.5s; | |
+ animation-iteration-count: 1; | |
+ animation-fill-mode: forwards; | |
+} | |
+ | |
+@keyframes fadein { | |
+ from { opacity: 0; } | |
+ to { : 1; } | |
+} */ | |
+ | |
+/*needed for background image since theres a margin-right: -15px for some reason*/ | |
+.row { | |
+ margin: auto !important; | |
} | |
.nav-link { | |
- color: #dd7; | |
+ color: #dd7; | |
} | |
.nav-link:hover { | |
- color: #aa4; | |
+ color: #aa4; | |
} | |
#cover { | |
- top: -10%; | |
- background-size: cover; | |
- box-shadow: 100px; | |
+ top: -10%; | |
+ mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1) 94%, rgba(0, 0, 0, 0)); | |
+ -webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1) 94%, rgba(0, 0, 0, 0)); | |
+ background: #121212; /*to be overriden*/ | |
+ background-size: cover; | |
+ background-position: center; | |
+ box-shadow: 100px; | |
+ height: 110vh !important; | |
} | |
.tooltip { | |
- left: 0px; | |
+ left: 0px; | |
} | |
.tooltip-inner { | |
- background-color: #343a40; | |
+ background-color: #121212; | |
+ font-family: 'Varela Round', monospace; | |
} | |
.arrow::before { | |
- border-bottom-color:#343a40 !important; | |
- border-top-color:#343a40 !important; | |
+ border-bottom-color:#121212 !important; | |
+ border-top-color:#121212 !important; | |
} | |
.col { | |
- padding-left: 0.25em; | |
- padding-right: 0.25em; | |
+ padding-left: 0.25em; | |
+ padding-right: 0.25em; | |
+} | |
+ | |
+#cover #mainline { | |
+ margin-top: -2.5em; | |
} | |
#mainline { | |
- font-weight: bold; | |
- font-size: 1.25em; | |
+ font-weight: bold; | |
+ font-size: 1.25em; | |
+} | |
+ | |
+#links { | |
+ padding-top: 2.5vh; | |
} | |
#links img { | |
- max-width: 50px; | |
- width:100%; | |
+ max-width: 50px; | |
+ width:100%; | |
} | |
.skills .card, #mainskill{ | |
- background-color: rgba(20, 20, 20, 0.5); | |
- box-shadow: 4px 4px 15px rgba(10, 10, 10, 0.8); | |
- border-radius: 8px; | |
+ background-color: rgba(40, 40, 40, 0.7); | |
+ box-shadow: 4px 4px 15px rgba(30, 30, 30, 0.8); | |
+ border-radius: 8px; | |
} | |
.skills .card-body, #mainskill .card-body { | |
- font-size: 0.8rem; | |
+ font-size: 0.8rem; | |
} | |
#mainskill { | |
- min-width:25%; | |
- max-width:50%; | |
+ min-width:25%; | |
+ max-width:50%; | |
} | |
footer { | |
- background-color: rgba(20, 20, 20, 0.4); | |
+ background-color: rgba(10, 10, 10, 0.6); | |
} | |
.nav-link.inline { /*reset for footer*/ | |
- display: inline; | |
- padding: 0; | |
+ display: inline; | |
+ padding: 0; | |
} | |
footer, .smaller { | |
- font-size: 0.75em; | |
+ font-size: 0.75em; | |
} | |
.project { | |
- font-weight: bold; | |
+ font-weight: bold; | |
} | |
.info-segment .large-content span { | |
- margin-left: 2em; | |
+ margin-left: 2em; | |
} | |
.splitview { | |
- margin-right: 0px !important; | |
- margin-left: 0px !important; | |
+ margin-right: 0px !important; | |
+ margin-left: 0px !important; | |
+} | |
+ | |
+.info-segment .headings { | |
+ color: #dd7; | |
+ padding: .5rem 1rem; | |
+} | |
+ | |
+.modal-content { | |
+ background: rgba(30, 30, 30, 0.8); | |
+} | |
+ | |
+/* | |
+ * timeline styling | |
+ */ | |
+ | |
+/*assumes timeline-content has 2 columns, 1 .timeline and 1 content column thats it*/ | |
+/*anchor also needs to be flex or else the anchor node gets misaligned*/ | |
+.timeline-content, .timeline-anchor { | |
+ display: flex; | |
+} | |
+ | |
+.timeline { | |
+ display: inline-block; | |
+ max-width: 1em; | |
+ height: auto; | |
+ background: rgba(80, 80, 80, 0.5); | |
+ margin: 3em; | |
+ border-radius: 1em; | |
+ /*patch for terminating the timeline at the nodes; assume p-5 divider exists at topmost and bottommost, and 2em of margin on both sides of the last element*/ | |
+ margin-top: 15em; | |
+ margin-bottom: 15em; | |
+} | |
+ | |
+/*all elements in the non timeline column that specifies it is an anchor*/ | |
+.timeline-content:not(.timeline) .timeline-anchor::before { | |
+ /*set up .fa for this pseudo element*/ | |
+ font-size: 2em; | |
+ font-family: var(--fa-style-family-classic); | |
+ font-weight: 900; | |
+ | |
+ /*node style on timeline*/ | |
+ content: "\f192"; | |
+ color: rgba(200, 200, 200, 0.8); | |
+ | |
+ /*apparently flexbox centers content in pseudo elements*/ | |
+ display:flex; | |
+ flex-direction:row; | |
+ align-items: center; | |
+ | |
+ /*move the pseudoelement onto the timeline*/ | |
+ position: relative; | |
+ left: -1.375em; /*.timeline margin (3em) + .col margin on timeline (0.25em) - half of this width (0.5em), scaled by 2em*/ | |
+ margin-left: -1em; /*remove the size of this element in parent*/ | |
+ width: 1em; | |
+} | |
+ | |
+/*special types with ctf-info still in it*/ | |
+.timeline-organized::before { | |
+ content: "\f005" !important; | |
+ left: -1.475em !important; /*seems like certain fonts have a different width than the normal node*/ | |
+} | |
+ | |
+/*big special types*/ | |
+.timeline-year::before { | |
+ content: "\f79f" !important; | |
+ left: -1.475em !important; | |
+} | |
+ | |
+.timeline-year > * { | |
+ font-size: 2.5em !important; | |
+ padding: 1.5em !important; | |
+ padding-left: 0.5em !important; | |
+} | |
+ | |
+/*smaller special types*/ | |
+.timeline-join > *, .timeline-announce > *, .timeline-hiatus > * { | |
+ font-size: 1.5em !important; | |
+ padding: 0.5em !important; | |
+} | |
+ | |
+.timeline-join::before { | |
+ content: "\f2f6" !important; | |
+} | |
+ | |
+.timeline-announce::before { | |
+ content: "\f0a1" !important; | |
+} | |
+ | |
+.timeline-hiatus::before { | |
+ content: "\f186" !important; | |
+ left: -1.275em !important; | |
+} | |
+ | |
+/* | |
+ * collapsable table styling | |
+ */ | |
+ | |
+.ctf-info { | |
+ position: relative; | |
+ width: 100%; | |
+ background: rgba(30, 30, 30, 0.4); | |
+ border-radius: 1em; | |
+ margin-top: 2em !important; | |
+ margin-bottom: 2em !important; | |
+} | |
+ | |
+.ctf-info > .btn-group { /*header*/ | |
+ width: 100%; | |
+ height: 100%; | |
+ background: rgba(60, 60, 60, 0.4); | |
+ border-radius: 1em; | |
+} | |
+ | |
+.ctf-info .ctf-table { | |
+ width: 100%; | |
+ /*if we set height here the collapse animation dies, and padding makes the animation choppy*/ | |
+} | |
+ | |
+.ctf-info .table-responsive { | |
+ padding: 0.7em; | |
+ padding-bottom: 0em; | |
+} | |
+ | |
+.ctf-info .table { | |
+ background: none; | |
+ width: 100%; | |
+} | |
+ | |
+.ctf-table th, .ctf-table td, .ctf-table .table-dark tbody+tbody { | |
+ border-top: none; | |
+} | |
+ | |
+.ctf-title { | |
+ font-size: 2em; | |
+ display: inline-block; | |
+ padding-top: 1.3em; | |
+ padding-left: 1.3em; | |
+} | |
+ | |
+.ctf-metadata { | |
+ position: absolute; | |
+ top: 0; | |
+ padding: 0.7em; | |
+} | |
+ | |
+.ctf-details { | |
+ position: absolute; | |
+ display: flex; | |
+ justify-content: center; | |
+ align-items: center; | |
+ right: 0; | |
+ height: 100%; | |
+ padding-right: 3em; | |
+ font-size: 1.5em; | |
+} | |
+ | |
+.ctf-details > *:not(sup) { | |
+ padding: 0.2em; | |
+} | |
+ | |
+/*android webview still uses webkit prefix for transform even though not for keyframes*/ | |
+.ctf-info.show .btn::after { | |
+ -webkit-transform: rotate(-180deg); | |
+ transform:rotate(-180deg); | |
+} | |
+ | |
+/*both ways should have a transition*/ | |
+.ctf-info .btn::after { | |
+ transition: transform 500ms ease; | |
+ scale: 1.5; | |
+} | |
+ | |
+/*for modelling a less obvious separator*/ | |
+.big-sep { | |
+ display: inline-block; | |
+ vertical-align: middle; | |
+} | |
+ | |
+.big-sep::before { | |
+ content: '/'; | |
+ font-size: 180%; | |
+ color: rgba(80, 80, 80, 0.5); | |
+ text-align: center; | |
+} | |
+ | |
+ | |
+.emojione { | |
+ width: 1em; | |
+ height: 1em; | |
+} | |
+ | |
+.text-first { | |
+ color: #FFD700; | |
+ text-shadow: 0 0 0.4em #FFD700; | |
+} | |
+ | |
+.text-second { | |
+ color: #C0C0C0; | |
+ text-shadow: 0 0 0.4em #FFF; | |
+} | |
+ | |
+.text-third { | |
+ color: #CD7F32; | |
+ text-shadow: 0 0 0.4em #CD7F32; | |
} | |
@media (max-width: 767px) { | |
- .skills { | |
- width: 100%; | |
- font-size: 0.75rem; | |
- } | |
- .skills .card-body { | |
- display:none; | |
- } | |
+ /*fixes stretching in safari*/ | |
+ .splitview img { | |
+ width: 100% !important; | |
+ height: 100% !important; | |
+ } | |
+ | |
+ .skills { | |
+ width: 100%; | |
+ font-size: 0.75rem; | |
+ } | |
- .headings { font-size: 1.25rem; } | |
- .large-content { font-size: 0.75em; width: 100%; } | |
+ .skills .card-body { | |
+ display: none; | |
+ } | |
- .splitview { width: 100%; text-align: center !important; padding: 2rem !important;} | |
+ .headings { font-size: 1.25rem; } | |
+ .large-content { font-size: 0.75em; width: 100%; } | |
- .p-5-flex { padding: 2rem !important; } /*patch back padding since p-5 is too much*/ | |
+ .splitview { width: 100%; text-align: center !important; padding: 2rem !important;} | |
+ | |
+ .p-5-flex { padding: 2rem !important; } /*patch back padding since p-5 is too much*/ | |
} | |
@media (min-width: 768px) { | |
- .headings { font-size: 1.5rem; } | |
- .large-content { font-size: 0.75em; width: 75%; } | |
- .skills { width: 65%; } | |
+ .headings { font-size: 1.5rem; } | |
+ .large-content { font-size: 0.75em; width: 75%; } | |
+ .skills { width: 90%; } | |
+ | |
+ .splitview { width: 50%; } | |
+ .info-subsegment { display: flex; } | |
- .splitview { width: 50%; } | |
- .info-subsegment { display: flex; } | |
+ .p-5-flex { padding: 5rem !important; } | |
- .p-5-flex { padding: 5rem !important; } | |
+ .info-segment { padding-left: 2%; padding-right: 2%; } | |
} | |
@media (min-width: 992px) { | |
- .headings { font-size: 1.75rem; } | |
- .large-content { font-size: 1em; } | |
+ .headings { font-size: 1.75rem; } | |
+ .large-content { font-size: 1em; } | |
} | |
@media (min-width: 1200px) { | |
- .headings { font-size: 2rem; } | |
- .large-content { font-size: 1.25em; } | |
+ .headings { font-size: 2rem; } | |
+ .large-content { font-size: 1.25em; } | |
+ .info-segment { padding-left: 5%; padding-right: 5%; } | |
+ .skills { width: 70%; } | |
} | |
\ No newline at end of file | |
diff --git a/main.html b/main.html | |
index 4fe82cd..1b2d8db 100644 | |
--- a/main.html | |
+++ b/main.html | |
@@ -1,97 +1,107 @@ | |
<!doctype html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> | |
<script type="text/javascript"> | |
//randomize background and preload | |
const bgCount = 7; | |
- var bg = new Image(); | |
- bg.src = 'images/' + Math.ceil(Math.random() * bgCount) + '.png'; | |
+ var bg = document.createElement("link"); | |
+ bg.rel = "preload"; | |
+ bg.as = "image"; | |
+ bg.href = 'images/' + Math.ceil(Math.random() * bgCount) + '.png';; | |
+ document.head.appendChild(bg); | |
+ | |
+ var preloadstyle = document.createElement("style"); | |
+ preloadstyle.innerText = 'body { background-position-y: 100vh !important; } #cover { background-image: url(' + bg.href + ') !important; }'; | |
+ document.head.appendChild(preloadstyle); | |
</script> | |
+ <link rel="preload" href="canvas.png" as="image"> | |
+ | |
<link href="https://fonts.googleapis.com/css2?family=Source+Code+Pro&display=swap" rel="stylesheet"> | |
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous"> | |
<link rel="stylesheet" href="https://unpkg.com/aos@next/dist/aos.css" /> | |
<link rel="stylesheet" href="main.css"> | |
- <title>Hi! - despawningbone's portfolio</title> | |
+ <title>Hi! - desp's portfolio</title> | |
</head> | |
- <body class="bg-dark text-center text-white"> | |
+ <body class="text-center text-white"> | |
<div id="cover" class="d-flex w-100 h-50 p-3 mx-auto flex-column"> | |
<header> | |
<nav class="nav nav-mast head justify-content-center float-md-right"> | |
<a class="nav-link active" aria-current="page" href="#">Home</a> | |
<a class="nav-link" href="/projects.html">Projects</a> | |
<a class="nav-link" href="https://stash.despawningbone.me">Codestash</a> | |
+ <a class="nav-link" href="/ctf.html">CTF diary</a> | |
</nav> | |
</header> | |
<div class="headings justify-content-center my-auto container-fluid w-75"> | |
<div id="mainline">despawningbone<br></div> | |
<div>Just a weeb who loves tech.</div> | |
<div id="links" class="mx-auto row mt-4 w-75"> | |
<a href="mailto://[email protected]" class="col"><img alt="email" class="img-fluid" data-toggle="tooltip" data-placement="bottom" title="contact me!" src="images/mail.png"></img></a> | |
<a href="https://github.com/despawningbone" class="col"><img alt="github" class="img-fluid" data-toggle="tooltip" data-placement="bottom" title="public projects only; check codestash for more!" src="images/github.png"></img></a> | |
<a href="#" class="col"><img id="discord" alt="discord" class="img-fluid" data-toggle="tooltip" data-placement="bottom" title="despawningbone#4078" src="images/discord.png"></img></a> | |
<a href="https://myanimelist.net/profile/despawningbone" class="col"><img alt="MAL" class="img-fluid" data-toggle="tooltip" data-placement="bottom" title="my anime list! (literally)" src="images/mal.png"></img></a> | |
<a href="https://osu.ppy.sh/users/despawningbone" class="col"><img alt="osu!" class="img-fluid" data-toggle="tooltip" data-placement="bottom" title="click the circles!" src="images/osu.png"></img></a> | |
</div> | |
</div> | |
</div> | |
<div id="info" class="d-flex flex-column container-fluid"> | |
<div class="info-segment" data-aos="fade-up"> | |
<p class="headings row pt-5 p-3">About me</p> | |
<p class="large-content pb-4 mx-auto">Heyo! I am an aspiring computer science major from Hong Kong who has been coding for fun since 2016. | |
<br>As one of my major hobbies, I try to explore a variety of different technical fields, from hardware development to UI design. | |
<br>However, my main focus has always been programming and system administration, with most of my time spent on doing projects such as despbot and vortex. | |
<br>Recently though, I've been spending more time on cybersecurity, and I hope to be able to work on this field in the future. | |
</p> | |
</div> | |
<div class="info-segment" data-aos="fade-up"> | |
<p class="headings row pt-5 p-3">Technical Skills</p> | |
<div id="mainskill" class="card mb-4 mx-auto"><div class="card-header">Java</div><div class="card-body">Preferred language</div></div> | |
<div id="techskills" class="skills row h-50 mx-auto container-fluid justify-content-center"> | |
<div class="card m-2"><div class="card-header">C#</div><div class="card-body">Alternative to java</div></div> | |
<div class="card m-2"><div class="card-header">Python</div><div class="card-body">Prototyping/automation</div></div> | |
<div class="card m-2"><div class="card-header">git</div><div class="card-body">Adept at common workflows</div></div> | |
<div class="card m-2"><div class="card-header">SQL</div><div class="card-body">SQLite/MySQL integrations</div></div> | |
<div class="card m-2"><div class="card-header">Linux</div><div class="card-body">Sysadmin on debian-based</div></div> | |
<div class="card m-2"><div class="card-header">IDA</div><div class="card-body">Reversing Win32 PEs</div></div> | |
<div class="card m-2"><div class="card-header">Pentesting</div><div class="card-body">Metasploit; Known-CVE scripts</div></div> | |
<div class="card m-2"><div class="card-header">Win32API</div><div class="card-body">Formats and functions</div></div> | |
<div class="card m-2"><div class="card-header">C/C++</div><div class="card-body">Novice knowledge for low level interactions</div></div> | |
<div class="card m-2"><div class="card-header">Arduino/Pi</div><div class="card-body">Small projects e.g. cap. keypad</div></div> | |
<div class="card m-2"><div class="card-header">CSS</div><div class="card-body">Theming web apps</div></div> | |
<div class="card m-2"><div class="card-header">JavaScript</div><div class="card-body">Basic knowledge</div></div> | |
<div class="card m-2"><div class="card-header">HTML</div><div class="card-body">Basic knowledge</div></div> | |
</div> | |
</div> | |
<div class="info-segment" data-aos="fade-up"> | |
<p class="headings row pt-5 p-3">Other Skills and Hobbies</p> | |
<div id="otherskills" class="skills row h-50 mx-auto container-fluid justify-content-center"> | |
<div class="card m-2"><div class="card-header">Cantonese</div><div class="card-body">Mother tongue</div></div> | |
<div class="card m-2"><div class="card-header">English</div><div class="card-body">Most fluent second language</div></div> | |
<div class="card m-2"><div class="card-header">Mandarin</div><div class="card-body">Can carry out daily conversations</div></div> | |
<div class="card m-2"><div class="card-header">Japanese</div><div class="card-body">(Work in progress)</div></div> | |
<div class="card m-2"><div class="card-header">Amateur Photography</div><div class="card-body">The backgrounds here are taken by me!</div></div> | |
</div> | |
</div> | |
</div> | |
<footer class="d-flex flex-column w-100 mt-5"> | |
<p class="large-content mx-auto p-2 pt-3">Made from scratch by despawningbone with Bootstrap and AOS - view in <a href="https://stash.despawningbone.me/diffusion/WEB/" class="nav-link inline">codestash</a></p> | |
</footer> | |
- <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> | |
+ <script src="https://code.jquery.com/jquery-3.6.3.min.js" integrity="sha256-pvPw+upLPUjgMXY0G+8O0xUf+/Im1MZjXxxgOcBQBXU=" crossorigin="anonymous"></script> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script> | |
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script> | |
<script src="https://unpkg.com/aos@next/dist/aos.js"></script> | |
<script src="main.js"></script> | |
</body> | |
</html> | |
diff --git a/main.js b/main.js | |
index 8934c1d..7e00a9d 100644 | |
--- a/main.js | |
+++ b/main.js | |
@@ -1,26 +1,48 @@ | |
AOS.init(); | |
-if(typeof bg !== 'undefined') document.getElementById('cover').style.backgroundImage = 'linear-gradient(to bottom, rgba(52, 58, 64, 0) 94%, rgba(52, 58, 64, 1)), url(' + bg.src + ')'; | |
- | |
//initialize tooltips | |
$('[data-toggle="tooltip"]').tooltip({ | |
- offset: '0, 20%' | |
+ offset: '0, 20%' | |
}); | |
+$('#discord').bind('click touchend', function() { | |
+ if (navigator.clipboard.writeText("despawningbone#4078")) { | |
+ $(this).tooltip('hide'); | |
+ $('#discord').attr('data-original-title', "(copied!)"); | |
+ setTimeout(() => $('#discord').attr('data-original-title', "despawningbone#4078"), 400); | |
+ $(this).tooltip('show'); | |
+ } | |
+}); | |
-/*$('[data-toggle="tooltip"]').mouseenter(function(){ //tooltip patch for browsers since its shifted somehow | |
- if(!/iPhone|iPad|iPod|Android/i.test(navigator.userAgent)) { | |
- document.styleSheets[2].cssRules[4].style.setProperty('left', (($(this).parent().index() + 1) * 2.5) + 'px', "important"); | |
- } | |
-});*/ //fixed by changing discord id from a to img wtf | |
+//relative data-target patch for accordions, and also to add .show back into the button | |
+$('body').on('click', '[data-toggle=collapse]', function (e) { | |
+ var $parent = $(this).parents('.ctf-info'); | |
+ $parent.find('.ctf-table').collapse('toggle'); | |
+ $parent.toggleClass('show') | |
+}); | |
-$('#discord').bind('click touchend', function() { | |
- console.log(this); | |
- if (navigator.clipboard.writeText("despawningbone#4078")) { | |
- $(this).tooltip('hide'); | |
- $('#discord').attr('data-original-title', "(copied!)"); | |
- setTimeout(() => $('#discord').attr('data-original-title', "despawningbone#4078"), 400); | |
- $(this).tooltip('show'); | |
+//ctf diary comment popup dynamically load content | |
+//TODO allow use of url#data-comment to load modal | |
+$('.ctf-table tbody tr').on('click',function(){ | |
+ href = $(this).attr('href'); | |
+ //if full writeup is available, redirect | |
+ if(href) { | |
+ window.location=href; | |
+ } else { | |
+ //else open comment popup | |
+ href = $(this).attr('data-comment'); | |
+ | |
+ //set comment title to chall name | |
+ $('#commentTitle').text('Comments - ' + this.firstElementChild.textContent) | |
+ | |
+ //only load if attr is found, which means a comment should be found | |
+ if(href) { | |
+ console.log('ctf/' + href + '.html') | |
+ $('.ctf-comment').load('ctf/' + href + '.html', function(res, status, xhr){ | |
+ if(status !== "error") //only show if we can load it, otherwise treat as no comment | |
+ $('#comment').modal({show:true}); | |
+ }); | |
} | |
+ } | |
}); | |
\ No newline at end of file | |
diff --git a/projects.html b/projects.html | |
index 3c6be78..6209de2 100644 | |
--- a/projects.html | |
+++ b/projects.html | |
@@ -1,95 +1,98 @@ | |
<!doctype html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> | |
+ <link rel="preload" href="canvas.png" as="image"> | |
+ | |
<link href="https://fonts.googleapis.com/css2?family=Source+Code+Pro&display=swap" rel="stylesheet"> | |
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous"> | |
<link rel="stylesheet" href="https://unpkg.com/aos@next/dist/aos.css" /> | |
<link rel="stylesheet" href="main.css"> | |
- <title>Hi! - despawningbone's portfolio</title> | |
+ <title>Hi! - desp's portfolio</title> | |
</head> | |
- <body class="bg-dark text-center text-white"> | |
+ <body class="text-center text-white"> | |
<div class="d-flex w-100 h-auto p-3 flex-column"> | |
<header> | |
<nav class="nav nav-mast head justify-content-center float-md-right"> | |
<a class="nav-link" href="/">Home</a> | |
<a class="nav-link active" aria-current="page" href="#">Projects</a> | |
<a class="nav-link" href="https://stash.despawningbone.me">Codestash</a> | |
+ <a class="nav-link" href="/ctf.html">CTF diary</a> | |
</nav> | |
</header> | |
- <div class="headings pt-5 my-auto mx-auto w-75"> | |
+ <div class="headings pt-5 my-auto mx-auto w-75" data-aos="fade-up"> | |
<div id="mainline">Project Highlights<br></div> | |
<div><i>More projects can be found on the codestash!</i></div> | |
</div> | |
</div> | |
<div class="info-segment row"> | |
<div class="w-100 mx-auto p-5-flex divider"></div> | |
<div class="text-right info-subsegment" data-aos="fade-up"> | |
<div class="splitview my-auto row"><img class="img-fluid mx-auto" src="images/vortex.jpg"></img></div> | |
<div class="m-auto pr-5 splitview"> | |
<p class="project headings"><a href="https://www.vortexnetwork.me" class="nav-link">Vortex Network</a></p> | |
<p class="large-content ml-auto"><span>Originally invited to be a plugin developer from the start of the server, I officially joined the development team not long after, | |
migrating it to a network. I have since maintained the Ubuntu-based infrastructure backend, the 3 websites and a discord bot along with plugin development.<br></span> | |
<span>From intrusion detection and prevention to general sysadmin work, I've gained a lot of real world insights into how actual system administration on a cloud might feel like, | |
and it taught me how to code as a team, use CD/CI technologies, and a lot more that I wouldn't have expected from developing a minecraft server.</span> | |
</p> | |
<p class="smaller">Project since July 2017</p> | |
</div> | |
</div> | |
<div class="w-100 mx-auto m-5 p-5-flex divider"></div> | |
<div class="text-left info-subsegment" data-aos="fade-up"> | |
<div class="m-auto pl-5 splitview"> | |
<p class="project headings"><a href="https://discordapp.com/oauth2/authorize?&client_id=311086271642599424&scope=bot&permissions=0" class="nav-link">despbot</a></p> | |
<p class="large-content mr-auto"><span>Ever since I started using Discord back in 2016, it has been my favorite communication platform; Everything just felt intuitive and simple yet customizable. | |
With this newfound favorite, I started digging into everything it has to offer, and I eventually stumbled upon the officially supported bots and their API.<br></span> | |
<span>Having only started programming by then, it proved to be a great opportunity for me to learn and test my skills while making helpful utilities for my daily use. | |
Throughout the years, it has evolved from a single 8ball to 50+ different commands, hardcoded values to per-guild configs, and from one giant class filled with if-elses to a multithreaded framework supporting nested subcommands.<br></span> | |
<span>It is currently semi-public as there are still a lot left to do; However, feel free to add it to any servers if you find it useful too ๐</span> | |
</p> | |
<p class="smaller">View in <a href="https://stash.despawningbone.me/diffusion/DESB/" class="nav-link inline">codestash</a> ยท Project since Jan 2017</p> | |
</div> | |
<div class="splitview my-auto row"><img class="img-fluid mx-auto" src="images/despbot.png"></img></div> | |
</div> | |
<div class="w-100 mx-auto m-5 p-5-flex divider"></div> | |
<div class="text-right info-subsegment" data-aos="fade-up"> | |
<div class="splitview my-auto row"><img class="img-fluid p-5-flex mx-auto" src="images/spigot.jpg"></img></div> | |
<div class="m-auto pr-5 splitview"> | |
<p class="project headings"><a href="https://www.spigotmc.org/resources/authors/despawningbone.256767/" class="nav-link">Spigot Plugins</a></p> | |
<p class="large-content ml-auto"><span>As one of the most fasinating games I've ever played, I've spent countless hours on the technical side of Minecraft, whether it was redstone or command blocks. | |
Eventually, with a private feature being heavily requested on a server I played frequently back then, I decided: why not try programming it myself and give it to the server instead?<br></span> | |
<span>With only basic C knowledge and having only created simple utilities, it proved to be a challenge, but it also was what really sparked my interest in computer science. | |
I created and published several more plugins in the coming years, before moving on to creating custom plugins for vortex network.</span> | |
</p> | |
<p class="smaller">Project since Dec 2016 ยท on hiatus</p> | |
</div> | |
</div> | |
<div class="w-100 mx-auto mt-5 p-5-flex divider"></div> | |
</div> | |
</div> | |
<footer class="d-flex flex-column w-100 mt-5"> | |
<p class="large-content mx-auto p-2 pt-3">Made from scratch by despawningbone with Bootstrap and AOS - view in <a href="https://stash.despawningbone.me/diffusion/WEB/" class="nav-link inline">codestash</a></p> | |
</footer> | |
- <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> | |
+ <script src="https://code.jquery.com/jquery-3.6.3.min.js" integrity="sha256-pvPw+upLPUjgMXY0G+8O0xUf+/Im1MZjXxxgOcBQBXU=" crossorigin="anonymous"></script> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script> | |
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script> | |
<script src="https://unpkg.com/aos@next/dist/aos.js"></script> | |
<script src="main.js"></script> | |
</body> | |
</html> | |
diff --git a/pygments-native.css b/pygments-native.css | |
new file mode 100644 | |
index 0000000..525604a | |
--- /dev/null | |
+++ b/pygments-native.css | |
@@ -0,0 +1,80 @@ | |
+.highlight .hll { background-color: #404040 } | |
+.highlight { background: #202020; color: #d0d0d0 } | |
+.highlight .c { color: #999999; font-style: italic } /* Comment */ | |
+.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ | |
+.highlight .esc { color: #d0d0d0 } /* Escape */ | |
+.highlight .g { color: #d0d0d0 } /* Generic */ | |
+.highlight .k { color: #6ab825; font-weight: bold } /* Keyword */ | |
+.highlight .l { color: #d0d0d0 } /* Literal */ | |
+.highlight .n { color: #d0d0d0 } /* Name */ | |
+.highlight .o { color: #d0d0d0 } /* Operator */ | |
+.highlight .x { color: #d0d0d0 } /* Other */ | |
+.highlight .p { color: #d0d0d0 } /* Punctuation */ | |
+.highlight .ch { color: #999999; font-style: italic } /* Comment.Hashbang */ | |
+.highlight .cm { color: #999999; font-style: italic } /* Comment.Multiline */ | |
+.highlight .cp { color: #cd2828; font-weight: bold } /* Comment.Preproc */ | |
+.highlight .cpf { color: #999999; font-style: italic } /* Comment.PreprocFile */ | |
+.highlight .c1 { color: #999999; font-style: italic } /* Comment.Single */ | |
+.highlight .cs { color: #e50808; font-weight: bold; background-color: #520000 } /* Comment.Special */ | |
+.highlight .gd { color: #d22323 } /* Generic.Deleted */ | |
+.highlight .ge { color: #d0d0d0; font-style: italic } /* Generic.Emph */ | |
+.highlight .gr { color: #d22323 } /* Generic.Error */ | |
+.highlight .gh { color: #ffffff; font-weight: bold } /* Generic.Heading */ | |
+.highlight .gi { color: #589819 } /* Generic.Inserted */ | |
+.highlight .go { color: #cccccc } /* Generic.Output */ | |
+.highlight .gp { color: #aaaaaa } /* Generic.Prompt */ | |
+.highlight .gs { color: #d0d0d0; font-weight: bold } /* Generic.Strong */ | |
+.highlight .gu { color: #ffffff; text-decoration: underline } /* Generic.Subheading */ | |
+.highlight .gt { color: #d22323 } /* Generic.Traceback */ | |
+.highlight .kc { color: #6ab825; font-weight: bold } /* Keyword.Constant */ | |
+.highlight .kd { color: #6ab825; font-weight: bold } /* Keyword.Declaration */ | |
+.highlight .kn { color: #6ab825; font-weight: bold } /* Keyword.Namespace */ | |
+.highlight .kp { color: #6ab825 } /* Keyword.Pseudo */ | |
+.highlight .kr { color: #6ab825; font-weight: bold } /* Keyword.Reserved */ | |
+.highlight .kt { color: #6ab825; font-weight: bold } /* Keyword.Type */ | |
+.highlight .ld { color: #d0d0d0 } /* Literal.Date */ | |
+.highlight .m { color: #3677a9 } /* Literal.Number */ | |
+.highlight .s { color: #ed9d13 } /* Literal.String */ | |
+.highlight .na { color: #bbbbbb } /* Name.Attribute */ | |
+.highlight .nb { color: #24909d } /* Name.Builtin */ | |
+.highlight .nc { color: #447fcf; text-decoration: underline } /* Name.Class */ | |
+.highlight .no { color: #40ffff } /* Name.Constant */ | |
+.highlight .nd { color: #ffa500 } /* Name.Decorator */ | |
+.highlight .ni { color: #d0d0d0 } /* Name.Entity */ | |
+.highlight .ne { color: #bbbbbb } /* Name.Exception */ | |
+.highlight .nf { color: #447fcf } /* Name.Function */ | |
+.highlight .nl { color: #d0d0d0 } /* Name.Label */ | |
+.highlight .nn { color: #447fcf; text-decoration: underline } /* Name.Namespace */ | |
+.highlight .nx { color: #d0d0d0 } /* Name.Other */ | |
+.highlight .py { color: #d0d0d0 } /* Name.Property */ | |
+.highlight .nt { color: #6ab825; font-weight: bold } /* Name.Tag */ | |
+.highlight .nv { color: #40ffff } /* Name.Variable */ | |
+.highlight .ow { color: #6ab825; font-weight: bold } /* Operator.Word */ | |
+.highlight .w { color: #666666 } /* Text.Whitespace */ | |
+.highlight .mb { color: #3677a9 } /* Literal.Number.Bin */ | |
+.highlight .mf { color: #3677a9 } /* Literal.Number.Float */ | |
+.highlight .mh { color: #3677a9 } /* Literal.Number.Hex */ | |
+.highlight .mi { color: #3677a9 } /* Literal.Number.Integer */ | |
+.highlight .mo { color: #3677a9 } /* Literal.Number.Oct */ | |
+.highlight .sa { color: #ed9d13 } /* Literal.String.Affix */ | |
+.highlight .sb { color: #ed9d13 } /* Literal.String.Backtick */ | |
+.highlight .sc { color: #ed9d13 } /* Literal.String.Char */ | |
+.highlight .dl { color: #ed9d13 } /* Literal.String.Delimiter */ | |
+.highlight .sd { color: #ed9d13 } /* Literal.String.Doc */ | |
+.highlight .s2 { color: #ed9d13 } /* Literal.String.Double */ | |
+.highlight .se { color: #ed9d13 } /* Literal.String.Escape */ | |
+.highlight .sh { color: #ed9d13 } /* Literal.String.Heredoc */ | |
+.highlight .si { color: #ed9d13 } /* Literal.String.Interpol */ | |
+.highlight .sx { color: #ffa500 } /* Literal.String.Other */ | |
+.highlight .sr { color: #ed9d13 } /* Literal.String.Regex */ | |
+.highlight .s1 { color: #ed9d13 } /* Literal.String.Single */ | |
+.highlight .ss { color: #ed9d13 } /* Literal.String.Symbol */ | |
+.highlight .bp { color: #24909d } /* Name.Builtin.Pseudo */ | |
+.highlight .fm { color: #447fcf } /* Name.Function.Magic */ | |
+.highlight .vc { color: #40ffff } /* Name.Variable.Class */ | |
+.highlight .vg { color: #40ffff } /* Name.Variable.Global */ | |
+.highlight .vi { color: #40ffff } /* Name.Variable.Instance */ | |
+.highlight .vm { color: #40ffff } /* Name.Variable.Magic */ | |
+.highlight .il { color: #3677a9 } /* Literal.Number.Integer.Long */ | |
+ | |
+pre code, .highlight.language-text { color: #e83e8c !important } | |
\ No newline at end of file | |
diff --git a/robots.txt b/robots.txt | |
new file mode 100644 | |
index 0000000..7b669f4 | |
--- /dev/null | |
+++ b/robots.txt | |
@@ -0,0 +1,3 @@ | |
+User-agent: * | |
+Disallow: /ctf.html | |
+Disallow: /ctf/ | |
\ No newline at end of file |
File Metadata
File Metadata
- Mime Type
- text/x-diff
- Expires
- Tue, Apr 15, 8:48 PM (6 h, 56 m)
- Storage Engine
- local-disk
- Storage Format
- Raw Data
- Storage Handle
- 0a/81/f4ebf3c226801908d196cd75581e