From 57c19107347e8993adcce10c6c011ac4a35d3968 Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 24 Sep 2026 18:58:02 +0200 Subject: [PATCH] humhub: mail-send loest Empfaenger zu GUIDs auf POST /mail nimmt als recipient ausschliesslich GUIDs an. Mit numerischen User-IDs antwortet die API "400 Validation failed" und "Du kannst keine Nachricht ohne Empfaenger senden!" - eine Meldung, die nach einem leeren Feld klingt, obwohl das Feld gefuellt ist. resolve_user gibt deshalb die GUID zurueck statt der ID; user_guid holt sie ueber /user/ nach, wenn die Benutzerliste sie nicht mitliefert. Geprueft gegen humhub.inmedias.it mit Benutzername, Profil-URL und numerischer ID; der Versand mit GUID ist belegt. Co-Authored-By: Claude Opus 5 (1M context) --- plugins/humhub/skills/humhub/SKILL.md | 7 +++++ plugins/humhub/skills/humhub/humhub.py | 37 +++++++++++++++++++------- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/plugins/humhub/skills/humhub/SKILL.md b/plugins/humhub/skills/humhub/SKILL.md index a3885f2..2123d27 100644 --- a/plugins/humhub/skills/humhub/SKILL.md +++ b/plugins/humhub/skills/humhub/SKILL.md @@ -102,6 +102,13 @@ und Wiki-Seiten. Empfänger sind Benutzername, Profil-URL oder numerische ID; mehrere mit Komma. +**`recipient` will GUIDs, keine User-IDs.** `mail-send` löst jede Angabe +deshalb bis zur GUID auf (`/user/` liefert sie, die Benutzerliste je nach +Fassung nicht). Mit numerischen IDs antwortet die API `400 Validation failed` +und „Du kannst keine Nachricht ohne Empfänger senden!" — die Meldung klingt +nach einem leeren Feld, obwohl das Feld gefüllt ist, und weist damit in die +falsche Richtung. + `mail-reply` hängt einen Beitrag an eine bestehende Konversation (`POST /mail//entry`). Die ID steht in der URL der Konversation: `…/mail/mail/index?id=35`. diff --git a/plugins/humhub/skills/humhub/humhub.py b/plugins/humhub/skills/humhub/humhub.py index ada3ec1..5d03861 100755 --- a/plugins/humhub/skills/humhub/humhub.py +++ b/plugins/humhub/skills/humhub/humhub.py @@ -350,8 +350,25 @@ def cmd_comment_create(args: argparse.Namespace) -> None: # ── Mail (Direktnachrichten) ───────────────────────────────────────────────── -def resolve_user(session: requests.Session, value: str) -> int: - """Resolve a user ID, username, or profile URL (…/u//) to a user ID. +def user_guid(session: requests.Session, user: dict) -> str: + """GUID eines Benutzers, notfalls über /user/ nachgeladen. + + Die Benutzerliste liefert die GUID je nach Fassung nicht mit. + """ + guid = user.get("guid") or (user.get("account") or {}).get("guid") + if guid: + return str(guid) + uid = user.get("id") or (user.get("account") or {}).get("id") + return str(api_get(session, f"/user/{uid}")["guid"]) + + +def resolve_user(session: requests.Session, value: str) -> str: + """Resolve a user ID, username, or profile URL (…/u//) to a user GUID. + + **GUID, nicht ID.** `POST /mail` nimmt als `recipient` ausschließlich + GUIDs an; mit numerischen IDs antwortet die API `400 Validation failed` + und „Du kannst keine Nachricht ohne Empfänger senden!" — eine Meldung, + die nach einem leeren Feld klingt, obwohl das Feld gefüllt ist. Slug und Benutzername sind nicht dasselbe: zu https://…/u/serge/ gehört der Benutzername "Serge". `get-by-username` kennt nur den Benutzernamen und @@ -360,7 +377,7 @@ def resolve_user(session: requests.Session, value: str) -> int: """ value = value.strip() if value.isdigit(): - return int(value) + return user_guid(session, {"id": int(value)}) name = value if "/" in value: teile = [t for t in urlparse(value).path.split("/") if t] @@ -370,7 +387,7 @@ def resolve_user(session: requests.Session, value: str) -> int: r = session.get(f"{BASE_URL}/user/get-by-username", params={"username": name}) if r.ok: - return int(r.json()["id"]) + return user_guid(session, r.json()) page = 1 while True: @@ -381,7 +398,7 @@ def resolve_user(session: requests.Session, value: str) -> int: slug_teile = [t for t in urlparse(u.get("url", "")).path.split("/") if t] slug = slug_teile[-1] if slug_teile else "" if name.lower() in (username.lower(), slug.lower()): - return int(u["id"]) + return user_guid(session, u) if len(results) < 50: break page += 1 @@ -390,17 +407,17 @@ def resolve_user(session: requests.Session, value: str) -> int: def cmd_mail_send(args: argparse.Namespace) -> None: session, _ = get_session() - ids = [resolve_user(session, teil) for teil in args.to.split(",") if teil.strip()] - if not ids: + guids = [resolve_user(session, teil) for teil in args.to.split(",") if teil.strip()] + if not guids: sys.exit("Error: kein Empfänger angegeben") message = read_file(args.file) data = api_post(session, "/mail", { "title": args.title, "message": message, - "recipient": ids, + "recipient": guids, }) - empfaenger = ", ".join(str(i) for i in ids) - print(f"Nachricht gesendet (Konversation {data.get('id')}) an Benutzer {empfaenger}") + empfaenger = ", ".join(guids) + print(f"Nachricht gesendet (Konversation {data.get('id')}) an {empfaenger}") def cmd_mail_reply(args: argparse.Namespace) -> None: