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/<id> 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) <noreply@anthropic.com>
This commit is contained in:
jhsandClaude Opus 5 committed 2026-09-24 18:58:02 +02:00
1 parent 7efb302c10
commit 57c1910734
2 files changed
+34 -10

No files matched your search

+7
View File
@@ -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/<id>` 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/<id>/entry`). Die ID steht in der URL der Konversation:
`…/mail/mail/index?id=35`.
+27 -10
View File
@@ -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/<slug>/) to a user ID.
def user_guid(session: requests.Session, user: dict) -> str:
"""GUID eines Benutzers, notfalls über /user/<id> 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/<slug>/) 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: