Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15dedb3d3f |
No files matched your search
@@ -1,6 +0,0 @@
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
||||
# lokale Umgebungs-/Secret-Dateien
|
||||
.env
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "humhub",
|
||||
"version": "0.3.0",
|
||||
"description": "HumHub via REST-API: Wiki-Seiten, Posts, Umfragen und Kommentare auflisten, anzeigen, anlegen und aktualisieren (humhub.py).",
|
||||
"version": "0.2.0",
|
||||
"description": "HumHub via REST-API: Wiki-Seiten, Posts und Umfragen auflisten, anzeigen, anlegen und aktualisieren (humhub.py).",
|
||||
"author": {
|
||||
"name": "inmedias.it"
|
||||
}
|
||||
|
||||
@@ -80,43 +80,6 @@ und nicht Komfort — ohne Begrenzung antwortet die Instanz ab ~25 Umfragen
|
||||
ebenfalls mit 500. Ein fehlgeschlagener POST legt nichts an; vor einem zweiten
|
||||
Versuch trotzdem mit `poll-list` prüfen.
|
||||
|
||||
### Kommentare
|
||||
|
||||
```bash
|
||||
./humhub.py comment-list <content-id|perma-url> # Kommentare eines Inhalts
|
||||
./humhub.py comment-create <content-id|perma-url> [datei|-] # Kommentar schreiben
|
||||
```
|
||||
|
||||
Beide nehmen wie `fetch` eine **Content-ID** oder Perma-URL (nicht die Post-/
|
||||
Wiki-Seiten-ID) und lösen das Zielobjekt selbst auf — funktioniert für Posts
|
||||
und Wiki-Seiten.
|
||||
|
||||
### Direktnachrichten
|
||||
|
||||
```bash
|
||||
./humhub.py mail-send <empfaenger> "<betreff>" [datei|-]
|
||||
./humhub.py mail-send https://humhub.inmedias.it/u/serge/ "Betreff" entwurf.md
|
||||
./humhub.py mail-send jan.stobbe,26 "Betreff" -
|
||||
./humhub.py mail-reply <konversations-id> [datei|-]
|
||||
```
|
||||
|
||||
Empfänger sind Benutzername, Profil-URL oder numerische ID; mehrere mit Komma.
|
||||
|
||||
`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`.
|
||||
|
||||
**Slug ≠ Benutzername.** Zur Profil-URL `/u/serge/` gehört der Benutzername
|
||||
`Serge`, und `get-by-username` unterscheidet Groß-/Kleinschreibung. Wer den
|
||||
Slug einsetzt, bekommt sonst `User not found!`. `mail-send` fängt das ab: erst
|
||||
`get-by-username`, dann ein Durchlauf der Benutzerliste, der Benutzername und
|
||||
Slug ohne Rücksicht auf Groß-/Kleinschreibung vergleicht.
|
||||
|
||||
Zum Lesen von Konversationen gibt es bewusst keinen Befehl — Nachrichten
|
||||
anderer gehören nicht ungefragt in einen Modellkontext. Auch `mail-reply`
|
||||
holt den Verlauf nicht ab, sondern prüft nur, ob die Konversation erreichbar
|
||||
ist.
|
||||
|
||||
Inhalte (Markdown) aus Datei oder `-` (stdin) übergeben — nicht als langes
|
||||
Shell-Argument (Sonderzeichen). `container_id` akzeptiert eine numerische
|
||||
Container-ID, einen **Space-Slug** oder eine **Space-URL** (`resolve_container`
|
||||
|
||||
Binary file not shown.
@@ -306,118 +306,6 @@ def cmd_space_list(args: argparse.Namespace) -> None:
|
||||
print("(keine Spaces gefunden)")
|
||||
|
||||
|
||||
# ── Comment commands ─────────────────────────────────────────────────────────
|
||||
|
||||
def cmd_comment_list(args: argparse.Namespace) -> None:
|
||||
session, _ = get_session()
|
||||
content_id = parse_content_ref(args.ref)
|
||||
data = api_get(session, f"/comment/content/{content_id}")
|
||||
results = data.get("results", data if isinstance(data, list) else [])
|
||||
if not results:
|
||||
print("(keine Kommentare)")
|
||||
return
|
||||
for c in results:
|
||||
cid = c.get("id")
|
||||
author = (c.get("createdBy") or {}).get("display_name", "?")
|
||||
created = c.get("createdAt", "")
|
||||
msg = (c.get("message") or "").replace("\n", " ")[:100]
|
||||
print(f"ID {cid:>5} [{created}] {author}: {msg}")
|
||||
|
||||
|
||||
def cmd_comment_create(args: argparse.Namespace) -> None:
|
||||
session, _ = get_session()
|
||||
content_id = parse_content_ref(args.ref)
|
||||
message = read_file(args.file)
|
||||
|
||||
envelope = api_get(session, f"/content/{content_id}")
|
||||
meta = envelope.get("metadata", {})
|
||||
model = meta.get("object_model", "")
|
||||
object_id = meta.get("object_id")
|
||||
if not model or not object_id:
|
||||
sys.exit(f"Error: Content {content_id} nicht auflösbar (object_model/object_id fehlen)")
|
||||
|
||||
payload = {
|
||||
"objectModel": model,
|
||||
"objectId": object_id,
|
||||
"Comment": {"message": message},
|
||||
}
|
||||
data = api_post(session, "/comment", payload)
|
||||
cid = data.get("id")
|
||||
short_model = model.split("\\")[-1]
|
||||
print(f"Kommentar erstellt (ID {cid}) auf {short_model} {object_id}")
|
||||
print(f"Perma: https://humhub.inmedias.it/content/perma?id={content_id}")
|
||||
|
||||
|
||||
# ── 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.
|
||||
|
||||
Slug und Benutzername sind nicht dasselbe: zu https://…/u/serge/ gehört der
|
||||
Benutzername "Serge". `get-by-username` kennt nur den Benutzernamen und
|
||||
unterscheidet Groß-/Kleinschreibung — deshalb der Fallback über die
|
||||
Benutzerliste, der auch den Slug vergleicht.
|
||||
"""
|
||||
value = value.strip()
|
||||
if value.isdigit():
|
||||
return int(value)
|
||||
name = value
|
||||
if "/" in value:
|
||||
teile = [t for t in urlparse(value).path.split("/") if t]
|
||||
if teile:
|
||||
name = teile[-1]
|
||||
|
||||
r = session.get(f"{BASE_URL}/user/get-by-username",
|
||||
params={"username": name})
|
||||
if r.ok:
|
||||
return int(r.json()["id"])
|
||||
|
||||
page = 1
|
||||
while True:
|
||||
data = api_get(session, f"/user?limit=50&page={page}")
|
||||
results = data.get("results", [])
|
||||
for u in results:
|
||||
username = (u.get("account") or {}).get("username", "")
|
||||
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"])
|
||||
if len(results) < 50:
|
||||
break
|
||||
page += 1
|
||||
sys.exit(f"Error: Benutzer {value!r} nicht gefunden")
|
||||
|
||||
|
||||
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:
|
||||
sys.exit("Error: kein Empfänger angegeben")
|
||||
message = read_file(args.file)
|
||||
data = api_post(session, "/mail", {
|
||||
"title": args.title,
|
||||
"message": message,
|
||||
"recipient": ids,
|
||||
})
|
||||
empfaenger = ", ".join(str(i) for i in ids)
|
||||
print(f"Nachricht gesendet (Konversation {data.get('id')}) an Benutzer {empfaenger}")
|
||||
|
||||
|
||||
def cmd_mail_reply(args: argparse.Namespace) -> None:
|
||||
"""Antwortet in einer bestehenden Konversation.
|
||||
|
||||
Bewusst ohne Lesebefehl: der Text der Konversation wird nicht abgerufen,
|
||||
nur ihre Erreichbarkeit geprueft.
|
||||
"""
|
||||
session, _ = get_session()
|
||||
message = read_file(args.file)
|
||||
r = session.get(f"{BASE_URL}/mail/{args.id}")
|
||||
if not r.ok:
|
||||
sys.exit(f"Error {r.status_code}: Konversation {args.id} nicht erreichbar")
|
||||
data = api_post(session, f"/mail/{args.id}/entry", {"message": message})
|
||||
print(f"Antwort geschrieben (Eintrag {data.get('id')}) in Konversation {args.id}")
|
||||
|
||||
|
||||
# ── Fetch (perma-/content-id-agnostisch) ─────────────────────────────────────
|
||||
|
||||
def parse_content_ref(ref: str) -> int:
|
||||
@@ -572,30 +460,6 @@ def main() -> None:
|
||||
p.add_argument("--topics", default=None, help="Komma-getrennte Topic-Namen")
|
||||
p.set_defaults(func=cmd_post_create)
|
||||
|
||||
# comment-list
|
||||
p = sub.add_parser("comment-list", help="Kommentare eines Inhalts auflisten")
|
||||
p.add_argument("ref", help="Content-ID oder Perma-Link (…/content/perma?id=NNNN)")
|
||||
p.set_defaults(func=cmd_comment_list)
|
||||
|
||||
# comment-create
|
||||
p = sub.add_parser("comment-create", help="Kommentar auf einen Inhalt (Post/Wiki-Seite) schreiben")
|
||||
p.add_argument("ref", help="Content-ID oder Perma-Link (…/content/perma?id=NNNN)")
|
||||
p.add_argument("file", help="Markdown-Datei oder '-' für stdin")
|
||||
p.set_defaults(func=cmd_comment_create)
|
||||
|
||||
# mail-send
|
||||
p = sub.add_parser("mail-send", help="Direktnachricht an Benutzer senden")
|
||||
p.add_argument("to", help="Benutzername, Profil-URL oder ID; mehrere mit Komma")
|
||||
p.add_argument("title", help="Betreff")
|
||||
p.add_argument("file", help="Markdown-Datei oder '-' für stdin")
|
||||
p.set_defaults(func=cmd_mail_send)
|
||||
|
||||
# mail-reply
|
||||
p = sub.add_parser("mail-reply", help="In einer bestehenden Konversation antworten")
|
||||
p.add_argument("id", type=int, help="Konversations-ID (aus /mail/mail/index?id=NN)")
|
||||
p.add_argument("file", help="Markdown-Datei oder '-' für stdin")
|
||||
p.set_defaults(func=cmd_mail_reply)
|
||||
|
||||
# post-update
|
||||
p = sub.add_parser("post-update", help="Post aktualisieren")
|
||||
p.add_argument("id", type=int, help="Post-ID")
|
||||
|
||||
Reference in new issue
Block a user