This commit is contained in:
2026-01-30 16:24:08 -08:00
parent e57dcf2190
commit 20045b8de8
5 changed files with 989 additions and 79 deletions

View File

@@ -223,6 +223,191 @@ def _room_id_matches_filter(room_id: str, allowed_ids_canon: set[str]) -> bool:
return bool(base) and base in allowed_ids_canon
def _extract_room_arg(args: Sequence[str]) -> Optional[str]:
"""Extract the `-room <value>` argument from a cmdnat args list."""
if not args:
return None
try:
tokens = list(args)
except Exception:
return None
for i, tok in enumerate(tokens):
try:
low = str(tok or "").strip()
if not low:
continue
low_lower = low.lower()
if low_lower in ("-room", "--room") and i + 1 < len(tokens):
return str(tokens[i + 1] or "").strip()
if low_lower.startswith("-room=") or low_lower.startswith("--room="):
parts = low.split("=", 1)
if len(parts) > 1:
return parts[1].strip()
except Exception:
continue
return None
def _resolve_room_identifier(value: str, config: Dict[str, Any]) -> Optional[str]:
"""Resolve a user-provided room identifier (display name or id) to a Matrix room_id.
Returns the canonical room_id string on success, otherwise None.
"""
try:
cand = str(value or "").strip()
if not cand:
return None
# If looks like an id already (starts with '!'), accept it as-is
if cand.startswith("!"):
return cand
# First try to resolve against configured default rooms (fast, local)
conf_ids = _parse_config_room_filter_ids(config)
if conf_ids:
# Attempt to fetch names for the configured IDs
try:
from Provider.matrix import Matrix
# Avoid __init__ network failures by requiring homeserver+token to exist
block = config.get("provider", {}).get("matrix", {})
if block and block.get("homeserver") and block.get("access_token"):
try:
m = Matrix(config)
rooms = m.list_rooms(room_ids=conf_ids)
for room in rooms or []:
name = str(room.get("name") or "").strip()
rid = str(room.get("room_id") or "").strip()
if name and name.lower() == cand.lower():
return rid
if name and cand.lower() in name.lower():
return rid
except Exception:
# Best-effort; fallback below
pass
except Exception:
pass
# Last resort: attempt to ask the server for matching rooms (if possible)
try:
from Provider.matrix import Matrix
block = config.get("provider", {}).get("matrix", {})
if block and block.get("homeserver") and block.get("access_token"):
try:
m = Matrix(config)
rooms = m.list_rooms()
for room in rooms or []:
name = str(room.get("name") or "").strip()
rid = str(room.get("room_id") or "").strip()
if name and name.lower() == cand.lower():
return rid
if name and cand.lower() in name.lower():
return rid
except Exception:
pass
except Exception:
pass
return None
except Exception:
return None
def _send_pending_to_rooms(config: Dict[str, Any], room_ids: List[str], args: Sequence[str]) -> int:
"""Send currently pending items to the specified list of room_ids.
This function mirrors the existing -send behavior but accepts explicit
room_ids so the same logic can be reused for -room direct invocation.
"""
pending_items = ctx.load_value(_MATRIX_PENDING_ITEMS_KEY, default=[])
items = _normalize_to_list(pending_items)
if not items:
log("No pending items to upload (use: @N | .matrix)", file=sys.stderr)
return 1
from Provider.matrix import Matrix
try:
provider = Matrix(config)
except Exception as exc:
log(f"Matrix not available: {exc}", file=sys.stderr)
return 1
text_value = _extract_text_arg(args)
if not text_value:
try:
text_value = str(ctx.load_value(_MATRIX_PENDING_TEXT_KEY, default="") or "").strip()
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
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 job in upload_jobs:
file_path = str(job.get("path") or "")
if not file_path:
continue
try:
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)
sent_any_for_room = True
except Exception as exc:
any_failed = True
log(f"Matrix send failed for {Path(file_path).name}: {exc}", file=sys.stderr)
if text_value and sent_any_for_room:
try:
provider.send_text_to_room(text_value, rid)
except Exception as exc:
any_failed = True
log(f"Matrix text send failed: {exc}", file=sys.stderr)
# Clear pending items once attempted
ctx.store_value(_MATRIX_PENDING_ITEMS_KEY, [])
try:
ctx.store_value(_MATRIX_PENDING_TEXT_KEY, "")
except Exception:
pass
return 1 if any_failed else 0
def _extract_text_arg(args: Sequence[str]) -> str:
"""Extract a `-text <value>` argument from a cmdnat args list."""
if not args:
@@ -1128,9 +1313,31 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
except Exception:
pass
# When piped (result has data), show rooms directly.
# When not piped (first command), show main menu.
# When piped (result has data), either send directly (if -room given)
# or show rooms for interactive selection.
if selected_items:
# If user provided a -room argument, resolve it and send to the target(s)
room_arg = _extract_room_arg(args)
if room_arg:
# Support comma-separated list of room names/ids
requested = [r.strip() for r in room_arg.split(",") if r.strip()]
resolved_ids: List[str] = []
for req in requested:
try:
rid = _resolve_room_identifier(req, config)
if rid:
resolved_ids.append(rid)
except Exception:
# Ignore unresolved entries
continue
if not resolved_ids:
log("Could not resolve specified room(s). Opening room selection table instead.", file=sys.stderr)
return _show_rooms_table(config)
# Proceed to send the pending items directly
return _send_pending_to_rooms(config, resolved_ids, args)
return _show_rooms_table(config)
else:
return _show_main_menu()
@@ -1185,6 +1392,12 @@ CMDLET = Cmdlet(
description="Ignore config room filter and show all joined rooms",
required=False,
),
CmdletArg(
name="room",
type="string",
description="Target room (name or id). Comma-separated values supported. Autocomplete uses configured defaults.",
required=False,
),
CmdletArg(
name="text",
type="string",