Neues Plugin, das MediaWiki-Seiten/Templates ueber die gevendorte, self- contained wiki.py (uv run --script; mwclient+python-dotenv) liest, rendert und schreibt. Instanzen wiki (Prod) und devwiki via -w. Zugangsdaten bleiben lokal (Umgebungsvariablen bzw. dotenv-Datei) - Beispiel-Vorlage beiliegend. SKILL.md dokumentiert Nutzung + Regel "Wiki nur via wiki.py/MCP, nicht Maintenance-Skript". Marketplace-Eintrag ergaenzt. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
182 lines
5.8 KiB
Python
Executable File
182 lines
5.8 KiB
Python
Executable File
#!/usr/bin/env -S uv run --script
|
|
# /// script
|
|
# requires-python = ">=3.11"
|
|
# dependencies = [
|
|
# "mwclient",
|
|
# "python-dotenv",
|
|
# ]
|
|
# ///
|
|
"""CLI for fetching and updating MediaWiki pages."""
|
|
|
|
import argparse
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
from urllib.parse import urlparse
|
|
|
|
from dotenv import load_dotenv
|
|
import mwclient
|
|
|
|
|
|
WIKI_ALIASES = {
|
|
"wiki": ("MEDIAWIKI_URL", "https://wiki.inmedias.it"),
|
|
"devwiki": ("DEVMEDIAWIKI_URL", "https://devwiki.inmedias.it"),
|
|
}
|
|
|
|
|
|
def get_site(wiki: str = "wiki") -> mwclient.Site:
|
|
load_dotenv(Path.home() / ".config/dokumentierer/.env")
|
|
load_dotenv() # current dir, doesn't override already-set vars
|
|
|
|
if wiki not in WIKI_ALIASES:
|
|
sys.exit(f"Error: unknown wiki '{wiki}'. Known: {', '.join(WIKI_ALIASES)}")
|
|
|
|
url_env, url_default = WIKI_ALIASES[wiki]
|
|
url = os.environ.get(url_env, url_default).rstrip("/")
|
|
|
|
prefix = "DEVMEDIAWIKI_" if wiki == "devwiki" else "MEDIAWIKI_"
|
|
user = os.environ.get(f"{prefix}USER") or os.environ.get("MEDIAWIKI_USER", "")
|
|
password = os.environ.get(f"{prefix}PASSWORD") or os.environ.get("MEDIAWIKI_PASSWORD", "")
|
|
|
|
if not user:
|
|
sys.exit(f"Error: MEDIAWIKI_USER is not set in .env")
|
|
if not password:
|
|
sys.exit(f"Error: MEDIAWIKI_PASSWORD is not set in .env")
|
|
|
|
parsed = urlparse(url)
|
|
host = parsed.netloc
|
|
# mwclient expects path to end with '/'
|
|
path = (parsed.path or "/") + "/"
|
|
path = path.replace("//", "/")
|
|
scheme = parsed.scheme or "https"
|
|
|
|
site = mwclient.Site(host, path=path, scheme=scheme)
|
|
site.login(user, password)
|
|
return site
|
|
|
|
|
|
def cmd_fetch(args: argparse.Namespace) -> None:
|
|
site = get_site(args.wiki)
|
|
page = site.pages[args.title]
|
|
if not page.exists:
|
|
sys.exit(f"Error: page '{args.title}' does not exist")
|
|
print(page.text(), end="")
|
|
|
|
|
|
NAMESPACES = {
|
|
"main": 0, "talk": 1, "user": 2, "project": 4,
|
|
"file": 6, "template": 10, "category": 14,
|
|
}
|
|
|
|
|
|
def _strip_html(text: str) -> str:
|
|
"""Remove HTML tags (used to clean search snippets)."""
|
|
import re
|
|
return re.sub(r"<[^>]+>", "", text)
|
|
|
|
|
|
def cmd_search(args: argparse.Namespace) -> None:
|
|
site = get_site(args.wiki)
|
|
ns_key = args.namespace
|
|
if ns_key is None or ns_key.lower() == "all":
|
|
ns = "*"
|
|
elif ns_key.isdigit():
|
|
ns = int(ns_key)
|
|
else:
|
|
ns = NAMESPACES.get(ns_key.lower())
|
|
if ns is None:
|
|
sys.exit(f"Error: unknown namespace '{ns_key}'. "
|
|
f"Known: all, {', '.join(NAMESPACES)}")
|
|
|
|
result = site.api(
|
|
"query", list="search",
|
|
srsearch=args.query,
|
|
srnamespace=ns,
|
|
srlimit=50,
|
|
srprop="title|snippet",
|
|
)
|
|
hits = result["query"]["search"]
|
|
if not hits:
|
|
print("(keine Ergebnisse)", file=sys.stderr)
|
|
for hit in hits:
|
|
print(hit["title"])
|
|
if hit.get("snippet"):
|
|
print(f" {_strip_html(hit['snippet']).strip()}")
|
|
|
|
|
|
def cmd_render(args: argparse.Namespace) -> None:
|
|
site = get_site(args.wiki)
|
|
result = site.api("parse", page=args.title, prop="text")
|
|
print(result["parse"]["text"]["*"], end="")
|
|
|
|
|
|
def cmd_upload(args: argparse.Namespace) -> None:
|
|
path = Path(args.file_path)
|
|
if not path.exists():
|
|
sys.exit(f"Error: file not found: {path}")
|
|
site = get_site(args.wiki)
|
|
with path.open("rb") as fh:
|
|
site.upload(fh, filename=args.wiki_filename,
|
|
description=args.description or "", ignore=True)
|
|
print(f"Uploaded '{args.wiki_filename}'")
|
|
|
|
|
|
def cmd_update(args: argparse.Namespace) -> None:
|
|
if args.file == "-":
|
|
content = sys.stdin.read()
|
|
else:
|
|
try:
|
|
with open(args.file, "r", encoding="utf-8") as fh:
|
|
content = fh.read()
|
|
except OSError as exc:
|
|
sys.exit(f"Error reading file: {exc}")
|
|
|
|
site = get_site(args.wiki)
|
|
page = site.pages[args.title]
|
|
page.save(content, summary=args.summary or "")
|
|
print(f"Saved '{args.title}'")
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser(description="MediaWiki page fetch/update tool")
|
|
parser.add_argument(
|
|
"--wiki", "-w",
|
|
choices=list(WIKI_ALIASES),
|
|
default="wiki",
|
|
help="Wiki-Instanz (Standard: wiki = wiki.inmedias.it)",
|
|
)
|
|
sub = parser.add_subparsers(dest="command", required=True)
|
|
|
|
fetch_p = sub.add_parser("fetch", help="Print page wikitext to stdout")
|
|
fetch_p.add_argument("title", help="Page title")
|
|
fetch_p.set_defaults(func=cmd_fetch)
|
|
|
|
search_p = sub.add_parser("search", help="Search for pages by title")
|
|
search_p.add_argument("query", help="Search term")
|
|
search_p.add_argument("--namespace", "-n", default=None,
|
|
help="Namespace filter: all (default), main, template, category, file, ... or numeric ID")
|
|
search_p.set_defaults(func=cmd_search)
|
|
|
|
render_p = sub.add_parser("render", help="Print rendered HTML of a page to stdout")
|
|
render_p.add_argument("title", help="Page title")
|
|
render_p.set_defaults(func=cmd_render)
|
|
|
|
upload_p = sub.add_parser("upload", help="Upload a local file to the wiki file store")
|
|
upload_p.add_argument("file_path", help="Absolute path to the local file")
|
|
upload_p.add_argument("wiki_filename", help="Target filename on the wiki (without 'File:' prefix)")
|
|
upload_p.add_argument("--description", default="", help="Wikitext for the file description page")
|
|
upload_p.set_defaults(func=cmd_upload)
|
|
|
|
update_p = sub.add_parser("update", help="Save wikitext to a page")
|
|
update_p.add_argument("title", help="Page title")
|
|
update_p.add_argument("file", help="File containing wikitext, or '-' for stdin")
|
|
update_p.add_argument("--summary", default="", help="Edit summary")
|
|
update_p.set_defaults(func=cmd_update)
|
|
|
|
args = parser.parse_args()
|
|
args.func(args)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|