Переглянути джерело

Add chat/ready endpoint and fill-prices kwargs to dev.html and MCP

- dev.html: GET /chat/templates, GET /chat/{service}/ready buttons; add local-sync and push-busy to actions list
- MCP: chat_send_ready tool; weekday/weekend params on run_action for fill-prices
ZiB 4 місяців тому
батько
коміт
859b9c6332
2 змінених файлів з 39 додано та 4 видалено
  1. 18 2
      room_cal/mcp/__init__.py
  2. 21 2
      room_cal/vue/dev.html

+ 18 - 2
room_cal/mcp/__init__.py

@@ -207,6 +207,20 @@ async def get_chat_thread(service_key: str, chat_id: str) -> dict:
   return result
   return result
 
 
 
 
+@mcp.tool()
+async def chat_send_ready(service_key: str) -> dict:
+  """Check whether sending messages is currently available for a service. Returns {ready: bool}."""
+  from room_cal.service_api import ServiceAPI
+  api = ServiceAPI()
+  try:
+    client = api.chat_client(service_key)
+  except KeyError:
+    raise ValueError(f'unknown service: {service_key!r}')
+  except TypeError:
+    raise ValueError(f'{service_key!r} does not support chat')
+  return {'ready': client.send_ready()}
+
+
 @mcp.tool()
 @mcp.tool()
 async def send_chat_message(service_key: str, chat_id: str, body: str) -> None:
 async def send_chat_message(service_key: str, chat_id: str, body: str) -> None:
   """Send a text message to a guest conversation thread."""
   """Send a text message to a guest conversation thread."""
@@ -247,8 +261,10 @@ async def run_action(
   feed_name: str | None = None,
   feed_name: str | None = None,
   title: str | None = None,
   title: str | None = None,
   service: str | None = None,
   service: str | None = None,
+  weekday: int | None = None,
+  weekend: int | None = None,
 ) -> object:
 ) -> object:
-  """Run a room_cal action over a date range. action must be one of the names in _ACTIONS (auto-discovered from room_cal.actions). Optional kwargs: feed_name, title (fetch-feed); service (fetch-prices)."""
+  """Run a room_cal action over a date range. action must be one of the names in _ACTIONS (auto-discovered from room_cal.actions). Optional kwargs: feed_name, title (fetch-feed); service (fetch-prices); weekday, weekend (fill-prices)."""
   from datetime import date as _date
   from datetime import date as _date
   if action not in _ACTIONS:
   if action not in _ACTIONS:
     raise ValueError(f'Unknown action: {action!r}')
     raise ValueError(f'Unknown action: {action!r}')
@@ -257,6 +273,6 @@ async def run_action(
   fn = getattr(mod, fn_name)
   fn = getattr(mod, fn_name)
   start = _date.fromisoformat(start_date)
   start = _date.fromisoformat(start_date)
   end = _date.fromisoformat(end_date) if end_date else start
   end = _date.fromisoformat(end_date) if end_date else start
-  all_kwargs = {'feed_name': feed_name, 'title': title, 'service': service}
+  all_kwargs = {'feed_name': feed_name, 'title': title, 'service': service, 'weekday': weekday, 'weekend': weekend}
   kwargs = {k: v for k, v in all_kwargs.items() if k in allowed_keys and v is not None}
   kwargs = {k: v for k, v in all_kwargs.items() if k in allowed_keys and v is not None}
   return await fn(start, end, **kwargs)
   return await fn(start, end, **kwargs)

+ 21 - 2
room_cal/vue/dev.html

@@ -204,7 +204,8 @@
       <option>fetch-feed</option><option>fetch-prices</option>
       <option>fetch-feed</option><option>fetch-prices</option>
       <option>push-prices</option><option>fill-prices</option>
       <option>push-prices</option><option>fill-prices</option>
       <option>apply-discounts</option><option>apply-rules</option>
       <option>apply-discounts</option><option>apply-rules</option>
-      <option>local-refresh</option><option>diff</option>
+      <option>local-refresh</option><option>local-sync</option>
+      <option>push-busy</option><option>diff</option>
     </select>
     </select>
   </div>
   </div>
   <div class="row">
   <div class="row">
@@ -222,10 +223,16 @@
   <!-- ── CHAT ──────────────────────────────────────── -->
   <!-- ── CHAT ──────────────────────────────────────── -->
   <h2>Chat <span class="section-note">(service-specific messaging)</span></h2>
   <h2>Chat <span class="section-note">(service-specific messaging)</span></h2>
   <div class="row">
   <div class="row">
+    <button onclick="doChatTemplates()">GET /chat/templates</button>
+  </div>
+  <div class="result" id="res-chat-templates"></div>
+  <div class="row" style="margin-top:8px">
     <label class="lbl">service</label>
     <label class="lbl">service</label>
     <select id="chat-service"></select>
     <select id="chat-service"></select>
+    <button onclick="doChatReady()" style="margin-left:6px">GET ready</button>
   </div>
   </div>
-  <div class="row">
+  <div class="result" id="res-chat-ready"></div>
+  <div class="row" style="margin-top:8px">
     <label class="lbl">chat_id</label>
     <label class="lbl">chat_id</label>
     <input id="chat-id" type="text" placeholder="thread / reservation id">
     <input id="chat-id" type="text" placeholder="thread / reservation id">
     <button onclick="doChatGet()">GET thread</button>
     <button onclick="doChatGet()">GET thread</button>
@@ -582,6 +589,18 @@
     }
     }
 
 
     // ── chat ──
     // ── chat ──
+    async function doChatTemplates() {
+      pending('res-chat-templates');
+      show('res-chat-templates', await api('GET', '/chat/templates'));
+    }
+
+    async function doChatReady() {
+      const service = document.getElementById('chat-service').value;
+      if (!service) return;
+      pending('res-chat-ready');
+      show('res-chat-ready', await api('GET', `/chat/${service}/ready`));
+    }
+
     async function doChatGet() {
     async function doChatGet() {
       const service = document.getElementById('chat-service').value;
       const service = document.getElementById('chat-service').value;
       const chatId = document.getElementById('chat-id').value.trim();
       const chatId = document.getElementById('chat-id').value.trim();