Grotti 3.7.0: Treiber für Kobra 4 und Marlin, smartere Dateien-Auswahl und einstellbare Etiketten.
Senden und Abbruch laufen über den Treiber, Orca-Doppelzeilen und tote Ideen-API fallen weg, Drucker folgen der Datei, Etikettenformat liegt in den Einstellungen.
This commit is contained in:
1 parent
5081212212
commit
db6a85a703
54 files changed
+3379
-384
No files matched your search
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Empfänger für Grotti-Meldungen → Issue auf Forgejo/Gitea.
|
||||
|
||||
Läuft getrennt von den Installationen. Der Token bleibt hier.
|
||||
|
||||
export FORGEJO_URL=https://git.gundel.dev
|
||||
export FORGEJO_TOKEN=...
|
||||
export FORGEJO_REPO=dg/grotti
|
||||
python scripts/meldung_relay.py
|
||||
|
||||
Port über RELAY_PORT (Standard 8780). Vor Forgejo mit Reverse-Proxy unter
|
||||
/hooks/grotti-meldung legen.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||||
|
||||
|
||||
def env(name, default=""):
|
||||
return (os.environ.get(name) or default).strip()
|
||||
|
||||
|
||||
def create_issue(title, body):
|
||||
base = env("FORGEJO_URL", "https://git.gundel.dev").rstrip("/")
|
||||
repo = env("FORGEJO_REPO", "dg/grotti")
|
||||
token = env("FORGEJO_TOKEN")
|
||||
if not token:
|
||||
raise RuntimeError("FORGEJO_TOKEN fehlt.")
|
||||
url = "%s/api/v1/repos/%s/issues" % (base, repo)
|
||||
payload = json.dumps({"title": title[:120], "body": body}).encode("utf-8")
|
||||
request = urllib.request.Request(
|
||||
url,
|
||||
data=payload,
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": "token %s" % token,
|
||||
},
|
||||
method="POST",
|
||||
)
|
||||
with urllib.request.urlopen(request, timeout=20) as response:
|
||||
data = json.loads(response.read().decode("utf-8"))
|
||||
return data.get("html_url") or data.get("url") or ""
|
||||
|
||||
|
||||
def issue_body(data):
|
||||
lines = [
|
||||
data.get("text") or "",
|
||||
"",
|
||||
"---",
|
||||
"Version: %s" % (data.get("version") or "—"),
|
||||
"Seite: %s" % (data.get("page") or "—"),
|
||||
"Drucker: %s" % (data.get("printer_host") or "—"),
|
||||
]
|
||||
err = (data.get("error_text") or "").strip()
|
||||
if err:
|
||||
lines.extend(["", "Fehlertext:", "```", err[:2000], "```"])
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
class Handler(BaseHTTPRequestHandler):
|
||||
def log_message(self, fmt, *args):
|
||||
return
|
||||
|
||||
def do_GET(self):
|
||||
if self.path.split("?", 1)[0] in ("/", "/gesundheit", "/hooks/grotti-meldung"):
|
||||
self._json(200, {"ok": True})
|
||||
return
|
||||
self._json(404, {"message": "Unbekannt."})
|
||||
|
||||
def do_POST(self):
|
||||
path = self.path.split("?", 1)[0]
|
||||
if path not in ("/", "/hooks/grotti-meldung"):
|
||||
self._json(404, {"message": "Unbekannt."})
|
||||
return
|
||||
length = int(self.headers.get("Content-Length") or 0)
|
||||
raw = self.rfile.read(length) if length else b"{}"
|
||||
try:
|
||||
data = json.loads(raw.decode("utf-8"))
|
||||
except (UnicodeError, json.JSONDecodeError):
|
||||
self._json(400, {"message": "Kein JSON."})
|
||||
return
|
||||
if not isinstance(data, dict) or not (data.get("text") or "").strip():
|
||||
self._json(400, {"message": "text fehlt."})
|
||||
return
|
||||
title = "Grotti: %s" % ((data.get("text") or "").strip().splitlines()[0][:80])
|
||||
try:
|
||||
url = create_issue(title, issue_body(data))
|
||||
except Exception as exc:
|
||||
self._json(502, {"message": str(exc)})
|
||||
return
|
||||
self._json(201, {"ok": True, "url": url})
|
||||
|
||||
def _json(self, code, payload):
|
||||
body = json.dumps(payload).encode("utf-8")
|
||||
self.send_response(code)
|
||||
self.send_header("Content-Type", "application/json; charset=utf-8")
|
||||
self.send_header("Content-Length", str(len(body)))
|
||||
self.end_headers()
|
||||
self.wfile.write(body)
|
||||
|
||||
|
||||
def main():
|
||||
port = int(env("RELAY_PORT", "8780") or 8780)
|
||||
httpd = ThreadingHTTPServer(("127.0.0.1", port), Handler)
|
||||
httpd.serve_forever()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user