kh
Some checks failed
smoke-mm / Install & smoke test mm --help (push) Has been cancelled

This commit is contained in:
2025-12-26 18:58:48 -08:00
parent 436089ca0c
commit 9310478a37
8 changed files with 1242 additions and 242 deletions

View File

@@ -18,6 +18,108 @@ _MATRIX_PENDING_ITEMS_KEY = "matrix_pending_items"
_MATRIX_PENDING_TEXT_KEY = "matrix_pending_text"
def _has_flag(args: Sequence[str], flag: str) -> bool:
try:
want = str(flag or "").strip().lower()
if not want:
return False
return any(str(a).strip().lower() == want for a in (args or []))
except Exception:
return False
def _parse_config_room_filter_ids(config: Dict[str, Any]) -> List[str]:
try:
if not isinstance(config, dict):
return []
providers = config.get("provider")
if not isinstance(providers, dict):
return []
matrix_conf = providers.get("matrix")
if not isinstance(matrix_conf, dict):
return []
raw = None
# Support a few common spellings; `room` is the documented key.
for key in ("room", "room_id", "rooms", "room_ids"):
if key in matrix_conf:
raw = matrix_conf.get(key)
break
if raw is None:
return []
# Allow either a string or a list-like value.
if isinstance(raw, (list, tuple, set)):
items = [str(v).strip() for v in raw if str(v).strip()]
return items
text = str(raw or "").strip()
if not text:
return []
# Comma-separated list of room IDs, but be tolerant of whitespace/newlines.
items = [p.strip() for p in re.split(r"[,\s]+", text) if p and p.strip()]
return items
except Exception:
return []
def _get_matrix_size_limit_bytes(config: Dict[str, Any]) -> Optional[int]:
"""Return max allowed per-file size in bytes for Matrix uploads.
Config: [provider=Matrix] size_limit=50 # MB
"""
try:
if not isinstance(config, dict):
return None
providers = config.get("provider")
if not isinstance(providers, dict):
return None
matrix_conf = providers.get("matrix")
if not isinstance(matrix_conf, dict):
return None
raw = None
for key in ("size_limit", "size_limit_mb", "max_mb"):
if key in matrix_conf:
raw = matrix_conf.get(key)
break
if raw is None:
return None
mb: Optional[float] = None
if isinstance(raw, (int, float)):
mb = float(raw)
else:
text = str(raw or "").strip().lower()
if not text:
return None
m = re.fullmatch(r"(\d+(?:\.\d+)?)\s*(mb|mib|m)?", text)
if not m:
return None
mb = float(m.group(1))
if mb is None or mb <= 0:
return None
# Use MiB semantics for predictable limits.
return int(mb * 1024 * 1024)
except Exception:
return None
def _room_id_matches_filter(room_id: str, allowed_ids_canon: set[str]) -> bool:
rid = str(room_id or "").strip()
if not rid or not allowed_ids_canon:
return False
rid_canon = rid.casefold()
if rid_canon in allowed_ids_canon:
return True
# Allow matching when config omits the homeserver part: "!abc" matches "!abc:server".
base = rid.split(":", 1)[0].strip().casefold()
return bool(base) and base in allowed_ids_canon
def _extract_text_arg(args: Sequence[str]) -> str:
"""Extract a `-text <value>` argument from a cmdnat args list."""
if not args:
@@ -378,17 +480,50 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
except Exception:
text_value = ""
size_limit_bytes = _get_matrix_size_limit_bytes(config)
size_limit_mb = (size_limit_bytes / (1024 * 1024)) if size_limit_bytes else None
# Resolve upload paths once (also avoids repeated downloads when sending to multiple rooms).
upload_jobs: List[Dict[str, Any]] = []
any_failed = False
for item in items:
file_path = _resolve_upload_path(item, config)
if not file_path:
any_failed = True
log("Matrix upload requires a local file (path) or a direct URL on the selected item", file=sys.stderr)
continue
media_path = Path(file_path)
if not media_path.exists():
any_failed = True
log(f"Matrix upload file missing: {file_path}", file=sys.stderr)
continue
if size_limit_bytes is not None:
try:
byte_size = int(media_path.stat().st_size)
except Exception:
byte_size = -1
if byte_size >= 0 and byte_size > size_limit_bytes:
any_failed = True
actual_mb = byte_size / (1024 * 1024)
lim = float(size_limit_mb or 0)
log(
f"ERROR: file is too big, skipping: {media_path.name} ({actual_mb:.1f} MB > {lim:.1f} MB)",
file=sys.stderr,
)
continue
upload_jobs.append({"path": str(media_path), "pipe_obj": item})
for rid in room_ids:
sent_any_for_room = False
for item in items:
file_path = _resolve_upload_path(item, config)
for job in upload_jobs:
file_path = str(job.get("path") or "")
if not file_path:
any_failed = True
log("Matrix upload requires a local file (path) or a direct URL on the selected item", file=sys.stderr)
continue
try:
link = provider.upload_to_room(file_path, rid, pipe_obj=item)
link = provider.upload_to_room(file_path, rid, pipe_obj=job.get("pipe_obj"))
debug(f"✓ Sent {Path(file_path).name} -> {rid}")
if link:
log(link)
@@ -433,13 +568,33 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
return 1
try:
rooms = provider.list_rooms()
configured_ids = None
if not _has_flag(args, "-all"):
ids = [str(v).strip() for v in _parse_config_room_filter_ids(config) if str(v).strip()]
if ids:
configured_ids = ids
rooms = provider.list_rooms(room_ids=configured_ids)
except Exception as exc:
log(f"Failed to list Matrix rooms: {exc}", file=sys.stderr)
return 1
# Diagnostics if a configured filter yields no rows (provider filtered before name lookups for speed).
if not rooms and not _has_flag(args, "-all"):
configured_ids_dbg = [str(v).strip() for v in _parse_config_room_filter_ids(config) if str(v).strip()]
if configured_ids_dbg:
try:
joined_ids = provider.list_joined_room_ids()
debug(f"[matrix] Configured room filter IDs: {configured_ids_dbg}")
debug(f"[matrix] Joined room IDs (from Matrix): {joined_ids}")
except Exception:
pass
if not rooms:
log("No joined rooms found.", file=sys.stderr)
if _parse_config_room_filter_ids(config) and not _has_flag(args, "-all"):
log("No joined rooms matched the configured Matrix room filter (use: .matrix -all)", file=sys.stderr)
else:
log("No joined rooms found.", file=sys.stderr)
return 0
table = ResultTable("Matrix Rooms (select with @N)")
@@ -482,6 +637,7 @@ CMDLET = Cmdlet(
usage="@N | .matrix",
arg=[
CmdletArg(name="send", type="bool", description="(internal) Send to selected room(s)", required=False),
CmdletArg(name="all", type="bool", description="Ignore config room filter and show all joined rooms", required=False),
CmdletArg(name="text", type="string", description="Send a follow-up text message after each upload (caption-like)", required=False),
],
exec=_run