j
This commit is contained in:
171
cmdnat/config.py
171
cmdnat/config.py
@@ -1,7 +1,9 @@
|
||||
from typing import List, Dict, Any
|
||||
from typing import List, Dict, Any, Optional, Sequence
|
||||
|
||||
from cmdlet._shared import Cmdlet, CmdletArg
|
||||
from SYS.config import load_config, save_config
|
||||
from SYS import pipeline as ctx
|
||||
from SYS.result_table import ResultTable
|
||||
|
||||
CMDLET = Cmdlet(
|
||||
name=".config",
|
||||
@@ -22,28 +24,21 @@ CMDLET = Cmdlet(
|
||||
)
|
||||
|
||||
|
||||
def flatten_config(config: Dict[str,
|
||||
Any],
|
||||
parent_key: str = "",
|
||||
sep: str = ".") -> List[Dict[str,
|
||||
Any]]:
|
||||
items = []
|
||||
def flatten_config(config: Dict[str, Any], parent_key: str = "", sep: str = ".") -> List[Dict[str, Any]]:
|
||||
items: List[Dict[str, Any]] = []
|
||||
for k, v in config.items():
|
||||
if k.startswith("_"): # Skip internal keys
|
||||
if k.startswith("_"):
|
||||
continue
|
||||
|
||||
new_key = f"{parent_key}{sep}{k}" if parent_key else k
|
||||
if isinstance(v, dict):
|
||||
items.extend(flatten_config(v, new_key, sep=sep))
|
||||
else:
|
||||
items.append(
|
||||
{
|
||||
"Key": new_key,
|
||||
"Value": str(v),
|
||||
"Type": type(v).__name__,
|
||||
"_selection_args": [new_key],
|
||||
}
|
||||
)
|
||||
items.append({
|
||||
"key": new_key,
|
||||
"value": v,
|
||||
"value_display": str(v),
|
||||
"type": type(v).__name__,
|
||||
})
|
||||
return items
|
||||
|
||||
|
||||
@@ -98,53 +93,133 @@ def set_nested_config(config: Dict[str, Any], key: str, value: str) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def _run(piped_result: Any, args: List[str], config: Dict[str, Any]) -> int:
|
||||
# Reload config to ensure we have the latest on disk
|
||||
# We don't use the passed 'config' because we want to edit the file
|
||||
# and 'config' might contain runtime objects (like worker manager)
|
||||
# But load_config() returns a fresh dict from disk (or cache)
|
||||
# We should use load_config()
|
||||
def _extract_piped_value(result: Any) -> Optional[str]:
|
||||
if isinstance(result, str):
|
||||
return result.strip() if result.strip() else None
|
||||
if isinstance(result, (int, float)):
|
||||
return str(result)
|
||||
if isinstance(result, dict):
|
||||
val = result.get("value")
|
||||
if val is not None:
|
||||
return str(val).strip()
|
||||
return None
|
||||
|
||||
|
||||
def _extract_value_arg(args: Sequence[str]) -> Optional[str]:
|
||||
if not args:
|
||||
return None
|
||||
tokens = [str(tok) for tok in args if tok is not None]
|
||||
flags = {"-value", "--value", "-set-value", "--set-value"}
|
||||
for idx, tok in enumerate(tokens):
|
||||
text = tok.strip()
|
||||
if not text:
|
||||
continue
|
||||
low = text.lower()
|
||||
if low in flags and idx + 1 < len(tokens):
|
||||
candidate = str(tokens[idx + 1]).strip()
|
||||
if candidate:
|
||||
return candidate
|
||||
if "=" in low:
|
||||
head, val = low.split("=", 1)
|
||||
if head in flags and val:
|
||||
return val.strip()
|
||||
for tok in tokens:
|
||||
text = str(tok).strip()
|
||||
if text and not text.startswith("-"):
|
||||
return text
|
||||
return None
|
||||
|
||||
|
||||
def _get_selected_config_key() -> Optional[str]:
|
||||
try:
|
||||
indices = ctx.get_last_selection() or []
|
||||
except Exception:
|
||||
indices = []
|
||||
try:
|
||||
items = ctx.get_last_result_items() or []
|
||||
except Exception:
|
||||
items = []
|
||||
if not indices or not items:
|
||||
return None
|
||||
idx = indices[0]
|
||||
if idx < 0 or idx >= len(items):
|
||||
return None
|
||||
item = items[idx]
|
||||
if isinstance(item, dict):
|
||||
return item.get("key")
|
||||
return getattr(item, "key", None)
|
||||
|
||||
|
||||
def _show_config_table(config_data: Dict[str, Any]) -> int:
|
||||
items = flatten_config(config_data)
|
||||
if not items:
|
||||
print("No configuration entries available.")
|
||||
return 0
|
||||
items.sort(key=lambda x: x.get("key"))
|
||||
|
||||
table = ResultTable("Configuration")
|
||||
table.set_table("config")
|
||||
table.set_source_command(".config", [])
|
||||
|
||||
for item in items:
|
||||
row = table.add_row()
|
||||
row.add_column("Key", item.get("key", ""))
|
||||
row.add_column("Value", item.get("value_display", ""))
|
||||
row.add_column("Type", item.get("type", ""))
|
||||
|
||||
ctx.set_last_result_table_overlay(table, items)
|
||||
ctx.set_current_stage_table(table)
|
||||
return 0
|
||||
|
||||
|
||||
def _strip_value_quotes(value: str) -> str:
|
||||
if not value:
|
||||
return value
|
||||
if (value.startswith('"') and value.endswith('"')) or (value.startswith("'") and value.endswith("'")):
|
||||
return value[1:-1]
|
||||
return value
|
||||
|
||||
|
||||
def _run(piped_result: Any, args: List[str], config: Dict[str, Any]) -> int:
|
||||
current_config = load_config()
|
||||
|
||||
# Parse args
|
||||
# We handle args manually because of the potential for spaces in values
|
||||
# and the @ expansion logic in CLI.py passing args
|
||||
selection_key = _get_selected_config_key()
|
||||
value_from_args = _extract_value_arg(args) if selection_key else None
|
||||
value_from_pipe = _extract_piped_value(piped_result)
|
||||
|
||||
if selection_key:
|
||||
new_value = value_from_pipe or value_from_args
|
||||
if not new_value:
|
||||
print(
|
||||
"Provide a new value via pipe or argument: @N | .config <value>"
|
||||
)
|
||||
return 1
|
||||
new_value = _strip_value_quotes(new_value)
|
||||
try:
|
||||
set_nested_config(current_config, selection_key, new_value)
|
||||
save_config(current_config)
|
||||
print(f"Updated '{selection_key}' to '{new_value}'")
|
||||
return 0
|
||||
except Exception as exc:
|
||||
print(f"Error updating config: {exc}")
|
||||
return 1
|
||||
|
||||
if not args:
|
||||
# List mode
|
||||
items = flatten_config(current_config)
|
||||
# Sort by key
|
||||
items.sort(key=lambda x: x["Key"])
|
||||
return _show_config_table(current_config)
|
||||
|
||||
# Emit items for ResultTable
|
||||
from SYS import pipeline as ctx
|
||||
|
||||
for item in items:
|
||||
ctx.emit(item)
|
||||
return 0
|
||||
|
||||
# Update mode
|
||||
key = args[0]
|
||||
|
||||
if len(args) < 2:
|
||||
print(f"Error: Value required for key '{key}'")
|
||||
return 1
|
||||
|
||||
value = " ".join(args[1:])
|
||||
|
||||
# Remove quotes if present
|
||||
if (value.startswith('"') and value.endswith('"')) or (value.startswith("'")
|
||||
and value.endswith("'")):
|
||||
value = value[1:-1]
|
||||
|
||||
value = _strip_value_quotes(" ".join(args[1:]))
|
||||
try:
|
||||
set_nested_config(current_config, key, value)
|
||||
save_config(current_config)
|
||||
print(f"Updated '{key}' to '{value}'")
|
||||
return 0
|
||||
except Exception as e:
|
||||
print(f"Error updating config: {e}")
|
||||
except Exception as exc:
|
||||
print(f"Error updating config: {exc}")
|
||||
return 1
|
||||
|
||||
|
||||
|
||||
514
cmdnat/matrix.py
514
cmdnat/matrix.py
@@ -15,6 +15,121 @@ from SYS import pipeline as ctx
|
||||
|
||||
_MATRIX_PENDING_ITEMS_KEY = "matrix_pending_items"
|
||||
_MATRIX_PENDING_TEXT_KEY = "matrix_pending_text"
|
||||
_MATRIX_MENU_STATE_KEY = "matrix_menu_state"
|
||||
_MATRIX_SELECTED_SETTING_KEY_KEY = "matrix_selected_setting_key"
|
||||
|
||||
|
||||
def _extract_piped_value(result: Any) -> Optional[str]:
|
||||
"""Extract the piped value from result (string, number, or dict with 'value' key)."""
|
||||
if isinstance(result, str):
|
||||
return result.strip() if result.strip() else None
|
||||
if isinstance(result, (int, float)):
|
||||
return str(result)
|
||||
if isinstance(result, dict):
|
||||
# Fallback to value field if it's a dict
|
||||
val = result.get("value")
|
||||
if val is not None:
|
||||
return str(val).strip()
|
||||
return None
|
||||
|
||||
|
||||
def _extract_value_arg(args: Sequence[str]) -> Optional[str]:
|
||||
"""Extract a fallback value from command-line args (value flag or positional)."""
|
||||
if not args:
|
||||
return None
|
||||
tokens = [str(tok) for tok in args if tok is not None]
|
||||
value_flags = {"-value", "--value", "-set-value", "--set-value"}
|
||||
for idx, tok in enumerate(tokens):
|
||||
low = tok.strip()
|
||||
if not low:
|
||||
continue
|
||||
low_lower = low.lower()
|
||||
if low_lower in value_flags and idx + 1 < len(tokens):
|
||||
candidate = str(tokens[idx + 1]).strip()
|
||||
if candidate:
|
||||
return candidate
|
||||
if "=" in low_lower:
|
||||
head, val = low_lower.split("=", 1)
|
||||
if head in value_flags and val:
|
||||
return val.strip()
|
||||
# Fallback to first non-flag token
|
||||
for tok in tokens:
|
||||
text = str(tok).strip()
|
||||
if text and not text.startswith("-"):
|
||||
return text
|
||||
return None
|
||||
|
||||
|
||||
def _extract_set_value_arg(args: Sequence[str]) -> Optional[str]:
|
||||
"""Extract the value from -set-value flag."""
|
||||
if not args:
|
||||
return None
|
||||
try:
|
||||
tokens = list(args)
|
||||
except Exception:
|
||||
return None
|
||||
for i, tok in enumerate(tokens):
|
||||
try:
|
||||
if str(tok).lower() == "-set-value" and i + 1 < len(tokens):
|
||||
return str(tokens[i + 1]).strip()
|
||||
except Exception:
|
||||
continue
|
||||
return None
|
||||
|
||||
|
||||
def _update_matrix_config(config: Dict[str, Any], key: str, value: Any) -> bool:
|
||||
"""Update a Matrix config value and write to config file.
|
||||
|
||||
Returns True if successful, False otherwise.
|
||||
"""
|
||||
try:
|
||||
from SYS.config import get_config_path
|
||||
from configparser import ConfigParser
|
||||
|
||||
if not isinstance(config, dict):
|
||||
return False
|
||||
|
||||
# Ensure provider.matrix section exists
|
||||
providers = config.get("provider", {})
|
||||
if not isinstance(providers, dict):
|
||||
providers = {}
|
||||
config["provider"] = providers
|
||||
|
||||
matrix_conf = providers.get("matrix", {})
|
||||
if not isinstance(matrix_conf, dict):
|
||||
matrix_conf = {}
|
||||
providers["matrix"] = matrix_conf
|
||||
|
||||
# Update the in-memory config
|
||||
matrix_conf[key] = value
|
||||
|
||||
# Try to write to config file using configparser
|
||||
try:
|
||||
config_path = get_config_path()
|
||||
if not config_path:
|
||||
return False
|
||||
|
||||
parser = ConfigParser()
|
||||
if Path(config_path).exists():
|
||||
parser.read(config_path)
|
||||
|
||||
section_name = "provider=matrix"
|
||||
if not parser.has_section(section_name):
|
||||
parser.add_section(section_name)
|
||||
|
||||
parser.set(section_name, key, str(value))
|
||||
|
||||
with open(config_path, "w") as f:
|
||||
parser.write(f)
|
||||
|
||||
return True
|
||||
except Exception as exc:
|
||||
debug(f"[matrix] Failed to write config file: {exc}")
|
||||
# Config was updated in memory at least
|
||||
return True
|
||||
except Exception as exc:
|
||||
debug(f"[matrix] Failed to update Matrix config: {exc}")
|
||||
return False
|
||||
|
||||
|
||||
def _has_flag(args: Sequence[str], flag: str) -> bool:
|
||||
@@ -466,9 +581,282 @@ def _resolve_upload_path(item: Any, config: Dict[str, Any]) -> Optional[str]:
|
||||
return None
|
||||
|
||||
|
||||
def _show_main_menu() -> int:
|
||||
"""Display main menu: Rooms or Settings."""
|
||||
table = ResultTable("Matrix (select with @N)")
|
||||
table.set_table("matrix")
|
||||
table.set_source_command(".matrix", [])
|
||||
|
||||
menu_items = [
|
||||
{
|
||||
"title": "Rooms",
|
||||
"description": "List and select rooms for uploads",
|
||||
"action": "rooms",
|
||||
},
|
||||
{
|
||||
"title": "Settings",
|
||||
"description": "View and modify Matrix configuration",
|
||||
"action": "settings",
|
||||
},
|
||||
]
|
||||
|
||||
for item in menu_items:
|
||||
row = table.add_row()
|
||||
row.add_column("Action", item["title"])
|
||||
row.add_column("Description", item["description"])
|
||||
|
||||
ctx.set_last_result_table_overlay(table, menu_items)
|
||||
ctx.set_current_stage_table(table)
|
||||
ctx.set_pending_pipeline_tail([[".matrix", "-menu-select"]], ".matrix")
|
||||
return 0
|
||||
|
||||
|
||||
def _show_settings_table(config: Dict[str, Any]) -> int:
|
||||
"""Display Matrix configuration settings as a modifiable table."""
|
||||
table = ResultTable("Matrix Settings (select with @N to modify)")
|
||||
table.set_table("matrix")
|
||||
table.set_source_command(".matrix", ["-settings"])
|
||||
|
||||
matrix_conf = {}
|
||||
try:
|
||||
if isinstance(config, dict):
|
||||
providers = config.get("provider")
|
||||
if isinstance(providers, dict):
|
||||
matrix_conf = providers.get("matrix") or {}
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
settings_items = []
|
||||
if isinstance(matrix_conf, dict):
|
||||
for key in sorted(matrix_conf.keys()):
|
||||
value = matrix_conf[key]
|
||||
# Skip sensitive/complex values
|
||||
if key in ("password",):
|
||||
value = "***"
|
||||
settings_items.append({
|
||||
"key": key,
|
||||
"value": str(value),
|
||||
"original_value": value,
|
||||
})
|
||||
|
||||
if not settings_items:
|
||||
log("No Matrix settings configured. Edit config.conf manually.", file=sys.stderr)
|
||||
return 0
|
||||
|
||||
for item in settings_items:
|
||||
row = table.add_row()
|
||||
row.add_column("Key", item["key"])
|
||||
row.add_column("Value", item["value"])
|
||||
|
||||
ctx.set_last_result_table_overlay(table, settings_items)
|
||||
ctx.set_current_stage_table(table)
|
||||
ctx.set_pending_pipeline_tail([[".matrix", "-settings-edit"]], ".matrix")
|
||||
return 0
|
||||
|
||||
|
||||
def _handle_menu_selection(selected: Any, config: Dict[str, Any]) -> int:
|
||||
"""Handle main menu selection (rooms or settings)."""
|
||||
items = _normalize_to_list(selected)
|
||||
if not items:
|
||||
return 1
|
||||
|
||||
item = items[0] # Only consider first selection
|
||||
action = None
|
||||
if isinstance(item, dict):
|
||||
action = item.get("action")
|
||||
else:
|
||||
action = getattr(item, "action", None)
|
||||
|
||||
if action == "settings":
|
||||
return _show_settings_table(config)
|
||||
else:
|
||||
return _show_rooms_table(config)
|
||||
|
||||
|
||||
def _handle_settings_edit(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
|
||||
"""Handle settings modification from selected rows.
|
||||
|
||||
Two-stage flow:
|
||||
1. User selects @4 from settings table (@4 stores selection index in context)
|
||||
2. User pipes value (@4 | "30" → result becomes "30")
|
||||
3. Handler uses both:
|
||||
- get_last_selection() → get the row index
|
||||
- result → the piped value
|
||||
- Retrieve the setting key from stored items
|
||||
- Update config
|
||||
|
||||
Usage: @4 | "30"
|
||||
"""
|
||||
# Get the last selected indices (@4 would give [3])
|
||||
selection_indices = []
|
||||
try:
|
||||
selection_indices = ctx.get_last_selection() or []
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Get the last result items (the settings table items)
|
||||
last_items = []
|
||||
try:
|
||||
last_items = ctx.get_last_result_items() or []
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if not selection_indices or not last_items:
|
||||
log("No setting selected. Use @N to select a setting first.", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
# Get the selected settings item
|
||||
idx = selection_indices[0]
|
||||
if idx < 0 or idx >= len(last_items):
|
||||
log("Invalid selection", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
selected_item = last_items[idx]
|
||||
key = None
|
||||
if isinstance(selected_item, dict):
|
||||
key = selected_item.get("key")
|
||||
else:
|
||||
key = getattr(selected_item, "key", None)
|
||||
|
||||
if not key:
|
||||
log("Invalid settings selection", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
# Prevent modifying sensitive settings
|
||||
if key in ("password", "access_token"):
|
||||
log(f"Cannot modify sensitive setting: {key}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
# Extract the piped value or fallback to CLI args
|
||||
new_value = _extract_piped_value(result) or _extract_value_arg(args)
|
||||
if new_value is None:
|
||||
log(f"To modify '{key}', pipe a literal value (e.g. @N | '30') or pass it as an arg: .matrix -settings-edit 30", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
# Update the config with the new value
|
||||
if _update_matrix_config(config, str(key), new_value):
|
||||
log(f"✓ Updated {key} = {new_value}")
|
||||
return 0
|
||||
else:
|
||||
log(f"✗ Failed to update {key}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
|
||||
def _show_rooms_table(config: Dict[str, Any]) -> int:
|
||||
"""Display rooms (refactored original behavior)."""
|
||||
from Provider.matrix import Matrix
|
||||
|
||||
try:
|
||||
provider = Matrix(config)
|
||||
except Exception as exc:
|
||||
log(f"Matrix not available: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
try:
|
||||
configured_ids = None
|
||||
# Use `-all` flag to override room filter
|
||||
all_rooms_flag = False
|
||||
if hasattr(ctx, "get_last_args"):
|
||||
try:
|
||||
last_args = ctx.get_last_args() or []
|
||||
all_rooms_flag = any(str(a).lower() == "-all" for a in last_args)
|
||||
except Exception:
|
||||
all_rooms_flag = False
|
||||
|
||||
if not all_rooms_flag:
|
||||
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
|
||||
if not rooms and not all_rooms_flag:
|
||||
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:
|
||||
if _parse_config_room_filter_ids(config) and not all_rooms_flag:
|
||||
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)")
|
||||
table.set_table("matrix")
|
||||
table.set_source_command(".matrix", [])
|
||||
|
||||
for room in rooms:
|
||||
row = table.add_row()
|
||||
name = str(room.get("name") or "").strip() if isinstance(room, dict) else ""
|
||||
room_id = str(room.get("room_id") or ""
|
||||
).strip() if isinstance(room,
|
||||
dict) else ""
|
||||
row.add_column("Name", name)
|
||||
row.add_column("Room", room_id)
|
||||
|
||||
# Make selection results clearer
|
||||
room_items: List[Dict[str, Any]] = []
|
||||
for room in rooms:
|
||||
if not isinstance(room, dict):
|
||||
continue
|
||||
room_id = str(room.get("room_id") or "").strip()
|
||||
name = str(room.get("name") or "").strip()
|
||||
room_items.append(
|
||||
{
|
||||
**room,
|
||||
"store": "matrix",
|
||||
"title": name or room_id or "Matrix Room",
|
||||
}
|
||||
)
|
||||
|
||||
ctx.set_last_result_table_overlay(table, room_items)
|
||||
ctx.set_current_stage_table(table)
|
||||
ctx.set_pending_pipeline_tail([[".matrix", "-send"]], ".matrix")
|
||||
return 0
|
||||
|
||||
|
||||
def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
|
||||
"""Main Matrix cmdlet execution.
|
||||
|
||||
Flow:
|
||||
1. First call: Show main menu (Rooms or Settings)
|
||||
2. User selects @1 (rooms) or @2 (settings)
|
||||
3. -menu-select: Route to appropriate handler
|
||||
4. -send: Send files to selected room(s) (when uploading)
|
||||
5. -settings-edit: Handle settings modification
|
||||
"""
|
||||
# Handle menu selection routing
|
||||
if _has_flag(args, "-menu-select"):
|
||||
return _handle_menu_selection(result, config)
|
||||
|
||||
# Handle settings view/edit
|
||||
if _has_flag(args, "-settings"):
|
||||
return _show_settings_table(config)
|
||||
|
||||
if _has_flag(args, "-settings-edit"):
|
||||
return _handle_settings_edit(result, args, config)
|
||||
|
||||
# Internal stage: send previously selected items to selected rooms.
|
||||
if any(str(a).lower() == "-send" for a in (args or [])):
|
||||
if _has_flag(args, "-send"):
|
||||
# Ensure we don't re-print the rooms picker table on the send stage.
|
||||
try:
|
||||
if hasattr(ctx, "set_last_result_table_overlay"):
|
||||
@@ -596,109 +984,27 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
|
||||
pass
|
||||
return 1 if any_failed else 0
|
||||
|
||||
# Default stage: show rooms, then wait for @N selection to resume sending.
|
||||
# Default stage: handle piped vs non-piped behavior
|
||||
selected_items = _normalize_to_list(result)
|
||||
if not selected_items:
|
||||
log(
|
||||
"Usage: @N | .matrix (select items first, then pick a room)",
|
||||
file=sys.stderr
|
||||
)
|
||||
return 1
|
||||
|
||||
ctx.store_value(_MATRIX_PENDING_ITEMS_KEY, selected_items)
|
||||
try:
|
||||
ctx.store_value(_MATRIX_PENDING_TEXT_KEY, _extract_text_arg(args))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
from Provider.matrix import Matrix
|
||||
|
||||
try:
|
||||
provider = Matrix(config)
|
||||
except Exception as exc:
|
||||
log(f"Matrix not available: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
try:
|
||||
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:
|
||||
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)")
|
||||
table.set_table("matrix")
|
||||
table.set_source_command(".matrix", [])
|
||||
|
||||
for room in rooms:
|
||||
row = table.add_row()
|
||||
name = str(room.get("name") or "").strip() if isinstance(room, dict) else ""
|
||||
room_id = str(room.get("room_id") or ""
|
||||
).strip() if isinstance(room,
|
||||
dict) else ""
|
||||
row.add_column("Name", name)
|
||||
row.add_column("Room", room_id)
|
||||
|
||||
# Make selection results clearer: stash a friendly title/store on the backing items.
|
||||
# This avoids confusion when the selection handler prints PipeObject debug info.
|
||||
room_items: List[Dict[str, Any]] = []
|
||||
for room in rooms:
|
||||
if not isinstance(room, dict):
|
||||
continue
|
||||
room_id = str(room.get("room_id") or "").strip()
|
||||
name = str(room.get("name") or "").strip()
|
||||
room_items.append(
|
||||
{
|
||||
**room,
|
||||
"store": "matrix",
|
||||
"title": name or room_id or "Matrix Room",
|
||||
}
|
||||
)
|
||||
|
||||
# Overlay table: user selects @N, then we resume with `.matrix -send`.
|
||||
ctx.set_last_result_table_overlay(table, room_items)
|
||||
ctx.set_current_stage_table(table)
|
||||
ctx.set_pending_pipeline_tail([[".matrix", "-send"]], ".matrix")
|
||||
return 0
|
||||
|
||||
# When piped (result has data), show rooms directly.
|
||||
# When not piped (first command), show main menu.
|
||||
if selected_items:
|
||||
return _show_rooms_table(config)
|
||||
else:
|
||||
return _show_main_menu()
|
||||
|
||||
|
||||
CMDLET = Cmdlet(
|
||||
name=".matrix",
|
||||
alias=["matrix",
|
||||
"rooms"],
|
||||
summary="Send selected items to a Matrix room",
|
||||
summary="Send selected items to a Matrix room or manage settings",
|
||||
usage="@N | .matrix",
|
||||
arg=[
|
||||
CmdletArg(
|
||||
@@ -707,6 +1013,30 @@ CMDLET = Cmdlet(
|
||||
description="(internal) Send to selected room(s)",
|
||||
required=False,
|
||||
),
|
||||
CmdletArg(
|
||||
name="menu-select",
|
||||
type="bool",
|
||||
description="(internal) Handle menu selection (rooms/settings)",
|
||||
required=False,
|
||||
),
|
||||
CmdletArg(
|
||||
name="settings",
|
||||
type="bool",
|
||||
description="(internal) Show settings table",
|
||||
required=False,
|
||||
),
|
||||
CmdletArg(
|
||||
name="settings-edit",
|
||||
type="bool",
|
||||
description="(internal) Handle settings modification",
|
||||
required=False,
|
||||
),
|
||||
CmdletArg(
|
||||
name="set-value",
|
||||
type="string",
|
||||
description="New value for selected setting (use with -settings-edit)",
|
||||
required=False,
|
||||
),
|
||||
CmdletArg(
|
||||
name="all",
|
||||
type="bool",
|
||||
|
||||
Reference in New Issue
Block a user