obliviously.., duh!

~^▼^~

bf resume

  • 18M, bisexual
  • want to go on adventures
  • love cooking and baking
  • will laugh at all your dumb jokes
  • i can provide free therapy
  • looove reading (favs: shades of magic, kingkiller chronicles)
  • morning person but i love late night talking
  • wont shut up ever
  • fav bands are krezip, 5sos, 1d, mcr, paramore
  • will send 20 reels a day
  • i can fix your printer if needed

cat.c

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>

#define CHUNK 128

int cat(int fd);
void cleanup(void);

int fd = STDIN_FILENO;

int main(int argc, char **argv) {
  atexit(cleanup);

  if(argc >= 2) {
    char *path = argv[1];
    fd = open(path, O_RDONLY);

    if(fd == -1) return 1;
  }

  return cat(fd);
}

void cleanup(void) {
  if(fd != STDIN_FILENO) close(fd);
}

int cat(int fd) {
  uint8_t buf[CHUNK + 1] = {0};
  ssize_t count = 0;

  while((count = read(fd, buf, CHUNK))) {
    if(count == -1) return 1;
    printf("%s", buf);
    memset(buf, 0, CHUNK);
  }

  return 0;
}

I: Reimplementing the coreutils in C

pong.py

#!/usr/bin/python3

import pygame

s = 80
v = 5
w = 50
h = 300
d = 40

pygame.init()
screen = pygame.display.set_mode((1280, 780))
font = pygame.freetype.Font("font.ttf", s)
clock = pygame.time.Clock()
running = True
dt = 0

fps = 60

ball = {
	"pos": pygame.Vector2(screen.get_width() / 2, screen.get_height() / 2),
	"vel": pygame.Vector2(v, v)
}

p1_points = 0
p2_points = 0

player1 = pygame.Rect(0, screen.get_height()/2 - h/2, w, h)
player2 = pygame.Rect(screen.get_width() - w, screen.get_height()/2 - h/2, w, h)

while running:
  for event in pygame.event.get():
    if event.type == pygame.QUIT:
      running = False

  screen.fill("purple")

  keys = pygame.key.get_pressed()
  if keys[pygame.K_w]:
      player1.y -= abs(ball["vel"].x) * fps * dt
  if keys[pygame.K_s]:
      player1.y += abs(ball["vel"].x) * fps * dt
  if keys[pygame.K_UP]:
      player2.y -= abs(ball["vel"].x) * fps * dt
  if keys[pygame.K_DOWN]:
      player2.y += abs(ball["vel"].x) * fps * dt

  # Rechts uit het scherm
  if ball["pos"].x + d/2 >= screen.get_width():
    ball["pos"].x = screen.get_width() / 2
    ball["pos"].y = screen.get_height() / 2
    ball["vel"] = pygame.Vector2(-v, v)
    p1_points += 1

  # Links uit het scherm
  if ball["pos"].x <= d/2:
    ball["pos"].x = screen.get_width() / 2
    ball["pos"].y = screen.get_height() / 2
    ball["vel"] = pygame.Vector2(v, v)
    p2_points += 1

  if ball["pos"].y + d/2 >= screen.get_height():
    ball["vel"].y *= -1

  if ball["pos"].y <= d/2:
    ball["vel"].y *= -1

  if player1.collidepoint(ball["pos"]) or player2.collidepoint(ball["pos"]):
    ball["vel"].x *= -1.2

  ball["pos"].x += ball["vel"].x * fps * dt
  ball["pos"].y += ball["vel"].y * fps * dt

  pygame.draw.circle(screen, "white", ball["pos"], d)
  pygame.draw.rect(screen, "white", player1)
  pygame.draw.rect(screen, "white", player2)

  font.render_to(screen, (s, s), str(p1_points), (255, 255, 255))
  font.render_to(screen, (screen.get_width() - 2*s, s), str(p2_points), (255, 255, 255))

  pygame.display.flip()

  dt = clock.tick(fps) / 1000

pygame.quit()

Pong in 85 lines of Python :)

cal.py

#!/usr/bin/env python3

import requests
from datetime import date
from datetime import timedelta
from icalendar import Calendar

MY_URI = "https://api.somtoday.nl/rest/v1/icalendar/stream/XXX/XXX"
YOUR_URI = "https://api.somtoday.nl/rest/v1/icalendar/stream/YYY/YYY"

def fetch_schedule(url):
    response = requests.get(url)
    
    if response.status_code == 200:
        return response.text

    raise Exception(f"Failed to fetch schedule, got: {response.status_code}")

def time_range(schedule, date):
    calendar = Calendar.from_ical(schedule)   
    events = []
    
    for component in calendar.walk('vevent'):
        event = {
          "start": component.get('dtstart').dt,
          "end": component.get('dtend').dt
        }
        
        if event['start'].date() == date:
            events.append(event)
    
    if not events:
        # No events for this day
        return None, None
    else:    
        events = sorted(events, key=lambda e: e['start'])
        return events[0], events[-1]

my_schedule = fetch_schedule(MY_URI)
your_schedule = fetch_schedule(YOUR_URI)

now = date.today()

for i in range(0, 7):
  date = now + timedelta(days=i)
  my_first, my_last = time_range(my_schedule, date)
  your_first, your_last = time_range(your_schedule, date)

  if not my_first or not my_last or not your_first or not your_last:
      continue

  if my_first['start'].time() == your_first['start'].time():
      print(f'{date} {my_first['start'].time()}')

  if my_last['end'].time() == your_last['end'].time():
      print(f'{date} {my_last['end'].time()}')

Script that given two Somtoday iCalendar URLs, calculates which days in the following week have matching first and last hours.

auth.py

import json
import dataclasses
import requests

@dataclasses.dataclass
class Credentials:
  access_token: str
  refresh_token: str
  api_url: str

DATA_FILE = 'creds.json'
ENDPOINT = 'https://somtoday.nl/oauth2/token'
CLIENT_ID = 'D50E0C06-32D1-4B41-A137-A9A850C892C2'

def refresh():
  body = {
    "grant_type": 'refresh_token',
    "refresh_token": read('refresh_token'),
    "client_id": CLIENT_ID
  }

  data = requests.post(ENDPOINT, data=body).json()
  write(data) # Save credentials for refreshing the next time.

  api_url = data.get("somtoday_api_url", "https://api.somtoday.nl")
  refresh_token = data.get("refresh_token")
  access_token = data.get("access_token")

  return Credentials(access_token, refresh_token, api_url)

def read(key = None):
  with open(DATA_FILE, "r") as file:
    data = json.load(file)
    return data[key] if key else data

def write(data):
  with open(DATA_FILE, "w") as file:
    json.dump(data, file)

Python script for (re)authenticating with the Somtoday API.

grades.py

#!/usr/bin/env python3

import os
import pickle
import pytz
import requests
import auth # from auth.py
import json

from datetime import datetime, timedelta

GRADES_FILE = "grades.pkl"

def get(endpoint, credentials):
  headers = {
      "Authorization": f'Bearer {credentials.access_token}',
      "Accept": 'application/json',
      "Origin": 'https://somtoday.nl',
      "Range": 'items=0-1000'
  }

  url = f'{credentials.api_url}{endpoint}'
  response = requests.get(url, headers=headers)

  return response.json()

def fetch_student_id(credentials):
  data = get('/rest/v1/leerlingen', credentials)
  return data['items'][0]['links'][0]['id']

def fetch_grades(student_id, credentials):
  data = get(f'/rest/v1/resultaten/huidigVoorLeerling/{student_id}', credentials)
  items = data['items']

  # We're not interested in averages, we're only interested in the grades themselves.
  useless_types = ["PeriodeGemiddeldeKolom", "RapportGemiddeldeKolom", "SEGemiddeldeKolom"]
  useless_subjects = ["4a-in", "5a-in", "6a-in", "LOB", "MEN", "PWS", "LO"]

  items = list(filter(lambda item: not item['type'] in useless_types, items))
  items = list(filter(lambda item: not item["vak"]["afkorting"] in useless_subjects, items))

  # Filter out grades that do not actually contain a grade. Ghost grades.
  items = list(filter(lambda item: "geldendResultaat" in item or "resultaatLabelAfkorting" in item, items))

  sort_by_datetime = lambda x: parse_item_datetime(x)
  items =  sorted(items, key = sort_by_datetime, reverse = True)

  grade_list = []

  for grade in items:
    try: result = grade["geldendResultaat"]
    # For letter grades, as opposed to 1-10 results.
    except KeyError: result = grade["resultaatLabelAfkorting"]

    datetime = parse_item_datetime(grade).strftime("%d/%m/%Y %H:%M:%S")
    subject = grade["vak"]["naam"]
    title = grade["omschrijving"]
    weight = grade["weging"]

    grade_list.append({
        "result": result,
        "datetime": datetime,
        "subject": subject,
        "title": title,
        "weight": weight
    })

  return grade_list  

def diff_grades(credentials):
  student_id = fetch_student_id(credentials)
  grade_list = fetch_grades(student_id, credentials)

  if grade_list != load_existing_grades():
    write_new_grades(grade_list)

    subject = grade_list[0]["subject"]
    send_notification(f'New grade for {subject}')

def send_notification(message):
  body = {
    "content": message,
    "components": [],
    "actions": {}
  }

  endpoint = "https://discord.com/api/webhooks/..."
  requests.post(endpoint, json=body)

# Helpers

def parse_item_datetime(item):
  return datetime.strptime(item["datumInvoer"], "%Y-%m-%dT%H:%M:%S.%f%z")


def load_existing_grades():
  if not os.path.isfile(GRADES_FILE):
    return []
  else: 
    with open(GRADES_FILE, "rb") as file:
      return pickle.load(file)

def write_new_grades(grades):
  with open(GRADES_FILE, "wb") as file:
    pickle.dump(grades, file)

# Main program :)

credentials = auth.refresh()
student_id = fetch_student_id(credentials)

diff_grades(credentials)

Simple script that sends Discord notifications when teachers publish grades

20:33. It's test week. Ah yesh. We love this.

Okay this is completely my own fault for postponing all the work 'til the last moment. But this is kind of stressing me out. So… yeaa?

Oops.

pov: trying to google-stalk someone but failing bc they are being healthy and not terminally online.

realizes how much of my own stuff is online and how easily findable it is

PANICS

when you should be going to bed but actually that seems like too much effort but you should really but just one more thing but really i should go but i dont want to

AAAAAAAAAAAAAAAAAAAa

query.js

// Shamelessly stolen from @devastatia :)

function $(query) {
  if (query.startsWith('#') && !query.includes(' ')) {
    return document.querySelector(query);
  }
  const nodes = document.querySelectorAll(query);
  return (nodes.length == 1) ? nodes.item(0) : nodes;
}

Who needs jQuery anyway??

You know that feeling when you're listening to a song and singing along with the lyrics, and then suddenly it's like you hear the lyrics for the first time when you realise their meaning and then it makes you cry?

Hello, Computer!

This is actually the second post that this monstrosity is able to properly render. —I accidentally threw away my data folder the first time around. Anyway,… Hello! welcome :)

This little thing is a prototype for my untitled successor to neopub, temporarily codenamed Pubb for the engine and Pebble for the CMS.

I wrote about it before on my blog:

I'm building a successor to neopub, my microblogging platform. It's still in early development, but things are going great so far. It's loosely based on the original source code of neopub—which is why its repo is hosted under the neopub namespace—, but has been rewritten from the ground up.

Here's the idea:

A single-person microblogging platform written in PHP8, built on open standards. It supports microformats2, micropub, Webmention, pingback, various feeds, ActivityPub and OpenHeart, along with @mentions, which I recently implemented on this site as well. There's going to be a very basic web interface for writing posts and uploading images. Additionally, there will also be a CLI—I'm gonna be using it myself, after all.

https://roblog.nl/blog/challenge-accepted

I've implemented IndieAuth and @mentions. Webmention and pingback are implemented too, but I'm not yet sending any. I haven't tested receiving. ActivityPub is complicated. The feeds work.

So yea. Shit works.

CC @devastatia