| 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, Nov 11, 11:46 PM (1 d, 3 h)
- Storage Engine
- local-disk
- Storage Format
- Raw Data
- Storage Handle
- 49/ff/2d40e8e3a3380e42b08ce232d685