|
|
@@ -3589,6 +3589,22 @@ CHATTO_SEND_ROOM_SCHEMA = {
|
|
|
"type": "string",
|
|
|
"description": "The message text to send (markdown supported).",
|
|
|
},
|
|
|
+ "thread_root_event_id": {
|
|
|
+ "type": "string",
|
|
|
+ "description": (
|
|
|
+ "Optional: event ID of the thread-root message. When given, "
|
|
|
+ "this message is posted INSIDE that thread. Mutually "
|
|
|
+ "exclusive with `create_thread`."
|
|
|
+ ),
|
|
|
+ },
|
|
|
+ "create_thread": {
|
|
|
+ "type": "boolean",
|
|
|
+ "description": (
|
|
|
+ "Optional: post this message as the ROOT of a new thread "
|
|
|
+ "(default false = normal message). Mutually exclusive with "
|
|
|
+ "`thread_root_event_id`."
|
|
|
+ ),
|
|
|
+ },
|
|
|
"base_url": {
|
|
|
"type": "string",
|
|
|
"description": (
|
|
|
@@ -3799,6 +3815,8 @@ async def _handle_chatto_send_room(args: dict) -> dict:
|
|
|
room_id = args.get("room_id") or ""
|
|
|
room_name = args.get("room_name") or ""
|
|
|
message = args.get("message") or ""
|
|
|
+ thread_root_event_id = args.get("thread_root_event_id") or ""
|
|
|
+ create_thread = bool(args.get("create_thread", False))
|
|
|
|
|
|
if not message:
|
|
|
return json.dumps({"success": False, "error": "message is required"})
|
|
|
@@ -3806,6 +3824,12 @@ async def _handle_chatto_send_room(args: dict) -> dict:
|
|
|
if not room_id and not room_name:
|
|
|
return json.dumps({"success": False, "error": "Exactly one of room_id or room_name is required"})
|
|
|
|
|
|
+ if thread_root_event_id and create_thread:
|
|
|
+ return json.dumps({
|
|
|
+ "success": False,
|
|
|
+ "error": "thread_root_event_id and create_thread are mutually exclusive",
|
|
|
+ })
|
|
|
+
|
|
|
# --- Read config: explicit args first, then fall back to resolved config ---
|
|
|
explicit_token = args.get("token") or ""
|
|
|
|
|
|
@@ -3857,9 +3881,26 @@ async def _handle_chatto_send_room(args: dict) -> dict:
|
|
|
normalised, ChattoConstants.SPLIT_THRESHOLD
|
|
|
)
|
|
|
message_ids = []
|
|
|
- for chunk in chunks:
|
|
|
- msg = await client.post_message(room_id, chunk)
|
|
|
+ root_message_id = ""
|
|
|
+ for idx, chunk in enumerate(chunks):
|
|
|
+ # First chunk of a new thread IS the thread root; subsequent
|
|
|
+ # chunks land inside the thread. thread_root_event_id given
|
|
|
+ # explicitly puts every chunk inside that existing thread.
|
|
|
+ effective_root = thread_root_event_id or root_message_id
|
|
|
+ msg = await client.post_message(room_id, chunk, thread_root_event_id=effective_root)
|
|
|
message_ids.append(msg.id)
|
|
|
+ if create_thread and not root_message_id:
|
|
|
+ root_message_id = msg.id
|
|
|
+
|
|
|
+ # 3. A newly created thread should be followed so later activity
|
|
|
+ # reaches the bot (mirrors the adapter's own thread handling).
|
|
|
+ if create_thread and root_message_id:
|
|
|
+ try:
|
|
|
+ await client.follow_thread(room_id, root_message_id)
|
|
|
+ except Exception:
|
|
|
+ # Follow is best-effort — the thread exists and the message
|
|
|
+ # was delivered; don't fail the tool over a follow hiccup.
|
|
|
+ pass
|
|
|
|
|
|
return json.dumps({
|
|
|
"success": True,
|
|
|
@@ -3867,6 +3908,8 @@ async def _handle_chatto_send_room(args: dict) -> dict:
|
|
|
"room_name": room_label,
|
|
|
"message_ids": message_ids,
|
|
|
"chunks_sent": len(message_ids),
|
|
|
+ "thread_root_event_id": thread_root_event_id or root_message_id,
|
|
|
+ "created_thread": bool(root_message_id),
|
|
|
})
|
|
|
|
|
|
except ChattoAuthError as exc:
|