kik-website/server/make_files.py
Zebreus 996cc53e56 Add the shell server's README and Code of Conduct files
The terminal versions of the pages for the home directory of the shell
server's root user, so each page's top command (cat README, cat README.en,
cat CODE_OF_CONDUCT, cat CODE_OF_CONDUCT.en) works in the web terminal.
Generated by server/make_files.py.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 03:04:12 +01:00

335 lines
17 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Builds README, README.en, CODE_OF_CONDUCT and CODE_OF_CONDUCT.en next to this script: the website's pages as
# terminal text with escape sequences, for the home directory of the shell server's root user.
# Usage: python3 server/make_files.py
import pathlib
import textwrap
ESC = '\x1b'
RESET = f'{ESC}[0m'
BOLD = f'{ESC}[1m'
DIM = f'{ESC}[38;2;31;154;63m' # the page's --dim
HOT = f'{ESC}[38;2;255;42;42m' # the page's --hot
WIDTH = 82 # the web terminal's width, as wide as the page column
INNER = WIDTH - 4 # text width inside "│ " ... " │"
SITE = 'https://berlin.ccc.de/kik/'
IMPRESSUM = 'https://berlin.ccc.de/page/impressum/'
TICKETS = 'https://tickets.berlin.ccc.de/cccb/kik-v0.1/'
CFP = 'https://cfp.berlin.ccc.de/kik-v01/cfp'
SCHEDULE = 'https://cfp.berlin.ccc.de/kik-v01/schedule'
MAP = 'https://www.openstreetmap.org/node/3872556357#map=20/52.5217003/13.3828041&layers=H'
def link(url, text, style=''):
return f'{ESC}]8;;{url}{ESC}\\{style}{text}{RESET if style else ""}{ESC}]8;;{ESC}\\'
class Line:
"""A line of text with its visible width, so boxes can be padded around styled text."""
def __init__(self, text='', width=None):
self.text, self.width = text, len(text) if width is None else width
def __add__(self, other):
return Line(self.text + other.text, self.width + other.width)
def styled(text, style):
return Line(f'{style}{text}{RESET}', len(text))
def linked(url, text, style=''):
return Line(link(url, text, style), len(text))
def joined(parts, gap=' '):
line = parts[0]
for part in parts[1:]:
line = line + Line(gap) + part
return line
def wrap(text, width, links=()):
"""Wraps plain text, then turns the given (text, url) pairs into links where they appear."""
lines = []
for raw in textwrap.wrap(text, width, break_on_hyphens=False, break_long_words=False):
rendered = raw
for needle, url in links:
rendered = rendered.replace(needle, link(url, needle))
lines.append(Line(rendered, len(raw)))
return lines
def paragraph(text, links=(), lead=None):
"""A paragraph, optionally starting with a bold lead-in like "Ticket:"."""
full = f'{lead} {text}' if lead else text
lines = wrap(full, INNER, links)
if lead:
first = lines[0]
lines[0] = Line(first.text.replace(lead, f'{BOLD}{lead}{RESET}', 1), first.width)
return lines
def pairs(items, label, links=()):
"""A key-value list like the page's: dim "Label :" column, values wrapped with a hanging indent."""
lines = []
for key, value in items:
wrapped = wrap(value, INNER - label, links)
lines.append(styled(f'{key} :'.ljust(label), DIM) + wrapped[0])
lines += [Line(' ' * label) + rest for rest in wrapped[1:]]
return lines
def bullets(items):
"""A list like the page's: dim "> " marker, bold title, text wrapped with a hanging indent."""
lines = []
for title, text in items:
wrapped = wrap(f'{title} {text}', INNER - 2)
first = wrapped[0]
lines.append(styled('> ', DIM) + Line(first.text.replace(title, f'{BOLD}{title}{RESET}', 1), first.width))
lines += [Line(' ') + rest for rest in wrapped[1:]]
return lines
def box(title, lines):
rule = WIDTH - 2 - len(f'─┤ {title} ├')
out = [Line('┌─') + styled('┤ ', DIM) + styled(title, BOLD) + styled(' ├', DIM) + Line('─' * rule + '┐')]
out += [Line('│ ') + line + Line(' ' * (INNER - line.width) + ' │') for line in lines]
out.append(Line('└' + '─' * (WIDTH - 2) + '┘'))
return out
def centered(line):
return Line(' ' * ((WIDTH - line.width) // 2)) + line
def nav(line):
return [styled('─' * WIDTH, DIM), line, styled('─' * WIDTH, DIM)]
def footer(parts):
line = linked(parts[0][1], parts[0][0], DIM)
for text, url in parts[1:]:
line = line + styled(' · ', DIM) + linked(url, text, DIM)
return [styled('─' * WIDTH, DIM), centered(line)]
def page(lines):
lines = [Line(), *lines, Line()]
for line in lines:
assert line.width <= WIDTH + 2, (line.width, line.text)
return ''.join(f'{line.text}\n' for line in lines)
def readme(t):
label = 3 + max(len(key) for key, _ in t['infos'] + t['route'])
return page([
styled('KHAOS IM ', BOLD) + styled('KIEZ', BOLD + HOT),
styled(t['date'], DIM),
Line(),
*nav(joined([linked(f'{SITE}{t["page"]}#{anchor}', f'[{n}] {title}') for n, title, anchor in t['sections']])),
Line(),
*box('{} {}'.format(*t['sections'][0][:2]), [
*paragraph(t['intro'][0]),
Line(),
*paragraph(t['intro'][1]),
Line(),
*pairs(t['infos'], label, links=[('cfp.berlin.ccc.de', 'https://cfp.berlin.ccc.de'), ('Code of Conduct', f'{SITE}{t["coc"]}')]),
]),
Line(),
*box('{} {}'.format(*t['sections'][1][:2]), [
*paragraph(t['ticket'], lead=t['ticket_lead']),
Line(),
linked(TICKETS, f'[ {t["get_ticket"]} ]', HOT),
Line(),
*paragraph(t['talks'], lead=t['talks_lead']),
Line(),
joined([linked(CFP, f'[ {t["submit"]} ]'), linked(SCHEDULE, f'[ {t["schedule"]} ]')]),
Line(),
*paragraph(t['other'], lead=t['other_lead']),
]),
Line(),
*box('{} {}'.format(*t['sections'][2][:2]), [
*pairs(t['route'], label, links=[('BBBike.de', 'http://www.bbbike.de/')]),
Line(),
linked(MAP, f'[ {t["map"]} ]'),
]),
Line(),
*footer(t['footer']),
])
def code_of_conduct(t):
return page([
*box('Code of Conduct', [
*paragraph(t['intro']),
Line(),
*bullets(t['rules']),
Line(),
*paragraph(t['house'], lead=t['house_lead']),
Line(),
*paragraph(t['problems'], lead=t['problems_lead']),
]),
Line(),
*footer(t['footer']),
])
DE = {
'page': '', 'coc': 'coc.html',
'date': 'CCC Berlin · 28. – 29.11.2026',
'sections': [(1, 'Infos', 'infos'), (2, 'Mitmachen', 'mitmachen'), (3, 'Anfahrt', 'anfahrt')],
'intro': [
'Khaos im Kiez ist ein kleines Chaos-Event, back to the roots: inspiriert von anderen kleinen Chaos-Events. '
'Ein Wochenende zum Chillen und um die Seite von Chaos-Events zu erleben, für die bei größeren Events oft '
'keine Zeit mehr bleibt.',
'Du kannst einen Talk oder Workshop halten, an deinen Projekten schrauben, gemeinsam hacken, löten, basteln '
'oder einfach vorbeikommen, zuhören und Leute kennenlernen. Alle sind willkommen, die Lust auf $Dinge, '
'Neugier und Chaos mitbringen.',
],
'infos': [
('Was', 'Khaos im Kiez (KiK)'),
('Wann', 'Sa 28.11. – So 29.11.2026, 11:00 – 23:59'),
('Wo', 'Marienstraße 11, 10117 Berlin'),
('Wer', 'Chaos Computer Club Berlin'),
('Preis', '0,00 € (Ticket trotzdem nötig, ~50 Plätze)'),
('Fahrplan', 'cfp.berlin.ccc.de'),
('Essen', 'Ewiges Frühstück, vegan'),
('Barrierefrei', 'Nicht barrierefrei.'),
('Regeln', 'Code of Conduct'),
],
'ticket_lead': 'Ticket:',
'ticket': 'Anmeldung über den Ticketshop. Das Ticket ist kostenlos, aber die Plätze sind begrenzt.',
'get_ticket': 'Ticket sichern',
'talks_lead': 'Talks & Workshops:',
'talks': 'Reiche deinen Beitrag im Call for Participation ein und schau dir später den Fahrplan an.',
'submit': 'Talk einreichen', 'schedule': 'Fahrplan',
'other_lead': 'Sonstiges:',
'other': 'Du willst anders helfen oder hast Fragen? Schreib an aprl [at] berlin [dot] ccc [dot] de.',
'route': [
('Adresse', 'Clubräume im Hinterhof der Marienstraße 11, 10117 Berlin'),
('Koordinaten', '52,52194° N, 13,38261° O'),
('Fahrrad', 'Im Innenhof gibt es eine begrenzte Zahl an Fahrradständern. Auf dem Weg liegen viele '
'Fahrradstraßen, nutz die am besten, z. B. mit BBBike.de.'),
('ÖPNV', 'Der Club liegt direkt beim S+U-Bahnhof Friedrichstraße (auch Regionalbahnen). Vom oberirdischen '
'S-Bahn-Steig (S3/S5/S7) nimmst du den Ausgang Schiffbauerdamm und gehst unter der Bahnbrücke über '
'die Spree. Über die Albrechtstraße kommst du zur Marienstraße.'),
('Fernverkehr', 'Aussteigen in Berlin Hauptbahnhof, dann mit S-Bahn oder Regionalbahn Richtung Osten eine '
'Station bis Friedrichstraße. Bahnhof Richtung Spree verlassen, über die Fußgängerbrücke und '
'der Albrechtstraße bis zur Stichstraße Marienstraße folgen.'),
('Flugzeug', 'BER: Airport Express (RE7, RB14) oder S9 bis Friedrichstraße, alternativ FEX bis Hauptbahnhof '
'(dann wie Fernverkehr). Achtung: Terminal 5 (früher SXF) ist nicht mit dem Rest des BER '
'verbunden, dort fährt nur die S9. Flugplatz Strausberg (EDAY): S7 direkt bis Friedrichstraße.'),
('Auto', 'Zufahrt von Hauptbahnhof oder Brandenburger Tor über die Luisenstraße. Von der Friedrichstraße '
'entweder über Schiffbauerdamm und Albrechtstraße (Außengastronomie und Kopfsteinpflaster) oder '
'über Reinhardstraße und Luisenstraße (die Albrechtstraße ist nur in Gegenrichtung befahrbar).'),
('Parken', 'Achtung: Es gibt kaum Parkplätze, und die Marienstraße und alle umliegenden Straßen sind '
'parkraumbewirtschaftet.'),
],
'map': 'Karte (OpenStreetMap)',
'footer': [('Impressum', IMPRESSUM), ('CCC Berlin', 'https://berlin.ccc.de/')],
}
EN = {
'page': 'en.html', 'coc': 'coc-en.html',
'date': 'CCC Berlin · 2026-11-28 – 2026-11-29',
'sections': [(1, 'Info', 'infos'), (2, 'Join in', 'join'), (3, 'Getting here', 'getting-here')],
'intro': [
'Khaos im Kiez is a small chaos event, back to the roots: inspired by other small chaos events. A weekend to '
'chill and to experience the side of chaos events that often gets no time at larger events.',
'Give a talk or workshop, work on your projects, hack together, solder, tinker, or simply drop by, listen and '
'meet people. Everyone who brings a love of $things, curiosity and a bit of chaos is welcome.',
],
'infos': [
('What', 'Khaos im Kiez (KiK)'),
('When', 'Sat 2026-11-28 – Sun 2026-11-29, 11:00 – 23:59'),
('Where', 'Marienstraße 11, 10117 Berlin, Germany'),
('Who', 'Chaos Computer Club Berlin'),
('Price', '€0.00 (ticket still required, ~50 seats)'),
('Schedule', 'cfp.berlin.ccc.de'),
('Food', 'Eternal breakfast, vegan'),
('Accessibility', 'Not accessible.'),
('Rules', 'Code of Conduct'),
],
'ticket_lead': 'Ticket:',
'ticket': 'Register via the ticket shop. The ticket is free, but seats are limited.',
'get_ticket': 'Get ticket',
'talks_lead': 'Talks & workshops:',
'talks': 'Submit your proposal via the call for participation and check the schedule later.',
'submit': 'Submit a talk', 'schedule': 'Schedule',
'other_lead': 'Other:',
'other': 'Want to help otherwise or have questions? Write to aprl [at] berlin [dot] ccc [dot] de.',
'route': [
('Address', 'Club rooms in the back yard of Marienstraße 11, 10117 Berlin'),
('Coordinates', '52.52194° N, 13.38261° E'),
('Bicycle', 'A limited number of bike racks are in the courtyard. Many bicycle streets lead there, preferably '
'use those, e.g. with BBBike.de.'),
('Local transit', 'The club is right next to S+U Friedrichstraße (regional trains stop there too). From the '
'elevated S-Bahn platform (S3/S5/S7) take the Schiffbauerdamm exit and walk across the Spree '
'beneath the railway bridge. Albrechtstraße leads to Marienstraße.'),
('Long distance', 'Get off at Berlin Hauptbahnhof, then take any S-Bahn or regional train eastbound one stop '
'to Friedrichstraße. Leave the station towards the Spree, cross the footbridge and follow '
'Albrechtstraße to the dead-end Marienstraße.'),
('Air', 'BER: Airport Express (RE7, RB14) or S9 to Friedrichstraße, or FEX to Hauptbahnhof (then as above). '
'Note: Terminal 5 (formerly SXF) is not connected to the rest of BER, only the S9 runs there. '
'Strausberg airfield (EDAY): S7 direct to Friedrichstraße.'),
('Car', 'Access from Hauptbahnhof or Brandenburger Tor via Luisenstraße. From Friedrichstraße either via '
'Schiffbauerdamm and Albrechtstraße (outdoor seating and cobblestones) or via Reinhardstraße and '
'Luisenstraße (Albrechtstraße is one-way the other direction).'),
('Parking', 'Warning: parking is scarce, and Marienstraße and all surrounding streets are subject to paid '
'parking zones.'),
],
'map': 'Map (OpenStreetMap)',
'footer': [('Legal notice', IMPRESSUM), ('CCC Berlin', 'https://berlin.ccc.de/')],
}
COC_DE = {
'intro': 'Khaos im Kiez ist eine Veranstaltung des Chaos Computer Club Berlin e.V. (CCCB). Damit sich alle '
'wohlfühlen, gelten diese Regeln für alle Teilnehmenden, Vortragenden und Helfenden.',
'rules': [
('Sei nett zueinander.', 'Geh respektvoll und freundlich miteinander um, auch wenn du anderer Meinung bist.'),
('Keine Diskriminierung.', 'Rassismus, Sexismus, Queerfeindlichkeit, Ableismus und andere menschenverachtende '
'Haltungen haben hier keinen Platz.'),
('Keine Belästigung.', 'Dazu gehören Beleidigungen, Bedrohungen, Stalking, unerwünschte körperliche '
'Berührungen und unerwünschte sexuelle Aufmerksamkeit. Ein Nein ist ein Nein.'),
('Frag vor dem Fotografieren.', 'Fotos, Video- und Tonaufnahmen von Personen nur mit deren Einverständnis. '
'Das gilt auch für Talks und Workshops.'),
('Achte auf Geräte und Eigentum.', 'Fass fremde Geräte nicht ohne Erlaubnis an. Geh pfleglich mit den Räumen '
'und dem Inventar um.'),
('Sei neugierig, nicht übergriffig.', 'Chaos ja, Grenzenüberschreitungen nein.'),
],
'house_lead': 'Hausrecht:',
'house': 'Die Orga hat Hausrecht. Bei Verstößen gegen diese Regeln kann sie Verwarnungen aussprechen, Personen '
'der Veranstaltung verweisen oder ein Hausverbot erteilen. Den Anweisungen der Orga ist Folge zu leisten.',
'problems_lead': 'Probleme?',
'problems': 'Wenn dir etwas passiert oder du etwas beobachtest, sprich eine Person aus der Orga an oder schreib an '
'aprl [at] berlin [dot] ccc [dot] de. Hinweise werden vertraulich behandelt.',
'footer': [('Startseite', SITE), ('Impressum', IMPRESSUM)],
}
COC_EN = {
'intro': 'Khaos im Kiez is an event of the Chaos Computer Club Berlin e.V. (CCCB). To make everyone feel '
'comfortable, these rules apply to all attendees, speakers and helpers.',
'rules': [
('Be nice to each other.', 'Treat each other respectfully and kindly, even when you disagree.'),
('No discrimination.', 'Racism, sexism, queerphobia, ableism and other hateful attitudes have no place here.'),
('No harassment.', 'This includes insults, threats, stalking, unwanted physical contact and unwanted sexual '
'attention. No means no.'),
('Ask before taking pictures.', 'Photos, video and audio recordings of people only with their consent. This '
'also applies to talks and workshops.'),
('Respect devices and property.', "Do not touch other people's devices without permission. Treat the rooms "
'and furniture with care.'),
('Be curious, not intrusive.', 'Chaos yes, crossing boundaries no.'),
],
'house_lead': 'House rules:',
'house': 'The organizers have the right to exclude people from the premises (Hausrecht). In case of violations '
"they may issue warnings, ask people to leave, or ban them from the event. Follow the organizers' "
'instructions.',
'problems_lead': 'Problems?',
'problems': 'If something happens to you or you notice something, talk to a member of the organizing team or '
'write to aprl [at] berlin [dot] ccc [dot] de. Reports are treated confidentially.',
'footer': [('Home', f'{SITE}en.html'), ('Legal notice', IMPRESSUM)],
}
out = pathlib.Path(__file__).parent
for name, text in [('README', readme(DE)), ('README.en', readme(EN)),
('CODE_OF_CONDUCT', code_of_conduct(COC_DE)), ('CODE_OF_CONDUCT.en', code_of_conduct(COC_EN))]:
(out / name).write_text(text, encoding='utf-8')