Kokkoja as of 2026-10-10: the teacher's requests, the Session window, notices, the QR code, the license

This commit is contained in:
ack committed 2026-10-10 19:01:20 +02:00
1 parent 95c80d04c9
commit 1d087af633
32 files changed
+4917 -890

No files matched your search

+38 -5
View File
@@ -106,6 +106,31 @@ def load_token(directory):
return token
def remembered_relay(directory, given=""):
"""The relay to use: the one given, which is then remembered in the
configuration directory, or else the one remembered from last time.
Returns (address, remembered): the address, or None if none was given
and none is remembered, and whether it came from memory. A relay
address is typed once; afterwards the program is started bare (Andy,
2026-10-08, after a start without --relay sent an invitation to a
relay on his own computer that was not running).
"""
path = directory / "relay"
if given:
given = given.strip()
directory.mkdir(parents=True, exist_ok=True)
if path.exists() and path.read_text().strip() == given:
return given, False
write_atomically(path, (given + "\n").encode())
return given, False
try:
kept = path.read_text().strip()
except OSError:
kept = ""
return (kept, True) if kept else (None, False)
def write_atomically(path, data):
"""Replace a file so that it is never seen half written."""
temporary = path.with_name("." + path.name + ".writing")
@@ -132,7 +157,8 @@ def keep_page(notebook):
changed = current.stat().st_mtime
except FileNotFoundError:
return None, False
if b"data-stroke=" not in data and b"<image" not in data:
# Blank: no stroke, no text block, no image.
if not any(mark in data for mark in (b"data-stroke=", b"data-text=", b"<image")):
return None, False
kept = [entry for entry in notebook.iterdir()
@@ -459,9 +485,10 @@ def make_server(port, relay, notebooks, token, config, site=""):
def main():
parser = argparse.ArgumentParser(description="Kokkoja local program")
parser.add_argument("--port", type=int, default=8760)
parser.add_argument("--relay", default="ws://127.0.0.1:8770",
help="relay used for sessions started here "
"(default ws://127.0.0.1:8770)")
parser.add_argument("--relay", default="",
help="relay used for sessions started here, such as "
"wss://kokkoja.example/relay; remembered, so it "
"need be given only once")
parser.add_argument("--site", default="",
help="Kokkoja web site that invitations link to, so that "
"guests can join with nothing installed (default: "
@@ -478,10 +505,16 @@ def main():
notebooks = arguments.notebooks or default_notebooks_directory()
config = arguments.config or config_directory()
relay, remembered = remembered_relay(config, arguments.relay)
if relay is None:
parser.exit(2, "Kokkoja needs the address of a relay the first time it is started:\n"
" python3 kokkoja.py --relay wss://kokkoja.example/relay\n"
"Afterwards the address is remembered and python3 kokkoja.py is enough.\n")
token = load_token(config)
server = make_server(arguments.port, arguments.relay, notebooks, token, config, arguments.site)
server = make_server(arguments.port, relay, notebooks, token, config, arguments.site)
address = f"http://localhost:{arguments.port}/#token={token}"
print(f"Kokkoja is at {address}", flush=True)
print(f"Relay: {relay}" + (" (remembered from last time)" if remembered else ""), flush=True)
print(f"Notebooks are in {notebooks}", flush=True)
print("Ctrl-C to stop.", flush=True)
if not arguments.no_browser: