dfd
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -222,3 +222,5 @@ config.conf
|
|||||||
config.d/
|
config.d/
|
||||||
MPV/ffmpeg/*
|
MPV/ffmpeg/*
|
||||||
MPV/portable_config/*
|
MPV/portable_config/*
|
||||||
|
Log/
|
||||||
|
Log/medeia_macina/telegram.session
|
||||||
|
|||||||
16
CLI.py
16
CLI.py
@@ -1088,10 +1088,12 @@ def _create_cmdlet_cli():
|
|||||||
if isinstance(provider_cfg, dict) and provider_cfg:
|
if isinstance(provider_cfg, dict) and provider_cfg:
|
||||||
try:
|
try:
|
||||||
from ProviderCore.registry import (
|
from ProviderCore.registry import (
|
||||||
|
list_providers,
|
||||||
list_search_providers,
|
list_search_providers,
|
||||||
list_file_providers,
|
list_file_providers,
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
list_providers = None # type: ignore
|
||||||
list_search_providers = None # type: ignore
|
list_search_providers = None # type: ignore
|
||||||
list_file_providers = None # type: ignore
|
list_file_providers = None # type: ignore
|
||||||
|
|
||||||
@@ -1103,6 +1105,13 @@ def _create_cmdlet_cli():
|
|||||||
search_availability = {}
|
search_availability = {}
|
||||||
file_availability = {}
|
file_availability = {}
|
||||||
meta_availability = {}
|
meta_availability = {}
|
||||||
|
provider_availability = {}
|
||||||
|
|
||||||
|
try:
|
||||||
|
if list_providers is not None:
|
||||||
|
provider_availability = list_providers(config) or {}
|
||||||
|
except Exception:
|
||||||
|
provider_availability = {}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if list_search_providers is not None:
|
if list_search_providers is not None:
|
||||||
@@ -1192,7 +1201,12 @@ def _create_cmdlet_cli():
|
|||||||
is_known = False
|
is_known = False
|
||||||
ok = None
|
ok = None
|
||||||
|
|
||||||
if prov in search_availability:
|
# Prefer unified provider registry for availability (covers providers that
|
||||||
|
# implement download-only behavior, like Telegram).
|
||||||
|
if prov in provider_availability:
|
||||||
|
is_known = True
|
||||||
|
ok = bool(provider_availability.get(prov))
|
||||||
|
elif prov in search_availability:
|
||||||
is_known = True
|
is_known = True
|
||||||
ok = bool(search_availability.get(prov))
|
ok = bool(search_availability.get(prov))
|
||||||
elif prov in file_availability:
|
elif prov in file_availability:
|
||||||
|
|||||||
Binary file not shown.
@@ -144,7 +144,7 @@ class Matrix(Provider):
|
|||||||
out.append({"room_id": room_id, "name": name})
|
out.append({"room_id": room_id, "name": name})
|
||||||
return out
|
return out
|
||||||
|
|
||||||
def upload_to_room(self, file_path: str, room_id: str) -> str:
|
def upload_to_room(self, file_path: str, room_id: str, **kwargs: Any) -> str:
|
||||||
"""Upload a file and send it to a specific room."""
|
"""Upload a file and send it to a specific room."""
|
||||||
path = Path(file_path)
|
path = Path(file_path)
|
||||||
if not path.exists():
|
if not path.exists():
|
||||||
@@ -174,6 +174,22 @@ class Matrix(Provider):
|
|||||||
if not content_uri:
|
if not content_uri:
|
||||||
raise Exception("No content_uri returned")
|
raise Exception("No content_uri returned")
|
||||||
|
|
||||||
|
# Build a fragment-free URL suitable for storage backends.
|
||||||
|
# `matrix.to` links use fragments (`#/...`) which some backends normalize away.
|
||||||
|
download_url_for_store = ""
|
||||||
|
try:
|
||||||
|
curi = str(content_uri or "").strip()
|
||||||
|
if curi.startswith("mxc://"):
|
||||||
|
rest = curi[len("mxc://"):]
|
||||||
|
if "/" in rest:
|
||||||
|
server_name, media_id = rest.split("/", 1)
|
||||||
|
server_name = str(server_name).strip()
|
||||||
|
media_id = str(media_id).strip()
|
||||||
|
if server_name and media_id:
|
||||||
|
download_url_for_store = f"{base}/_matrix/media/v3/download/{quote(server_name, safe='')}/{quote(media_id, safe='')}"
|
||||||
|
except Exception:
|
||||||
|
download_url_for_store = ""
|
||||||
|
|
||||||
# Determine message type
|
# Determine message type
|
||||||
msgtype = "m.file"
|
msgtype = "m.file"
|
||||||
ext = path.suffix.lower()
|
ext = path.suffix.lower()
|
||||||
@@ -199,6 +215,44 @@ class Matrix(Provider):
|
|||||||
if send_resp.status_code != 200:
|
if send_resp.status_code != 200:
|
||||||
raise Exception(f"Matrix send message failed: {send_resp.text}")
|
raise Exception(f"Matrix send message failed: {send_resp.text}")
|
||||||
|
|
||||||
|
event_id = (send_resp.json() or {}).get("event_id")
|
||||||
|
link = f"https://matrix.to/#/{room_id}/{event_id}" if event_id else f"https://matrix.to/#/{room_id}"
|
||||||
|
|
||||||
|
# Optional: if a PipeObject is provided and it already has store+hash,
|
||||||
|
# attach the uploaded URL back to the stored file.
|
||||||
|
try:
|
||||||
|
pipe_obj = kwargs.get("pipe_obj")
|
||||||
|
if pipe_obj is not None:
|
||||||
|
from Store import Store
|
||||||
|
|
||||||
|
# Prefer the direct media download URL for storage backends.
|
||||||
|
Store(self.config, suppress_debug=True).try_add_url_for_pipe_object(
|
||||||
|
pipe_obj,
|
||||||
|
download_url_for_store or link,
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return link
|
||||||
|
|
||||||
|
def send_text_to_room(self, text: str, room_id: str) -> str:
|
||||||
|
"""Send a plain text message to a specific room."""
|
||||||
|
message = str(text or "").strip()
|
||||||
|
if not message:
|
||||||
|
return ""
|
||||||
|
if not room_id:
|
||||||
|
raise Exception("Matrix room_id missing")
|
||||||
|
|
||||||
|
base, token = self._get_homeserver_and_token()
|
||||||
|
encoded_room = quote(str(room_id), safe="")
|
||||||
|
txn_id = f"mm_{int(time.time())}_{uuid.uuid4().hex[:8]}"
|
||||||
|
send_url = f"{base}/_matrix/client/v3/rooms/{encoded_room}/send/m.room.message/{txn_id}"
|
||||||
|
send_headers = {"Authorization": f"Bearer {token}"}
|
||||||
|
payload = {"msgtype": "m.text", "body": message}
|
||||||
|
send_resp = requests.put(send_url, headers=send_headers, json=payload)
|
||||||
|
if send_resp.status_code != 200:
|
||||||
|
raise Exception(f"Matrix send text failed: {send_resp.text}")
|
||||||
|
|
||||||
event_id = (send_resp.json() or {}).get("event_id")
|
event_id = (send_resp.json() or {}).get("event_id")
|
||||||
return f"https://matrix.to/#/{room_id}/{event_id}" if event_id else f"https://matrix.to/#/{room_id}"
|
return f"https://matrix.to/#/{room_id}/{event_id}" if event_id else f"https://matrix.to/#/{room_id}"
|
||||||
|
|
||||||
@@ -247,9 +301,11 @@ class Matrix(Provider):
|
|||||||
try:
|
try:
|
||||||
file_path = ''
|
file_path = ''
|
||||||
delete_after = False
|
delete_after = False
|
||||||
|
pipe_obj = None
|
||||||
if isinstance(payload, dict):
|
if isinstance(payload, dict):
|
||||||
file_path = str(payload.get('path') or '')
|
file_path = str(payload.get('path') or '')
|
||||||
delete_after = bool(payload.get('delete_after', False))
|
delete_after = bool(payload.get('delete_after', False))
|
||||||
|
pipe_obj = payload.get('pipe_obj')
|
||||||
else:
|
else:
|
||||||
file_path = str(getattr(payload, 'path', '') or '')
|
file_path = str(getattr(payload, 'path', '') or '')
|
||||||
if not file_path:
|
if not file_path:
|
||||||
@@ -262,7 +318,7 @@ class Matrix(Provider):
|
|||||||
print(f"Matrix upload file missing: {file_path}")
|
print(f"Matrix upload file missing: {file_path}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
link = self.upload_to_room(str(media_path), str(room_id))
|
link = self.upload_to_room(str(media_path), str(room_id), pipe_obj=pipe_obj)
|
||||||
if link:
|
if link:
|
||||||
print(link)
|
print(link)
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, Optional, Tuple
|
from typing import Any, Dict, Optional, Tuple
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
@@ -143,6 +145,21 @@ class Telegram(Provider):
|
|||||||
session_base = self._session_base_path()
|
session_base = self._session_base_path()
|
||||||
chat, message_id = _parse_telegram_message_url(url)
|
chat, message_id = _parse_telegram_message_url(url)
|
||||||
|
|
||||||
|
def _format_bytes(num: Optional[int]) -> str:
|
||||||
|
try:
|
||||||
|
if num is None:
|
||||||
|
return "?B"
|
||||||
|
n = float(num)
|
||||||
|
suffixes = ["B", "KB", "MB", "GB", "TB"]
|
||||||
|
for s in suffixes:
|
||||||
|
if n < 1024 or s == suffixes[-1]:
|
||||||
|
if s == "B":
|
||||||
|
return f"{int(n)}{s}"
|
||||||
|
return f"{n:.1f}{s}"
|
||||||
|
n /= 1024
|
||||||
|
except Exception:
|
||||||
|
return "?B"
|
||||||
|
|
||||||
client = TelegramClient(str(session_base), app_id, api_hash)
|
client = TelegramClient(str(session_base), app_id, api_hash)
|
||||||
try:
|
try:
|
||||||
# This prompts on first run for phone/code and persists the session.
|
# This prompts on first run for phone/code and persists the session.
|
||||||
@@ -226,7 +243,35 @@ class Telegram(Provider):
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
downloaded = _resolve(client.download_media(message, file=str(output_dir)))
|
# Progress callback: prints to stderr so it doesn't interfere with pipeline stdout.
|
||||||
|
last_print = {"t": 0.0}
|
||||||
|
def _progress(current: int, total: int) -> None:
|
||||||
|
try:
|
||||||
|
now = time.monotonic()
|
||||||
|
# Throttle to avoid spamming.
|
||||||
|
if now - float(last_print.get("t", 0.0)) < 0.25 and current < total:
|
||||||
|
return
|
||||||
|
last_print["t"] = now
|
||||||
|
|
||||||
|
pct = ""
|
||||||
|
try:
|
||||||
|
if total and total > 0:
|
||||||
|
pct = f" {min(100.0, (current / total) * 100.0):5.1f}%"
|
||||||
|
except Exception:
|
||||||
|
pct = ""
|
||||||
|
|
||||||
|
line = f"[telegram] Downloading{pct} ({_format_bytes(current)}/{_format_bytes(total)})"
|
||||||
|
sys.stderr.write("\r" + line)
|
||||||
|
sys.stderr.flush()
|
||||||
|
except Exception:
|
||||||
|
return
|
||||||
|
|
||||||
|
downloaded = _resolve(client.download_media(message, file=str(output_dir), progress_callback=_progress))
|
||||||
|
try:
|
||||||
|
sys.stderr.write("\n")
|
||||||
|
sys.stderr.flush()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
if not downloaded:
|
if not downloaded:
|
||||||
raise Exception("Telegram download returned no file")
|
raise Exception("Telegram download returned no file")
|
||||||
downloaded_path = Path(str(downloaded))
|
downloaded_path = Path(str(downloaded))
|
||||||
|
|||||||
@@ -24,7 +24,17 @@ class ZeroXZero(Provider):
|
|||||||
response = client.post("https://0x0.st", files={"file": handle})
|
response = client.post("https://0x0.st", files={"file": handle})
|
||||||
|
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
return response.text.strip()
|
uploaded_url = response.text.strip()
|
||||||
|
try:
|
||||||
|
pipe_obj = kwargs.get("pipe_obj")
|
||||||
|
if pipe_obj is not None:
|
||||||
|
from Store import Store
|
||||||
|
|
||||||
|
Store(self.config, suppress_debug=True).try_add_url_for_pipe_object(pipe_obj, uploaded_url)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return uploaded_url
|
||||||
|
|
||||||
raise Exception(f"Upload failed: {response.status_code} - {response.text}")
|
raise Exception(f"Upload failed: {response.status_code} - {response.text}")
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from __future__ import annotations
|
|||||||
import importlib
|
import importlib
|
||||||
import inspect
|
import inspect
|
||||||
import pkgutil
|
import pkgutil
|
||||||
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, Iterable, Optional, Type
|
from typing import Any, Dict, Iterable, Optional, Type
|
||||||
|
|
||||||
@@ -22,6 +23,9 @@ from SYS.logger import debug
|
|||||||
from Store._base import Store as BaseStore
|
from Store._base import Store as BaseStore
|
||||||
|
|
||||||
|
|
||||||
|
_SHA256_HEX_RE = re.compile(r"^[0-9a-fA-F]{64}$")
|
||||||
|
|
||||||
|
|
||||||
# Backends that failed to initialize earlier in the current process.
|
# Backends that failed to initialize earlier in the current process.
|
||||||
# Keyed by (store_type, instance_key) where instance_key is the name used under config.store.<type>.<instance_key>.
|
# Keyed by (store_type, instance_key) where instance_key is the name used under config.store.<type>.<instance_key>.
|
||||||
_FAILED_BACKEND_CACHE: Dict[tuple[str, str], str] = {}
|
_FAILED_BACKEND_CACHE: Dict[tuple[str, str], str] = {}
|
||||||
@@ -237,3 +241,40 @@ class Store:
|
|||||||
|
|
||||||
def is_available(self, backend_name: str) -> bool:
|
def is_available(self, backend_name: str) -> bool:
|
||||||
return backend_name in self._backends
|
return backend_name in self._backends
|
||||||
|
|
||||||
|
def try_add_url_for_pipe_object(self, pipe_obj: Any, url: str) -> bool:
|
||||||
|
"""Best-effort helper: if `pipe_obj` contains `store` + `hash`, add `url` to that store backend.
|
||||||
|
|
||||||
|
Intended for providers to attach newly generated/hosted URLs back to an existing stored file.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
url_text = str(url or "").strip()
|
||||||
|
if not url_text:
|
||||||
|
return False
|
||||||
|
|
||||||
|
store_name = None
|
||||||
|
file_hash = None
|
||||||
|
if isinstance(pipe_obj, dict):
|
||||||
|
store_name = pipe_obj.get("store")
|
||||||
|
file_hash = pipe_obj.get("hash")
|
||||||
|
else:
|
||||||
|
store_name = getattr(pipe_obj, "store", None)
|
||||||
|
file_hash = getattr(pipe_obj, "hash", None)
|
||||||
|
|
||||||
|
store_name = str(store_name).strip() if store_name is not None else ""
|
||||||
|
file_hash = str(file_hash).strip() if file_hash is not None else ""
|
||||||
|
if not store_name or not file_hash:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not _SHA256_HEX_RE.fullmatch(file_hash):
|
||||||
|
return False
|
||||||
|
|
||||||
|
backend = self[store_name]
|
||||||
|
add_url = getattr(backend, "add_url", None)
|
||||||
|
if not callable(add_url):
|
||||||
|
return False
|
||||||
|
|
||||||
|
ok = add_url(file_hash.lower(), [url_text])
|
||||||
|
return bool(ok) if ok is not None else True
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|||||||
@@ -1554,22 +1554,10 @@ class Add_File(Cmdlet):
|
|||||||
return 1
|
return 1
|
||||||
|
|
||||||
try:
|
try:
|
||||||
hoster_url = provider.upload_to_room(str(media_path), str(room_id))
|
hoster_url = provider.upload_to_room(str(media_path), str(room_id), pipe_obj=pipe_obj)
|
||||||
log(f"File uploaded: {hoster_url}", file=sys.stderr)
|
log(f"File uploaded: {hoster_url}", file=sys.stderr)
|
||||||
|
|
||||||
# Associate URL with Hydrus if possible
|
|
||||||
f_hash = Add_File._resolve_file_hash(None, media_path, pipe_obj, None)
|
f_hash = Add_File._resolve_file_hash(None, media_path, pipe_obj, None)
|
||||||
if f_hash:
|
|
||||||
try:
|
|
||||||
store_name = getattr(pipe_obj, "store", None)
|
|
||||||
if store_name:
|
|
||||||
store = Store(config)
|
|
||||||
backend = store[str(store_name)]
|
|
||||||
client = getattr(backend, "_client", None)
|
|
||||||
if client is not None and hasattr(client, "associate_url"):
|
|
||||||
client.associate_url(str(f_hash), hoster_url)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
log(f"Upload failed: {exc}", file=sys.stderr)
|
log(f"Upload failed: {exc}", file=sys.stderr)
|
||||||
@@ -1676,24 +1664,10 @@ class Add_File(Cmdlet):
|
|||||||
log(f"File provider '{provider_name}' not available", file=sys.stderr)
|
log(f"File provider '{provider_name}' not available", file=sys.stderr)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
hoster_url = file_provider.upload(str(media_path))
|
hoster_url = file_provider.upload(str(media_path), pipe_obj=pipe_obj)
|
||||||
log(f"File uploaded: {hoster_url}", file=sys.stderr)
|
log(f"File uploaded: {hoster_url}", file=sys.stderr)
|
||||||
|
|
||||||
# Associate URL with Hydrus if possible
|
|
||||||
f_hash = Add_File._resolve_file_hash(None, media_path, pipe_obj, None)
|
f_hash = Add_File._resolve_file_hash(None, media_path, pipe_obj, None)
|
||||||
if f_hash:
|
|
||||||
try:
|
|
||||||
# Only associate when we can target an explicit Hydrus store backend.
|
|
||||||
# Do not fall back to a global/default Hydrus client.
|
|
||||||
store_name = getattr(pipe_obj, "store", None)
|
|
||||||
if store_name:
|
|
||||||
store = Store(config)
|
|
||||||
backend = store[str(store_name)]
|
|
||||||
client = getattr(backend, "_client", None)
|
|
||||||
if client is not None and hasattr(client, "associate_url"):
|
|
||||||
client.associate_url(str(f_hash), hoster_url)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
log(f"Upload failed: {exc}", file=sys.stderr)
|
log(f"Upload failed: {exc}", file=sys.stderr)
|
||||||
|
|||||||
@@ -15,6 +15,24 @@ import pipeline as ctx
|
|||||||
|
|
||||||
|
|
||||||
_MATRIX_PENDING_ITEMS_KEY = "matrix_pending_items"
|
_MATRIX_PENDING_ITEMS_KEY = "matrix_pending_items"
|
||||||
|
_MATRIX_PENDING_TEXT_KEY = "matrix_pending_text"
|
||||||
|
|
||||||
|
|
||||||
|
def _extract_text_arg(args: Sequence[str]) -> str:
|
||||||
|
"""Extract a `-text <value>` argument from a cmdnat args list."""
|
||||||
|
if not args:
|
||||||
|
return ""
|
||||||
|
try:
|
||||||
|
tokens = list(args)
|
||||||
|
except Exception:
|
||||||
|
return ""
|
||||||
|
for i, tok in enumerate(tokens):
|
||||||
|
try:
|
||||||
|
if str(tok).lower() == "-text" and i + 1 < len(tokens):
|
||||||
|
return str(tokens[i + 1] or "").strip()
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def _normalize_to_list(value: Any) -> List[Any]:
|
def _normalize_to_list(value: Any) -> List[Any]:
|
||||||
@@ -341,8 +359,16 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
|
|||||||
log(f"Matrix not available: {exc}", file=sys.stderr)
|
log(f"Matrix not available: {exc}", file=sys.stderr)
|
||||||
return 1
|
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 = ""
|
||||||
|
|
||||||
any_failed = False
|
any_failed = False
|
||||||
for rid in room_ids:
|
for rid in room_ids:
|
||||||
|
sent_any_for_room = False
|
||||||
for item in items:
|
for item in items:
|
||||||
file_path = _resolve_upload_path(item, config)
|
file_path = _resolve_upload_path(item, config)
|
||||||
if not file_path:
|
if not file_path:
|
||||||
@@ -350,16 +376,29 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
|
|||||||
log("Matrix upload requires a local file (path) or a direct URL on the selected item", file=sys.stderr)
|
log("Matrix upload requires a local file (path) or a direct URL on the selected item", file=sys.stderr)
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
link = provider.upload_to_room(file_path, rid)
|
link = provider.upload_to_room(file_path, rid, pipe_obj=item)
|
||||||
debug(f"✓ Sent {Path(file_path).name} -> {rid}")
|
debug(f"✓ Sent {Path(file_path).name} -> {rid}")
|
||||||
if link:
|
if link:
|
||||||
log(link)
|
log(link)
|
||||||
|
sent_any_for_room = True
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
any_failed = True
|
any_failed = True
|
||||||
log(f"Matrix send failed for {Path(file_path).name}: {exc}", file=sys.stderr)
|
log(f"Matrix send failed for {Path(file_path).name}: {exc}", file=sys.stderr)
|
||||||
|
|
||||||
|
# Optional caption-like follow-up message (sent once per room).
|
||||||
|
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 we've attempted to send.
|
# Clear pending items once we've attempted to send.
|
||||||
ctx.store_value(_MATRIX_PENDING_ITEMS_KEY, [])
|
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
|
return 1 if any_failed else 0
|
||||||
|
|
||||||
# Default stage: show rooms, then wait for @N selection to resume sending.
|
# Default stage: show rooms, then wait for @N selection to resume sending.
|
||||||
@@ -369,6 +408,10 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
|
|||||||
return 1
|
return 1
|
||||||
|
|
||||||
ctx.store_value(_MATRIX_PENDING_ITEMS_KEY, selected_items)
|
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
|
from Provider.matrix import Matrix
|
||||||
try:
|
try:
|
||||||
@@ -431,6 +474,7 @@ CMDLET = Cmdlet(
|
|||||||
usage="@N | .matrix",
|
usage="@N | .matrix",
|
||||||
arg=[
|
arg=[
|
||||||
CmdletArg(name="send", type="bool", description="(internal) Send to selected room(s)", required=False),
|
CmdletArg(name="send", type="bool", description="(internal) Send to selected room(s)", required=False),
|
||||||
|
CmdletArg(name="text", type="string", description="Send a follow-up text message after each upload (caption-like)", required=False),
|
||||||
],
|
],
|
||||||
exec=_run
|
exec=_run
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user