#!/usr/bin/env python3 """Append one reply to the newOS4 maintenance thread. Hermes writes here instead of POSTing to agent-replies.php, so an expired admin cookie can never make a reply look delivered when it was not. Same JSON schema and file the PHP endpoint serves to the in-app `feedback-inbox` command. Usage: newos4-reply.py "Triage 2026-10-23: ..." [--author hermes] """ import argparse, fcntl, json, os, time ROOT = '/var/www/webx11.duckdns.org/spaces/newos4' DIR = os.path.join(ROOT, 'data', 'maintenance') FILE = os.path.join(DIR, 'replies.json') MAX = 200 def main(): ap = argparse.ArgumentParser() ap.add_argument('body') ap.add_argument('--author', default='hermes', choices=['hermes', 'newos4']) a = ap.parse_args() body = a.body.strip() if not body: raise SystemExit('refusing to write an empty reply') body = body[:2000] os.makedirs(DIR, exist_ok=True) lock = open(FILE + '.lock', 'a+') fcntl.flock(lock, fcntl.LOCK_EX) try: try: with open(FILE, encoding='utf-8') as f: data = json.load(f) except Exception: data = [] if not isinstance(data, list): data = [] entry = { 'id': 'REP-' + time.strftime('%Y%m%d-%H%M%S') + '-' + os.urandom(3).hex(), 'created_at': time.strftime('%Y-%m-%dT%H:%M:%S+00:00', time.gmtime()), 'author': a.author, 'body': body, } data.append(entry) tmp = FILE + '.tmp' with open(tmp, 'w', encoding='utf-8') as f: json.dump(data[-MAX:], f, indent=2) f.flush() os.fsync(f.fileno()) os.replace(tmp, FILE) os.chmod(FILE, 0o664) try: os.chmod(FILE, 0o664) except Exception: pass print(entry['id']) finally: fcntl.flock(lock, fcntl.LOCK_UN) lock.close() if __name__ == '__main__': main()