packetdial: add gated call-queue + time-frame edit wrappers (live-verified)

Added write wrappers, each tested create→update→delete on the arizonacomputerguru test
domain (sanctioned, non-production):
- call queues: create-callqueue, update-callqueue, delete-callqueue + add-agent /
  update-agent / remove-agent
- time frames: create-timeframe, update-timeframe, delete-timeframe (body-discriminated —
  same path, server selects the op from the body; wrappers pass --body verbatim)

All behind --confirm (gate verified: DRY RUN refuses without it). SKILL.md documents the
bodies + the days-of-week-needs-array gotcha + names ACG as the test bed; capability map
+ api.md history updated. No production objects touched; no test leftovers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-22 07:44:30 -07:00
parent 25b2ff45bf
commit f42f9c2419
4 changed files with 139 additions and 2 deletions

View File

@@ -404,3 +404,51 @@ class NetSapiensClient:
return self.request(
"POST", f"domains/{urllib.parse.quote(domain)}/phonenumbers", json_body=body
)
# --- call queue writes (live-verified on arizonacomputerguru, 2026-06-22) ---
# Create body requires {"synchronous":"yes","callqueue":"<id>"} (+ optional config).
def create_callqueue(self, domain: str, body: dict) -> Any:
return self.request(
"POST", f"domains/{urllib.parse.quote(domain)}/callqueues", json_body=body
)
def update_callqueue(self, domain: str, callqueue: str, body: dict) -> Any:
d, c = urllib.parse.quote(domain), urllib.parse.quote(callqueue)
return self.request("PUT", f"domains/{d}/callqueues/{c}", json_body=body)
def delete_callqueue(self, domain: str, callqueue: str) -> Any:
d, c = urllib.parse.quote(domain), urllib.parse.quote(callqueue)
return self.request("DELETE", f"domains/{d}/callqueues/{c}")
def add_callqueue_agent(self, domain: str, callqueue: str, body: dict) -> Any:
# body requires {"callqueue-agent-id": "<ext>@<domain>"}.
d, c = urllib.parse.quote(domain), urllib.parse.quote(callqueue)
return self.request("POST", f"domains/{d}/callqueues/{c}/agents", json_body=body)
def update_callqueue_agent(self, domain: str, callqueue: str, agent_id: str, body: dict) -> Any:
d, c, a = (urllib.parse.quote(domain), urllib.parse.quote(callqueue),
urllib.parse.quote(agent_id))
return self.request("PUT", f"domains/{d}/callqueues/{c}/agents/{a}", json_body=body)
def remove_callqueue_agent(self, domain: str, callqueue: str, agent_id: str) -> Any:
d, c, a = (urllib.parse.quote(domain), urllib.parse.quote(callqueue),
urllib.parse.quote(agent_id))
return self.request("DELETE", f"domains/{d}/callqueues/{c}/agents/{a}")
# --- timeframe writes ---
# Same path, body-discriminated variants (NetSapiens routes PUT/DELETE by body):
# create: {"synchronous":"yes","timeframe-name":"X","timeframe-type":"days-of-week|specific-dates|holiday|custom|always"}
# update PUT body carries the type-specific array(s); pass --body verbatim.
# delete with no body = delete the whole timeframe; with {"child_id":N} = delete one entry.
def create_timeframe(self, domain: str, body: dict) -> Any:
return self.request(
"POST", f"domains/{urllib.parse.quote(domain)}/timeframes", json_body=body
)
def update_timeframe(self, domain: str, tf_id: str, body: dict) -> Any:
d, t = urllib.parse.quote(domain), urllib.parse.quote(tf_id)
return self.request("PUT", f"domains/{d}/timeframes/{t}", json_body=body)
def delete_timeframe(self, domain: str, tf_id: str, body: Optional[dict] = None) -> Any:
d, t = urllib.parse.quote(domain), urllib.parse.quote(tf_id)
return self.request("DELETE", f"domains/{d}/timeframes/{t}", json_body=body)