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 '
N/A
' + else: + formatted = f"{n}{'th' if 11 <= (n % 100) <= 13 else ['th', 'st', 'nd', 'rd', 'th'][min(n % 10, 4)]}" + if n <= 3: + return '
' + formatted + '
' + else: + return '
' + formatted + '
' + + +special_field_names = { + 'name': 'Challenge ', + 'writeup-url': 'Full writeup', + #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'{v} {" ".join([v for k, v in chall_decor.items() if k in c])}', + 'challenge-written': lambda v,_: f'{v}', + 'writeup-url': lambda v,_: '' + ('โœ”๏ธ' if v else 'โŒ') + '', + 'solve-status': lambda v,_: '' + {'solved': 'โœ”๏ธ', 'co-solved': '๐Ÿค', 'sniped': '๐Ÿ’ค', 'unsolved': '๐Ÿ‘€'}[v] + '', + #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 . + date=f'', + duration=f'{v["duration"]}h', #currently we are hardcoding hours, but we can always parse + type=v['type'], + team=v['team'], + rank='
Organizer
' + if v['organizer'] else + (('
' + v['rank'] + '
' if isinstance(v['rank'], str) else get_ordinal(v['rank'])) + + ('โœจ' if v['full-clear'] else '')), + #use the first challenge as header definition + challengeheader='' + + ''.join([ + #normal fields that are named as expected so we can just use it as the headers + '' + name.replace('-', ' ').capitalize() + '' + if name not in special_field_names else + #special fields that needs renaming + special_field_names[name] + for name in v['challenges'][0].keys()]) + + '' + if 'challenges' in v else "
No specific challenges have been logged; It's all a team effort!
", #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(['' + #assume every chall object follows the same format as the first, or else header mismatches + + ''.join([ + f'{field}' + if name not in special_fields else + special_fields[name](field, chall) + for name, field in chall.items()]) + + '' + 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 @@ + +
+
+ + + +
+ {rank} +
+
{team}
+
+
+ +
+
+ + + {challengeheader} + {challenges} +
+
+
+
\ 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 @@ + + + + + + + + + + + + + + + + desp's CTF diary + + + +
+
+ +
+
+
CTF diary
+
+ A timeline on the CTFs I've played in, challenges I've solved, and comments on the challenges. +
+
+
+ + +
+ +
+ +
+ +
+ + {ctfs} + +
+ +
+
+ + + + + + + + +
+

Made from scratch by despawningbone with Bootstrap and AOS - view in codestash

+
+ + + + + + + + + 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 @@ +
+
+ {content} +
+
\ 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 @@ + + - Hi! - despawningbone's portfolio + Hi! - desp's portfolio - +
despawningbone
Just a weeb who loves tech.

About me

Heyo! I am an aspiring computer science major from Hong Kong who has been coding for fun since 2016.
As one of my major hobbies, I try to explore a variety of different technical fields, from hardware development to UI design.
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.
Recently though, I've been spending more time on cybersecurity, and I hope to be able to work on this field in the future.

Technical Skills

Java
Preferred language
C#
Alternative to java
Python
Prototyping/automation
git
Adept at common workflows
SQL
SQLite/MySQL integrations
Linux
Sysadmin on debian-based
IDA
Reversing Win32 PEs
Pentesting
Metasploit; Known-CVE scripts
Win32API
Formats and functions
C/C++
Novice knowledge for low level interactions
Arduino/Pi
Small projects e.g. cap. keypad
CSS
Theming web apps
JavaScript
Basic knowledge
HTML
Basic knowledge

Other Skills and Hobbies

Cantonese
Mother tongue
English
Most fluent second language
Mandarin
Can carry out daily conversations
Japanese
(Work in progress)
Amateur Photography
The backgrounds here are taken by me!

Made from scratch by despawningbone with Bootstrap and AOS - view in codestash

- + 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 @@ + + - Hi! - despawningbone's portfolio + Hi! - desp's portfolio - +
-
+
Project Highlights
More projects can be found on the codestash!

Vortex Network

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.
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.

Project since July 2017

despbot

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.
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.
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 ๐Ÿ˜Š

View in codestash ยท Project since Jan 2017

Spigot Plugins

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?
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.

Project since Dec 2016 ยท on hiatus

Made from scratch by despawningbone with Bootstrap and AOS - view in codestash

- + 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