wxcli 1.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (413) hide show
  1. wxcli/__init__.py +4 -0
  2. wxcli/_playbook/.claude/agents/migration-advisor.md +239 -0
  3. wxcli/_playbook/.claude/agents/wxc-calling-builder.md +946 -0
  4. wxcli/_playbook/.claude/rules/cleanup.md +29 -0
  5. wxcli/_playbook/.claude/rules/cucm-migration.md +58 -0
  6. wxcli/_playbook/.claude/rules/org-health.md +14 -0
  7. wxcli/_playbook/.claude/settings.json +8 -0
  8. wxcli/_playbook/.claude/skills/audit-compliance/SKILL.md +515 -0
  9. wxcli/_playbook/.claude/skills/call-control/SKILL.md +747 -0
  10. wxcli/_playbook/.claude/skills/configure-features/SKILL.md +623 -0
  11. wxcli/_playbook/.claude/skills/configure-routing/SKILL.md +627 -0
  12. wxcli/_playbook/.claude/skills/contact-center/SKILL.md +1181 -0
  13. wxcli/_playbook/.claude/skills/cucm-migrate/SKILL.md +820 -0
  14. wxcli/_playbook/.claude/skills/customer-assist/SKILL.md +443 -0
  15. wxcli/_playbook/.claude/skills/device-platform/SKILL.md +407 -0
  16. wxcli/_playbook/.claude/skills/manage-call-settings/SKILL.md +757 -0
  17. wxcli/_playbook/.claude/skills/manage-devices/SKILL.md +790 -0
  18. wxcli/_playbook/.claude/skills/manage-identity/SKILL.md +638 -0
  19. wxcli/_playbook/.claude/skills/manage-licensing/SKILL.md +528 -0
  20. wxcli/_playbook/.claude/skills/manage-meetings/SKILL.md +708 -0
  21. wxcli/_playbook/.claude/skills/messaging-bots/SKILL.md +365 -0
  22. wxcli/_playbook/.claude/skills/messaging-spaces/SKILL.md +520 -0
  23. wxcli/_playbook/.claude/skills/org-health/SKILL.md +159 -0
  24. wxcli/_playbook/.claude/skills/provision-calling/SKILL.md +421 -0
  25. wxcli/_playbook/.claude/skills/query-live/SKILL.md +183 -0
  26. wxcli/_playbook/.claude/skills/query-live/domains/call-flow-trace.md +223 -0
  27. wxcli/_playbook/.claude/skills/query-live/domains/call-history.md +44 -0
  28. wxcli/_playbook/.claude/skills/query-live/domains/features.md +169 -0
  29. wxcli/_playbook/.claude/skills/query-live/domains/numbers.md +93 -0
  30. wxcli/_playbook/.claude/skills/query-live/domains/people-and-settings.md +146 -0
  31. wxcli/_playbook/.claude/skills/query-live/domains/routing.md +124 -0
  32. wxcli/_playbook/.claude/skills/reporting/SKILL.md +501 -0
  33. wxcli/_playbook/.claude/skills/reporting/references/cdr-recipes.md +1628 -0
  34. wxcli/_playbook/.claude/skills/reporting-cc/SKILL.md +251 -0
  35. wxcli/_playbook/.claude/skills/reporting-meetings/SKILL.md +290 -0
  36. wxcli/_playbook/.claude/skills/teardown/SKILL.md +214 -0
  37. wxcli/_playbook/.claude/skills/video-mesh/SKILL.md +379 -0
  38. wxcli/_playbook/.claude/skills/wxc-calling-debug/SKILL.md +475 -0
  39. wxcli/_playbook/CLAUDE.md +373 -0
  40. wxcli/_playbook/docs/reference/CLAUDE.md +25 -0
  41. wxcli/_playbook/docs/reference/admin-apps-data.md +643 -0
  42. wxcli/_playbook/docs/reference/admin-audit-security.md +322 -0
  43. wxcli/_playbook/docs/reference/admin-hybrid.md +383 -0
  44. wxcli/_playbook/docs/reference/admin-identity-scim.md +1007 -0
  45. wxcli/_playbook/docs/reference/admin-licensing.md +414 -0
  46. wxcli/_playbook/docs/reference/admin-org-management.md +586 -0
  47. wxcli/_playbook/docs/reference/admin-partner.md +357 -0
  48. wxcli/_playbook/docs/reference/archive/wxc-sdk-patterns.md +1229 -0
  49. wxcli/_playbook/docs/reference/archive/wxcadm-advanced.md +835 -0
  50. wxcli/_playbook/docs/reference/archive/wxcadm-core.md +743 -0
  51. wxcli/_playbook/docs/reference/archive/wxcadm-devices-workspaces.md +1305 -0
  52. wxcli/_playbook/docs/reference/archive/wxcadm-features.md +849 -0
  53. wxcli/_playbook/docs/reference/archive/wxcadm-locations.md +938 -0
  54. wxcli/_playbook/docs/reference/archive/wxcadm-person.md +1255 -0
  55. wxcli/_playbook/docs/reference/archive/wxcadm-routing.md +1262 -0
  56. wxcli/_playbook/docs/reference/authentication.md +965 -0
  57. wxcli/_playbook/docs/reference/call-control.md +1205 -0
  58. wxcli/_playbook/docs/reference/call-features-additional.md +2541 -0
  59. wxcli/_playbook/docs/reference/call-features-major.md +1320 -0
  60. wxcli/_playbook/docs/reference/call-routing.md +2304 -0
  61. wxcli/_playbook/docs/reference/contact-center-analytics.md +1049 -0
  62. wxcli/_playbook/docs/reference/contact-center-core.md +1614 -0
  63. wxcli/_playbook/docs/reference/contact-center-journey.md +457 -0
  64. wxcli/_playbook/docs/reference/contact-center-routing.md +1138 -0
  65. wxcli/_playbook/docs/reference/devices-core.md +1869 -0
  66. wxcli/_playbook/docs/reference/devices-dect.md +1150 -0
  67. wxcli/_playbook/docs/reference/devices-platform.md +467 -0
  68. wxcli/_playbook/docs/reference/devices-workspaces.md +1426 -0
  69. wxcli/_playbook/docs/reference/emergency-services.md +1122 -0
  70. wxcli/_playbook/docs/reference/location-calling-core.md +2037 -0
  71. wxcli/_playbook/docs/reference/location-calling-media.md +1334 -0
  72. wxcli/_playbook/docs/reference/location-recording-advanced.md +1482 -0
  73. wxcli/_playbook/docs/reference/meetings-content.md +473 -0
  74. wxcli/_playbook/docs/reference/meetings-core.md +903 -0
  75. wxcli/_playbook/docs/reference/meetings-infrastructure.md +899 -0
  76. wxcli/_playbook/docs/reference/meetings-settings.md +850 -0
  77. wxcli/_playbook/docs/reference/messaging-bots.md +1091 -0
  78. wxcli/_playbook/docs/reference/messaging-spaces.md +695 -0
  79. wxcli/_playbook/docs/reference/person-call-settings-behavior.md +1639 -0
  80. wxcli/_playbook/docs/reference/person-call-settings-handling.md +1394 -0
  81. wxcli/_playbook/docs/reference/person-call-settings-media.md +1898 -0
  82. wxcli/_playbook/docs/reference/person-call-settings-permissions.md +1329 -0
  83. wxcli/_playbook/docs/reference/provisioning.md +1366 -0
  84. wxcli/_playbook/docs/reference/reporting-analytics.md +1318 -0
  85. wxcli/_playbook/docs/reference/self-service-call-settings.md +1020 -0
  86. wxcli/_playbook/docs/reference/virtual-lines.md +1132 -0
  87. wxcli/_playbook/docs/reference/webhooks-events.md +1444 -0
  88. wxcli/_playbook/docs/reference/wxcadm-xsi-realtime.md +953 -0
  89. wxcli/_version.py +24 -0
  90. wxcli/auth.py +137 -0
  91. wxcli/commands/__init__.py +0 -0
  92. wxcli/commands/_registry.py +181 -0
  93. wxcli/commands/activation_email.py +103 -0
  94. wxcli/commands/admin_recordings.py +585 -0
  95. wxcli/commands/analytics.py +105 -0
  96. wxcli/commands/announcement_playlists.py +219 -0
  97. wxcli/commands/announcements.py +519 -0
  98. wxcli/commands/archive_users.py +36 -0
  99. wxcli/commands/attachment_actions.py +60 -0
  100. wxcli/commands/audit_events.py +83 -0
  101. wxcli/commands/authorizations.py +113 -0
  102. wxcli/commands/auto_attendant.py +642 -0
  103. wxcli/commands/broadworks_billing_reports.py +126 -0
  104. wxcli/commands/broadworks_enterprises.py +160 -0
  105. wxcli/commands/broadworks_subscribers.py +319 -0
  106. wxcli/commands/broadworks_workspaces.py +108 -0
  107. wxcli/commands/call_controls.py +1304 -0
  108. wxcli/commands/call_park.py +494 -0
  109. wxcli/commands/call_pickup.py +229 -0
  110. wxcli/commands/call_queue.py +1878 -0
  111. wxcli/commands/call_recording.py +707 -0
  112. wxcli/commands/call_routing.py +1610 -0
  113. wxcli/commands/call_settings_for_me_phase_5.py +212 -0
  114. wxcli/commands/caller_reputation.py +169 -0
  115. wxcli/commands/calling_service.py +305 -0
  116. wxcli/commands/cc_address_book.py +770 -0
  117. wxcli/commands/cc_agent_greetings.py +242 -0
  118. wxcli/commands/cc_agent_summaries.py +39 -0
  119. wxcli/commands/cc_agent_wellbeing.py +216 -0
  120. wxcli/commands/cc_agents.py +425 -0
  121. wxcli/commands/cc_ai_assistant.py +55 -0
  122. wxcli/commands/cc_ai_feature.py +294 -0
  123. wxcli/commands/cc_audio_files.py +149 -0
  124. wxcli/commands/cc_auto_csat.py +311 -0
  125. wxcli/commands/cc_aux_code.py +458 -0
  126. wxcli/commands/cc_business_hour.py +353 -0
  127. wxcli/commands/cc_call_monitoring.py +228 -0
  128. wxcli/commands/cc_callbacks.py +194 -0
  129. wxcli/commands/cc_campaign.py +212 -0
  130. wxcli/commands/cc_captures.py +39 -0
  131. wxcli/commands/cc_contact_list.py +169 -0
  132. wxcli/commands/cc_contact_number.py +295 -0
  133. wxcli/commands/cc_data_sources.py +235 -0
  134. wxcli/commands/cc_desktop_layout.py +380 -0
  135. wxcli/commands/cc_desktop_profile.py +322 -0
  136. wxcli/commands/cc_dial_number.py +511 -0
  137. wxcli/commands/cc_dial_plan.py +347 -0
  138. wxcli/commands/cc_dnc.py +101 -0
  139. wxcli/commands/cc_entry_point.py +545 -0
  140. wxcli/commands/cc_ewt.py +51 -0
  141. wxcli/commands/cc_flow.py +477 -0
  142. wxcli/commands/cc_global_vars.py +430 -0
  143. wxcli/commands/cc_holiday_list.py +335 -0
  144. wxcli/commands/cc_journey.py +1261 -0
  145. wxcli/commands/cc_legacy_flows.py +49 -0
  146. wxcli/commands/cc_multimedia_profile.py +436 -0
  147. wxcli/commands/cc_notification.py +51 -0
  148. wxcli/commands/cc_outdial_ani.py +604 -0
  149. wxcli/commands/cc_overrides.py +341 -0
  150. wxcli/commands/cc_queue.py +1083 -0
  151. wxcli/commands/cc_queue_stats.py +55 -0
  152. wxcli/commands/cc_realtime.py +51 -0
  153. wxcli/commands/cc_resource_collection.py +312 -0
  154. wxcli/commands/cc_search.py +46 -0
  155. wxcli/commands/cc_site.py +400 -0
  156. wxcli/commands/cc_skill.py +350 -0
  157. wxcli/commands/cc_skill_profile.py +289 -0
  158. wxcli/commands/cc_subscriptions.py +426 -0
  159. wxcli/commands/cc_summaries.py +130 -0
  160. wxcli/commands/cc_tasks.py +844 -0
  161. wxcli/commands/cc_team.py +325 -0
  162. wxcli/commands/cc_user_profiles.py +349 -0
  163. wxcli/commands/cc_users.py +701 -0
  164. wxcli/commands/cc_work_types.py +363 -0
  165. wxcli/commands/cdr.py +89 -0
  166. wxcli/commands/classifications.py +38 -0
  167. wxcli/commands/cleanup.py +1427 -0
  168. wxcli/commands/client_settings.py +72 -0
  169. wxcli/commands/conference.py +328 -0
  170. wxcli/commands/configure.py +72 -0
  171. wxcli/commands/converged_recordings.py +347 -0
  172. wxcli/commands/converged_recordings_export.py +310 -0
  173. wxcli/commands/cq_playlists.py +42 -0
  174. wxcli/commands/cucm.py +2794 -0
  175. wxcli/commands/cucm_config.py +129 -0
  176. wxcli/commands/customer_assist.py +373 -0
  177. wxcli/commands/data_sources.py +227 -0
  178. wxcli/commands/dect_devices.py +787 -0
  179. wxcli/commands/device_configurations.py +77 -0
  180. wxcli/commands/device_dynamic_settings.py +320 -0
  181. wxcli/commands/device_settings.py +1489 -0
  182. wxcli/commands/devices.py +280 -0
  183. wxcli/commands/domains.py +142 -0
  184. wxcli/commands/ecm.py +178 -0
  185. wxcli/commands/emergency_services.py +827 -0
  186. wxcli/commands/events.py +83 -0
  187. wxcli/commands/external_voicemail.py +52 -0
  188. wxcli/commands/groups.py +206 -0
  189. wxcli/commands/guest_management.py +76 -0
  190. wxcli/commands/hds.py +340 -0
  191. wxcli/commands/hot_desk.py +67 -0
  192. wxcli/commands/hot_desking_members.py +118 -0
  193. wxcli/commands/hot_desking_portal.py +124 -0
  194. wxcli/commands/hunt_group.py +577 -0
  195. wxcli/commands/hybrid_clusters.py +71 -0
  196. wxcli/commands/hybrid_connectors.py +67 -0
  197. wxcli/commands/identity_org.py +159 -0
  198. wxcli/commands/init_playbook.py +155 -0
  199. wxcli/commands/licenses.py +106 -0
  200. wxcli/commands/live_monitoring.py +40 -0
  201. wxcli/commands/location_call_handling.py +577 -0
  202. wxcli/commands/location_schedules.py +341 -0
  203. wxcli/commands/location_settings.py +1297 -0
  204. wxcli/commands/location_voicemail.py +494 -0
  205. wxcli/commands/locations.py +336 -0
  206. wxcli/commands/meeting_captions.py +110 -0
  207. wxcli/commands/meeting_chats.py +63 -0
  208. wxcli/commands/meeting_invitees.py +234 -0
  209. wxcli/commands/meeting_messages.py +28 -0
  210. wxcli/commands/meeting_participants.py +278 -0
  211. wxcli/commands/meeting_polls.py +111 -0
  212. wxcli/commands/meeting_preferences.py +515 -0
  213. wxcli/commands/meeting_qa.py +78 -0
  214. wxcli/commands/meeting_qualities.py +43 -0
  215. wxcli/commands/meeting_reports.py +101 -0
  216. wxcli/commands/meeting_session_types.py +105 -0
  217. wxcli/commands/meeting_site.py +57 -0
  218. wxcli/commands/meeting_slido.py +44 -0
  219. wxcli/commands/meeting_summaries.py +92 -0
  220. wxcli/commands/meeting_tracking_codes.py +233 -0
  221. wxcli/commands/meeting_transcripts.py +230 -0
  222. wxcli/commands/meetings.py +1997 -0
  223. wxcli/commands/memberships.py +164 -0
  224. wxcli/commands/messages.py +213 -0
  225. wxcli/commands/mode_management.py +244 -0
  226. wxcli/commands/my_call_settings.py +3577 -0
  227. wxcli/commands/numbers.py +405 -0
  228. wxcli/commands/operating_modes.py +410 -0
  229. wxcli/commands/org_contacts.py +284 -0
  230. wxcli/commands/org_health_cli.py +55 -0
  231. wxcli/commands/org_settings.py +71 -0
  232. wxcli/commands/organizations.py +83 -0
  233. wxcli/commands/paging_group.py +257 -0
  234. wxcli/commands/partner_admins.py +141 -0
  235. wxcli/commands/partner_reports.py +188 -0
  236. wxcli/commands/partner_tags.py +216 -0
  237. wxcli/commands/people.py +279 -0
  238. wxcli/commands/person_call_settings.py +71 -0
  239. wxcli/commands/pstn.py +280 -0
  240. wxcli/commands/recording_report.py +154 -0
  241. wxcli/commands/report_templates.py +38 -0
  242. wxcli/commands/reports.py +144 -0
  243. wxcli/commands/resource_group_memberships.py +160 -0
  244. wxcli/commands/resource_groups.py +67 -0
  245. wxcli/commands/roles.py +63 -0
  246. wxcli/commands/room_tabs.py +160 -0
  247. wxcli/commands/rooms.py +225 -0
  248. wxcli/commands/scim_bulk.py +46 -0
  249. wxcli/commands/scim_groups.py +246 -0
  250. wxcli/commands/scim_schemas.py +82 -0
  251. wxcli/commands/scim_users.py +283 -0
  252. wxcli/commands/security_audit.py +54 -0
  253. wxcli/commands/service_apps.py +51 -0
  254. wxcli/commands/single_number_reach.py +209 -0
  255. wxcli/commands/team_memberships.py +155 -0
  256. wxcli/commands/teams.py +153 -0
  257. wxcli/commands/ucm_profile.py +42 -0
  258. wxcli/commands/update.py +219 -0
  259. wxcli/commands/user_settings.py +5116 -0
  260. wxcli/commands/video_mesh.py +1090 -0
  261. wxcli/commands/virtual_extensions.py +502 -0
  262. wxcli/commands/virtual_line_settings.py +2072 -0
  263. wxcli/commands/webhooks.py +176 -0
  264. wxcli/commands/wholesale_billing_reports.py +147 -0
  265. wxcli/commands/wholesale_provisioning.py +523 -0
  266. wxcli/commands/workspace_locations.py +339 -0
  267. wxcli/commands/workspace_metrics.py +103 -0
  268. wxcli/commands/workspace_personalization.py +70 -0
  269. wxcli/commands/workspace_settings.py +3347 -0
  270. wxcli/commands/workspaces.py +279 -0
  271. wxcli/commands/xapi.py +114 -0
  272. wxcli/config.py +139 -0
  273. wxcli/errors.py +77 -0
  274. wxcli/main.py +210 -0
  275. wxcli/migration/CLAUDE.md +106 -0
  276. wxcli/migration/__init__.py +1 -0
  277. wxcli/migration/advisory/CLAUDE.md +196 -0
  278. wxcli/migration/advisory/__init__.py +46 -0
  279. wxcli/migration/advisory/advisor.py +107 -0
  280. wxcli/migration/advisory/advisory_patterns.py +2552 -0
  281. wxcli/migration/advisory/recommendation_rules.py +835 -0
  282. wxcli/migration/cucm/CLAUDE.md +74 -0
  283. wxcli/migration/cucm/__init__.py +5 -0
  284. wxcli/migration/cucm/connection.py +265 -0
  285. wxcli/migration/cucm/discovery.py +225 -0
  286. wxcli/migration/cucm/extractors/__init__.py +1 -0
  287. wxcli/migration/cucm/extractors/announcements.py +92 -0
  288. wxcli/migration/cucm/extractors/base.py +90 -0
  289. wxcli/migration/cucm/extractors/device_profiles.py +98 -0
  290. wxcli/migration/cucm/extractors/devices.py +193 -0
  291. wxcli/migration/cucm/extractors/e911.py +103 -0
  292. wxcli/migration/cucm/extractors/features.py +309 -0
  293. wxcli/migration/cucm/extractors/helpers.py +73 -0
  294. wxcli/migration/cucm/extractors/informational.py +295 -0
  295. wxcli/migration/cucm/extractors/locations.py +221 -0
  296. wxcli/migration/cucm/extractors/moh.py +100 -0
  297. wxcli/migration/cucm/extractors/remote_destinations.py +96 -0
  298. wxcli/migration/cucm/extractors/routing.py +493 -0
  299. wxcli/migration/cucm/extractors/shared_lines.py +146 -0
  300. wxcli/migration/cucm/extractors/templates.py +229 -0
  301. wxcli/migration/cucm/extractors/tier4.py +173 -0
  302. wxcli/migration/cucm/extractors/users.py +240 -0
  303. wxcli/migration/cucm/extractors/voicemail.py +243 -0
  304. wxcli/migration/cucm/extractors/workspaces.py +88 -0
  305. wxcli/migration/cucm/unity_connection.py +304 -0
  306. wxcli/migration/execute/CLAUDE.md +372 -0
  307. wxcli/migration/execute/__init__.py +303 -0
  308. wxcli/migration/execute/batch.py +277 -0
  309. wxcli/migration/execute/dependency.py +483 -0
  310. wxcli/migration/execute/engine.py +964 -0
  311. wxcli/migration/execute/handlers.py +2232 -0
  312. wxcli/migration/execute/planner.py +1939 -0
  313. wxcli/migration/execute/runtime.py +571 -0
  314. wxcli/migration/export/CLAUDE.md +49 -0
  315. wxcli/migration/export/__init__.py +8 -0
  316. wxcli/migration/export/csv_export.py +126 -0
  317. wxcli/migration/export/deployment_plan.py +479 -0
  318. wxcli/migration/export/json_export.py +60 -0
  319. wxcli/migration/models.py +941 -0
  320. wxcli/migration/phone_models.py +50 -0
  321. wxcli/migration/preflight/CLAUDE.md +52 -0
  322. wxcli/migration/preflight/__init__.py +89 -0
  323. wxcli/migration/preflight/checks.py +911 -0
  324. wxcli/migration/preflight/runner.py +293 -0
  325. wxcli/migration/rate_limiter.py +144 -0
  326. wxcli/migration/report/CLAUDE.md +245 -0
  327. wxcli/migration/report/__init__.py +7 -0
  328. wxcli/migration/report/appendix.py +2437 -0
  329. wxcli/migration/report/assembler.py +183 -0
  330. wxcli/migration/report/charts.py +263 -0
  331. wxcli/migration/report/executive.py +776 -0
  332. wxcli/migration/report/explainer.py +704 -0
  333. wxcli/migration/report/helpers.py +74 -0
  334. wxcli/migration/report/ingest.py +152 -0
  335. wxcli/migration/report/notice_templates.py +192 -0
  336. wxcli/migration/report/score.py +375 -0
  337. wxcli/migration/report/styles.py +1052 -0
  338. wxcli/migration/report/user_diff.py +1052 -0
  339. wxcli/migration/report/user_notice.py +425 -0
  340. wxcli/migration/state.py +139 -0
  341. wxcli/migration/store.py +860 -0
  342. wxcli/migration/transform/CLAUDE.md +107 -0
  343. wxcli/migration/transform/__init__.py +12 -0
  344. wxcli/migration/transform/analysis_pipeline.py +453 -0
  345. wxcli/migration/transform/analyzers/__init__.py +142 -0
  346. wxcli/migration/transform/analyzers/css_permission.py +189 -0
  347. wxcli/migration/transform/analyzers/css_routing.py +335 -0
  348. wxcli/migration/transform/analyzers/device_compatibility.py +122 -0
  349. wxcli/migration/transform/analyzers/dn_ambiguity.py +131 -0
  350. wxcli/migration/transform/analyzers/duplicate_user.py +221 -0
  351. wxcli/migration/transform/analyzers/extension_conflict.py +173 -0
  352. wxcli/migration/transform/analyzers/feature_approximation.py +227 -0
  353. wxcli/migration/transform/analyzers/layout_overflow.py +174 -0
  354. wxcli/migration/transform/analyzers/location_ambiguity.py +184 -0
  355. wxcli/migration/transform/analyzers/missing_data.py +300 -0
  356. wxcli/migration/transform/analyzers/selective_call_handling.py +394 -0
  357. wxcli/migration/transform/analyzers/shared_line.py +170 -0
  358. wxcli/migration/transform/analyzers/voicemail_compatibility.py +267 -0
  359. wxcli/migration/transform/analyzers/workspace_license.py +212 -0
  360. wxcli/migration/transform/cross_reference.py +1136 -0
  361. wxcli/migration/transform/cucm_pattern.py +246 -0
  362. wxcli/migration/transform/decisions.py +301 -0
  363. wxcli/migration/transform/e164.py +145 -0
  364. wxcli/migration/transform/engine.py +256 -0
  365. wxcli/migration/transform/mappers/CLAUDE.md +123 -0
  366. wxcli/migration/transform/mappers/__init__.py +76 -0
  367. wxcli/migration/transform/mappers/announcement_mapper.py +122 -0
  368. wxcli/migration/transform/mappers/base.py +190 -0
  369. wxcli/migration/transform/mappers/button_template_mapper.py +186 -0
  370. wxcli/migration/transform/mappers/call_forwarding_mapper.py +228 -0
  371. wxcli/migration/transform/mappers/call_settings_mapper.py +144 -0
  372. wxcli/migration/transform/mappers/css_mapper.py +868 -0
  373. wxcli/migration/transform/mappers/dect_mapper.py +367 -0
  374. wxcli/migration/transform/mappers/device_layout_mapper.py +289 -0
  375. wxcli/migration/transform/mappers/device_mapper.py +227 -0
  376. wxcli/migration/transform/mappers/device_profile_mapper.py +190 -0
  377. wxcli/migration/transform/mappers/device_settings_mapper.py +464 -0
  378. wxcli/migration/transform/mappers/e911_mapper.py +129 -0
  379. wxcli/migration/transform/mappers/ecbn_mapper.py +263 -0
  380. wxcli/migration/transform/mappers/executive_assistant_mapper.py +156 -0
  381. wxcli/migration/transform/mappers/feature_mapper.py +1179 -0
  382. wxcli/migration/transform/mappers/line_mapper.py +310 -0
  383. wxcli/migration/transform/mappers/location_mapper.py +235 -0
  384. wxcli/migration/transform/mappers/moh_mapper.py +114 -0
  385. wxcli/migration/transform/mappers/monitoring_mapper.py +221 -0
  386. wxcli/migration/transform/mappers/receptionist_mapper.py +245 -0
  387. wxcli/migration/transform/mappers/routing_mapper.py +821 -0
  388. wxcli/migration/transform/mappers/snr_mapper.py +201 -0
  389. wxcli/migration/transform/mappers/softkey_mapper.py +205 -0
  390. wxcli/migration/transform/mappers/user_mapper.py +264 -0
  391. wxcli/migration/transform/mappers/voicemail_group_mapper.py +286 -0
  392. wxcli/migration/transform/mappers/voicemail_mapper.py +514 -0
  393. wxcli/migration/transform/mappers/workspace_mapper.py +464 -0
  394. wxcli/migration/transform/normalizers.py +2008 -0
  395. wxcli/migration/transform/pattern_converter.py +106 -0
  396. wxcli/migration/transform/pipeline.py +271 -0
  397. wxcli/migration/transform/rules.py +255 -0
  398. wxcli/org_health/CLAUDE.md +63 -0
  399. wxcli/org_health/__init__.py +7 -0
  400. wxcli/org_health/analyze.py +93 -0
  401. wxcli/org_health/checks.py +458 -0
  402. wxcli/org_health/collector.py +62 -0
  403. wxcli/org_health/models.py +78 -0
  404. wxcli/org_health/report.py +300 -0
  405. wxcli/output.py +101 -0
  406. wxcli-1.2.0.dist-info/METADATA +505 -0
  407. wxcli-1.2.0.dist-info/RECORD +413 -0
  408. wxcli-1.2.0.dist-info/WHEEL +5 -0
  409. wxcli-1.2.0.dist-info/entry_points.txt +2 -0
  410. wxcli-1.2.0.dist-info/licenses/LICENSE +191 -0
  411. wxcli-1.2.0.dist-info/scm_file_list.json +751 -0
  412. wxcli-1.2.0.dist-info/scm_version.json +8 -0
  413. wxcli-1.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1878 @@
1
+ import json
2
+ import typer
3
+ from wxcli.auth import get_api
4
+ from wxcli.errors import WebexError, handle_rest_error
5
+ from wxcli.output import print_table, print_json
6
+ from wxcli.config import get_org_id
7
+
8
+
9
+ app = typer.Typer(help="Manage Webex Calling call-queue.")
10
+
11
+
12
+ @app.command("list")
13
+ def cmd_list(
14
+ location_id: str = typer.Option(None, "--location-id", help="Returns the list of call queues in this location."),
15
+ name: str = typer.Option(None, "--name", help="Returns only the call queues matching the given name."),
16
+ phone_number: str = typer.Option(None, "--phone-number", help="Returns only the call queues matching the given primary phon"),
17
+ department_id: str = typer.Option(None, "--department-id", help="Returns only call queues matching the given department ID."),
18
+ department_name: str = typer.Option(None, "--department-name", help="Returns only call queues matching the given department name."),
19
+ has_cx_essentials: str = typer.Option(None, "--has-cx-essentials", help="Returns only the list of call queues with Customer Assist li"),
20
+ digital_inbox_enabled: str = typer.Option(None, "--digital-inbox-enabled", help="Returns only the list of call queues with digital inbox enab"),
21
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
22
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
23
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
24
+ debug: bool = typer.Option(False, "--debug"),
25
+ ):
26
+ """Read the List of Call Queues with Customer Assist."""
27
+ api = get_api(debug=debug)
28
+ url = f"https://webexapis.com/v1/telephony/config/queues"
29
+ params = {}
30
+ if location_id is not None:
31
+ params["locationId"] = location_id
32
+ if name is not None:
33
+ params["name"] = name
34
+ if phone_number is not None:
35
+ params["phoneNumber"] = phone_number
36
+ if department_id is not None:
37
+ params["departmentId"] = department_id
38
+ if department_name is not None:
39
+ params["departmentName"] = department_name
40
+ if has_cx_essentials is not None:
41
+ params["hasCxEssentials"] = has_cx_essentials
42
+ if digital_inbox_enabled is not None:
43
+ params["digitalInboxEnabled"] = digital_inbox_enabled
44
+ if limit > 0:
45
+ params["max"] = limit
46
+ if offset > 0:
47
+ params["start"] = offset
48
+ org_id = get_org_id()
49
+ if org_id is not None:
50
+ params["orgId"] = org_id
51
+ try:
52
+ if limit > 0:
53
+ result = api.session.rest_get(url, params=params)
54
+ result = result or {}
55
+ items = result.get("queues", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
56
+ else:
57
+ items = list(api.session.follow_pagination(url=url, params=params, item_key="queues"))
58
+ except WebexError as e:
59
+ handle_rest_error(e)
60
+ if output == "json":
61
+ print_json(items)
62
+ else:
63
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
64
+
65
+
66
+
67
+ @app.command("show-org-settings")
68
+ def show_org_settings(
69
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
70
+ debug: bool = typer.Option(False, "--debug"),
71
+ ):
72
+ """Get Call Queue Settings."""
73
+ api = get_api(debug=debug)
74
+ url = f"https://webexapis.com/v1/telephony/config/queues/settings"
75
+ params = {}
76
+ org_id = get_org_id()
77
+ if org_id is not None:
78
+ params["orgId"] = org_id
79
+ try:
80
+ result = api.session.rest_get(url, params=params)
81
+ except WebexError as e:
82
+ handle_rest_error(e)
83
+ if output == "json":
84
+ print_json(result)
85
+ else:
86
+ if isinstance(result, dict):
87
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
88
+ elif isinstance(result, list):
89
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
90
+ else:
91
+ print_json(result)
92
+
93
+
94
+
95
+ @app.command("update-org-settings")
96
+ def update_org_settings(
97
+ maintain_queue_position_for_sim_ring_enabled: bool = typer.Option(None, "--maintain-queue-position-for-sim-ring-enabled/--no-maintain-queue-position-for-sim-ring-enabled", help="Indicates whether callers keep their queue position when sim"),
98
+ force_agent_unavailable_on_bounced_enabled: bool = typer.Option(None, "--force-agent-unavailable-on-bounced-enabled/--no-force-agent-unavailable-on-bounced-enabled", help="Indicates whether Customer Assist agents are changed to unav"),
99
+ play_tone_to_agent_for_barge_in_enabled: bool = typer.Option(None, "--play-tone-to-agent-for-barge-in-enabled/--no-play-tone-to-agent-for-barge-in-enabled", help="Organization-wide default that plays a tone to agents when a"),
100
+ play_tone_to_agent_for_silent_monitoring_enabled: bool = typer.Option(None, "--play-tone-to-agent-for-silent-monitoring-enabled/--no-play-tone-to-agent-for-silent-monitoring-enabled", help="Organization-wide default that plays a tone to agents when a"),
101
+ play_tone_to_agent_for_supervisor_coaching_enabled: bool = typer.Option(None, "--play-tone-to-agent-for-supervisor-coaching-enabled/--no-play-tone-to-agent-for-supervisor-coaching-enabled", help="Organization-wide default that plays a tone to agents when a"),
102
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
103
+ debug: bool = typer.Option(False, "--debug"),
104
+ ):
105
+ """Update Call Queue Settings."""
106
+ api = get_api(debug=debug)
107
+ url = f"https://webexapis.com/v1/telephony/config/queues/settings"
108
+ params = {}
109
+ org_id = get_org_id()
110
+ if org_id is not None:
111
+ params["orgId"] = org_id
112
+ if json_body:
113
+ body = json.loads(json_body)
114
+ else:
115
+ body = {}
116
+ if maintain_queue_position_for_sim_ring_enabled is not None:
117
+ body["maintainQueuePositionForSimRingEnabled"] = maintain_queue_position_for_sim_ring_enabled
118
+ if force_agent_unavailable_on_bounced_enabled is not None:
119
+ body["forceAgentUnavailableOnBouncedEnabled"] = force_agent_unavailable_on_bounced_enabled
120
+ if play_tone_to_agent_for_barge_in_enabled is not None:
121
+ body["playToneToAgentForBargeInEnabled"] = play_tone_to_agent_for_barge_in_enabled
122
+ if play_tone_to_agent_for_silent_monitoring_enabled is not None:
123
+ body["playToneToAgentForSilentMonitoringEnabled"] = play_tone_to_agent_for_silent_monitoring_enabled
124
+ if play_tone_to_agent_for_supervisor_coaching_enabled is not None:
125
+ body["playToneToAgentForSupervisorCoachingEnabled"] = play_tone_to_agent_for_supervisor_coaching_enabled
126
+ try:
127
+ result = api.session.rest_put(url, json=body, params=params)
128
+ except WebexError as e:
129
+ handle_rest_error(e)
130
+ typer.echo(f"Updated.")
131
+
132
+
133
+
134
+ @app.command("create")
135
+ def create(
136
+ location_id: str = typer.Argument(help="locationId"),
137
+ has_cx_essentials: str = typer.Option(None, "--has-cx-essentials", help="Creates a Customer Assist call queue, when `true`. This requ"),
138
+ name: str = typer.Option(None, "--name", help="(required) Unique name for the call queue."),
139
+ phone_number: str = typer.Option(None, "--phone-number", help="Primary phone number of the call queue. Either a `phoneNumbe"),
140
+ extension: str = typer.Option(None, "--extension", help="Primary phone extension of the call queue. Either a `phoneNu"),
141
+ language_code: str = typer.Option(None, "--language-code", help="Language code."),
142
+ first_name: str = typer.Option(None, "--first-name", help="First name to be shown when calls are forwarded out of this"),
143
+ last_name: str = typer.Option(None, "--last-name", help="Last name to be shown when calls are forwarded out of this c"),
144
+ time_zone: str = typer.Option(None, "--time-zone", help="Time zone for the call queue."),
145
+ calling_line_id_policy: str = typer.Option(None, "--calling-line-id-policy", help="Choices: DIRECT_LINE, LOCATION_NUMBER, CUSTOM"),
146
+ calling_line_id_phone_number: str = typer.Option(None, "--calling-line-id-phone-number", help="Calling line ID Phone number which will be shown if CUSTOM i"),
147
+ allow_agent_join_enabled: bool = typer.Option(None, "--allow-agent-join-enabled/--no-allow-agent-join-enabled", help="Whether or not to allow agents to join or unjoin a queue."),
148
+ phone_number_for_outgoing_calls_enabled: bool = typer.Option(None, "--phone-number-for-outgoing-calls-enabled/--no-phone-number-for-outgoing-calls-enabled", help="When `true`, indicates that the agent's configuration allows"),
149
+ dial_by_name: str = typer.Option(None, "--dial-by-name", help="The name to be used for dial by name functions. Characters o"),
150
+ digital_inbox_enabled: bool = typer.Option(None, "--digital-inbox-enabled/--no-digital-inbox-enabled", help="Digital Inbox enabled for Queue. This field is applicable fo"),
151
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
152
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
153
+ debug: bool = typer.Option(False, "--debug"),
154
+ ):
155
+ """Create a Call Queue with Customer Assist\n\nExample --json-body:\n '{"name":"...","callPolicies":{"routingType":"PRIORITY_BASED","policy":"CIRCULAR","callBounce":{"callBounceEnabled":"...","callBounceMaxRings":"...","agentUnavailableEnabled":"...","alertAgentEnabled":"...","alertAgentMaxSeconds":"...","callBounceOnHoldEnabled":"...","callBounceOnHoldMaxSeconds":"..."},"distinctiveRing":{"enabled":"...","ringPattern":"..."}},"queueSettings":{"queueSize":0,"overflow":{"action":"...","greeting":"...","sendToVoicemail":"...","transferNumber":"...","overflowAfterWaitEnabled":"...","overflowAfterWaitTime":"...","playOverflowGreetingEnabled":"...","audioAnnouncementFiles":"..."},"callOfferToneEnabled":true,"resetCallStatisticsEnabled":true,"welcomeMessage":{"greeting":"...","enabled":"...","alwaysEnabled":"...","audioAnnouncementFiles":"..."},"waitMessage":{"waitMode":"...","enabled":"...","handlingTime":"...","defaultHandlingTime":"...","queuePosition":"...","highVolumeMessageEnabled":"...","estimatedWaitingTime":"...","callbackOptionEnabled":"..."},"comfortMessage":{"greeting":"...","enabled":"...","timeBetweenMessages":"...","audioAnnouncementFiles":"..."},"comfortMessageBypass":{"greeting":"...","enabled":"...","callWaitingAgeThreshold":"...","audioAnnouncementFiles":"..."}},"agents":[{"id":"...","weight":"...","skillLevel":"..."}],"phoneNumber":"...","extension":"...","languageCode":"...","firstName":"..."}'."""
156
+ api = get_api(debug=debug)
157
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues"
158
+ params = {}
159
+ if has_cx_essentials is not None:
160
+ params["hasCxEssentials"] = has_cx_essentials
161
+ org_id = get_org_id()
162
+ if org_id is not None:
163
+ params["orgId"] = org_id
164
+ if json_body:
165
+ body = json.loads(json_body)
166
+ else:
167
+ body = {}
168
+ if name is not None:
169
+ body["name"] = name
170
+ if phone_number is not None:
171
+ body["phoneNumber"] = phone_number
172
+ if extension is not None:
173
+ body["extension"] = extension
174
+ if language_code is not None:
175
+ body["languageCode"] = language_code
176
+ if first_name is not None:
177
+ body["firstName"] = first_name
178
+ if last_name is not None:
179
+ body["lastName"] = last_name
180
+ if time_zone is not None:
181
+ body["timeZone"] = time_zone
182
+ if calling_line_id_policy is not None:
183
+ body["callingLineIdPolicy"] = calling_line_id_policy
184
+ if calling_line_id_phone_number is not None:
185
+ body["callingLineIdPhoneNumber"] = calling_line_id_phone_number
186
+ if allow_agent_join_enabled is not None:
187
+ body["allowAgentJoinEnabled"] = allow_agent_join_enabled
188
+ if phone_number_for_outgoing_calls_enabled is not None:
189
+ body["phoneNumberForOutgoingCallsEnabled"] = phone_number_for_outgoing_calls_enabled
190
+ if dial_by_name is not None:
191
+ body["dialByName"] = dial_by_name
192
+ if digital_inbox_enabled is not None:
193
+ body["digitalInboxEnabled"] = digital_inbox_enabled
194
+ _missing = [f for f in ['name'] if f not in body or body[f] is None]
195
+ if _missing:
196
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
197
+ raise typer.Exit(1)
198
+ try:
199
+ result = api.session.rest_post(url, json=body, params=params)
200
+ except WebexError as e:
201
+ handle_rest_error(e)
202
+ if output == "json":
203
+ print_json(result)
204
+ elif isinstance(result, dict) and "id" in result:
205
+ typer.echo(f"Created: {result['id']}")
206
+ elif not result or result == {}:
207
+ typer.echo("Created.")
208
+ else:
209
+ print_json(result)
210
+
211
+
212
+
213
+ @app.command("show")
214
+ def show(
215
+ location_id: str = typer.Argument(help="locationId"),
216
+ queue_id: str = typer.Argument(help="queueId"),
217
+ has_cx_essentials: str = typer.Option(None, "--has-cx-essentials", help="Must be set to `true`, to view the details of a call queue w"),
218
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
219
+ debug: bool = typer.Option(False, "--debug"),
220
+ ):
221
+ """Get Details for a Call Queue with Customer Assist."""
222
+ api = get_api(debug=debug)
223
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}"
224
+ params = {}
225
+ if has_cx_essentials is not None:
226
+ params["hasCxEssentials"] = has_cx_essentials
227
+ org_id = get_org_id()
228
+ if org_id is not None:
229
+ params["orgId"] = org_id
230
+ try:
231
+ result = api.session.rest_get(url, params=params)
232
+ except WebexError as e:
233
+ handle_rest_error(e)
234
+ if output == "json":
235
+ print_json(result)
236
+ else:
237
+ if isinstance(result, dict):
238
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
239
+ elif isinstance(result, list):
240
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
241
+ else:
242
+ print_json(result)
243
+
244
+
245
+
246
+ @app.command("update")
247
+ def update(
248
+ location_id: str = typer.Argument(help="locationId"),
249
+ queue_id: str = typer.Argument(help="queueId"),
250
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="Whether or not the call queue is enabled."),
251
+ name: str = typer.Option(None, "--name", help="Unique name for the call queue."),
252
+ language_code: str = typer.Option(None, "--language-code", help="Language code."),
253
+ first_name: str = typer.Option(None, "--first-name", help="First name to be shown when calls are forwarded out of this"),
254
+ last_name: str = typer.Option(None, "--last-name", help="Last name to be shown when calls are forwarded out of this c"),
255
+ time_zone: str = typer.Option(None, "--time-zone", help="Time zone for the hunt group."),
256
+ phone_number: str = typer.Option(None, "--phone-number", help="Primary phone number of the call queue."),
257
+ extension: str = typer.Option(None, "--extension", help="Extension of the call queue."),
258
+ calling_line_id_policy: str = typer.Option(None, "--calling-line-id-policy", help="Choices: DIRECT_LINE, LOCATION_NUMBER, CUSTOM"),
259
+ calling_line_id_phone_number: str = typer.Option(None, "--calling-line-id-phone-number", help="Calling line ID Phone number which will be shown if CUSTOM i"),
260
+ allow_call_waiting_for_agents_enabled: bool = typer.Option(None, "--allow-call-waiting-for-agents-enabled/--no-allow-call-waiting-for-agents-enabled", help="Flag to indicate whether call waiting is enabled for agents."),
261
+ allow_agent_join_enabled: bool = typer.Option(None, "--allow-agent-join-enabled/--no-allow-agent-join-enabled", help="Whether or not to allow agents to join or unjoin a queue."),
262
+ phone_number_for_outgoing_calls_enabled: bool = typer.Option(None, "--phone-number-for-outgoing-calls-enabled/--no-phone-number-for-outgoing-calls-enabled", help="When `true`, indicates that the agent's configuration allows"),
263
+ dial_by_name: str = typer.Option(None, "--dial-by-name", help="Sets or clears the name to be used for dial by name function"),
264
+ digital_inbox_enabled: bool = typer.Option(None, "--digital-inbox-enabled/--no-digital-inbox-enabled", help="Digital Inbox enabled for Queue. This field is applicable fo"),
265
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
266
+ debug: bool = typer.Option(False, "--debug"),
267
+ ):
268
+ """Update a Call Queue\n\nExample --json-body:\n '{"queueSettings":{"queueSize":0,"overflow":{"action":"...","greeting":"...","sendToVoicemail":"...","transferNumber":"...","overflowAfterWaitEnabled":"...","overflowAfterWaitTime":"...","playOverflowGreetingEnabled":"...","audioAnnouncementFiles":"..."},"callOfferToneEnabled":true,"resetCallStatisticsEnabled":true,"welcomeMessage":{"greeting":"...","enabled":"...","alwaysEnabled":"...","audioAnnouncementFiles":"..."},"waitMessage":{"waitMode":"...","enabled":"...","handlingTime":"...","defaultHandlingTime":"...","queuePosition":"...","highVolumeMessageEnabled":"...","estimatedWaitingTime":"...","callbackOptionEnabled":"..."},"comfortMessage":{"greeting":"...","enabled":"...","timeBetweenMessages":"...","audioAnnouncementFiles":"..."},"comfortMessageBypass":{"greeting":"...","enabled":"...","callWaitingAgeThreshold":"...","audioAnnouncementFiles":"..."}},"enabled":true,"name":"...","languageCode":"...","firstName":"...","lastName":"...","timeZone":"...","phoneNumber":"..."}'."""
269
+ api = get_api(debug=debug)
270
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}"
271
+ params = {}
272
+ org_id = get_org_id()
273
+ if org_id is not None:
274
+ params["orgId"] = org_id
275
+ if json_body:
276
+ body = json.loads(json_body)
277
+ else:
278
+ body = {}
279
+ if enabled is not None:
280
+ body["enabled"] = enabled
281
+ if name is not None:
282
+ body["name"] = name
283
+ if language_code is not None:
284
+ body["languageCode"] = language_code
285
+ if first_name is not None:
286
+ body["firstName"] = first_name
287
+ if last_name is not None:
288
+ body["lastName"] = last_name
289
+ if time_zone is not None:
290
+ body["timeZone"] = time_zone
291
+ if phone_number is not None:
292
+ body["phoneNumber"] = phone_number
293
+ if extension is not None:
294
+ body["extension"] = extension
295
+ if calling_line_id_policy is not None:
296
+ body["callingLineIdPolicy"] = calling_line_id_policy
297
+ if calling_line_id_phone_number is not None:
298
+ body["callingLineIdPhoneNumber"] = calling_line_id_phone_number
299
+ if allow_call_waiting_for_agents_enabled is not None:
300
+ body["allowCallWaitingForAgentsEnabled"] = allow_call_waiting_for_agents_enabled
301
+ if allow_agent_join_enabled is not None:
302
+ body["allowAgentJoinEnabled"] = allow_agent_join_enabled
303
+ if phone_number_for_outgoing_calls_enabled is not None:
304
+ body["phoneNumberForOutgoingCallsEnabled"] = phone_number_for_outgoing_calls_enabled
305
+ if dial_by_name is not None:
306
+ body["dialByName"] = dial_by_name
307
+ if digital_inbox_enabled is not None:
308
+ body["digitalInboxEnabled"] = digital_inbox_enabled
309
+ try:
310
+ result = api.session.rest_put(url, json=body, params=params)
311
+ except WebexError as e:
312
+ handle_rest_error(e)
313
+ typer.echo(f"Updated.")
314
+
315
+
316
+
317
+ @app.command("delete")
318
+ def delete(
319
+ location_id: str = typer.Argument(help="locationId"),
320
+ queue_id: str = typer.Argument(help="queueId"),
321
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
322
+ debug: bool = typer.Option(False, "--debug"),
323
+ ):
324
+ """Delete a Call Queue."""
325
+ if not force:
326
+ typer.confirm(f"Delete {queue_id}?", abort=True)
327
+ api = get_api(debug=debug)
328
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}"
329
+ params = {}
330
+ org_id = get_org_id()
331
+ if org_id is not None:
332
+ params["orgId"] = org_id
333
+ try:
334
+ api.session.rest_delete(url, params=params)
335
+ except WebexError as e:
336
+ handle_rest_error(e)
337
+ typer.echo(f"Deleted: {queue_id}")
338
+
339
+
340
+
341
+ @app.command("list-announcements")
342
+ def list_announcements(
343
+ location_id: str = typer.Argument(help="locationId"),
344
+ queue_id: str = typer.Argument(help="queueId"),
345
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
346
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
347
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
348
+ debug: bool = typer.Option(False, "--debug"),
349
+ ):
350
+ """Read the List of Call Queue Announcement Files."""
351
+ api = get_api(debug=debug)
352
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/announcements"
353
+ params = {}
354
+ if limit > 0:
355
+ params["max"] = limit
356
+ if offset > 0:
357
+ params["start"] = offset
358
+ org_id = get_org_id()
359
+ if org_id is not None:
360
+ params["orgId"] = org_id
361
+ result = None
362
+ try:
363
+ result = api.session.rest_get(url, params=params)
364
+ except WebexError as e:
365
+ handle_rest_error(e)
366
+ result = result or []
367
+ items = result.get("announcements", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
368
+ if output == "json":
369
+ print_json(items)
370
+ else:
371
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
372
+
373
+
374
+
375
+ @app.command("delete-announcements")
376
+ def delete_announcements(
377
+ location_id: str = typer.Argument(help="locationId"),
378
+ queue_id: str = typer.Argument(help="queueId"),
379
+ file_name: str = typer.Argument(help="fileName"),
380
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
381
+ debug: bool = typer.Option(False, "--debug"),
382
+ ):
383
+ """Delete a Call Queue Announcement File."""
384
+ if not force:
385
+ typer.confirm(f"Delete {file_name}?", abort=True)
386
+ api = get_api(debug=debug)
387
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/announcements/{file_name}"
388
+ params = {}
389
+ org_id = get_org_id()
390
+ if org_id is not None:
391
+ params["orgId"] = org_id
392
+ try:
393
+ api.session.rest_delete(url, params=params)
394
+ except WebexError as e:
395
+ handle_rest_error(e)
396
+ typer.echo(f"Deleted: {file_name}")
397
+
398
+
399
+
400
+ @app.command("show-call-forwarding")
401
+ def show_call_forwarding(
402
+ location_id: str = typer.Argument(help="locationId"),
403
+ queue_id: str = typer.Argument(help="queueId"),
404
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
405
+ debug: bool = typer.Option(False, "--debug"),
406
+ ):
407
+ """Get Call Forwarding Settings for a Call Queue."""
408
+ api = get_api(debug=debug)
409
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/callForwarding"
410
+ params = {}
411
+ org_id = get_org_id()
412
+ if org_id is not None:
413
+ params["orgId"] = org_id
414
+ try:
415
+ result = api.session.rest_get(url, params=params)
416
+ except WebexError as e:
417
+ handle_rest_error(e)
418
+ if output == "json":
419
+ print_json(result)
420
+ else:
421
+ if isinstance(result, dict):
422
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
423
+ elif isinstance(result, list):
424
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
425
+ else:
426
+ print_json(result)
427
+
428
+
429
+
430
+ @app.command("update-call-forwarding")
431
+ def update_call_forwarding(
432
+ location_id: str = typer.Argument(help="locationId"),
433
+ queue_id: str = typer.Argument(help="queueId"),
434
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
435
+ debug: bool = typer.Option(False, "--debug"),
436
+ ):
437
+ """Update Call Forwarding Settings for a Call Queue\n\nExample --json-body:\n '{"callForwarding":{"always":{"enabled":"...","destination":"...","ringReminderEnabled":"...","destinationVoicemailEnabled":"..."},"selective":{"enabled":"...","destination":"...","ringReminderEnabled":"...","destinationVoicemailEnabled":"..."},"rules":["..."],"operatingModes":{"enabled":"...","modes":"..."}}}'."""
438
+ api = get_api(debug=debug)
439
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/callForwarding"
440
+ params = {}
441
+ org_id = get_org_id()
442
+ if org_id is not None:
443
+ params["orgId"] = org_id
444
+ if json_body:
445
+ body = json.loads(json_body)
446
+ else:
447
+ body = {}
448
+ try:
449
+ result = api.session.rest_put(url, json=body, params=params)
450
+ except WebexError as e:
451
+ handle_rest_error(e)
452
+ typer.echo(f"Updated.")
453
+
454
+
455
+
456
+ @app.command("create-selective-rules")
457
+ def create_selective_rules(
458
+ location_id: str = typer.Argument(help="locationId"),
459
+ queue_id: str = typer.Argument(help="queueId"),
460
+ name: str = typer.Option(None, "--name", help="(required) Unique name for the selective rule in the hunt group."),
461
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="Reflects if rule is enabled."),
462
+ holiday_schedule: str = typer.Option(None, "--holiday-schedule", help="Name of the location's holiday schedule which determines whe"),
463
+ business_schedule: str = typer.Option(None, "--business-schedule", help="Name of the location's business schedule which determines wh"),
464
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
465
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
466
+ debug: bool = typer.Option(False, "--debug"),
467
+ ):
468
+ """Create a Selective Call Forwarding Rule for a Call Queue\n\nExample --json-body:\n '{"name":"...","callsFrom":{"selection":"ANY","customNumbers":{"privateNumberEnabled":"...","unavailableNumberEnabled":"...","numbers":"..."}},"callsTo":{"numbers":["..."]},"enabled":true,"holidaySchedule":"...","businessSchedule":"...","forwardTo":{"selection":"FORWARD_TO_DEFAULT_NUMBER","phoneNumber":"..."}}'."""
469
+ api = get_api(debug=debug)
470
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/callForwarding/selectiveRules"
471
+ params = {}
472
+ org_id = get_org_id()
473
+ if org_id is not None:
474
+ params["orgId"] = org_id
475
+ if json_body:
476
+ body = json.loads(json_body)
477
+ else:
478
+ body = {}
479
+ if name is not None:
480
+ body["name"] = name
481
+ if enabled is not None:
482
+ body["enabled"] = enabled
483
+ if holiday_schedule is not None:
484
+ body["holidaySchedule"] = holiday_schedule
485
+ if business_schedule is not None:
486
+ body["businessSchedule"] = business_schedule
487
+ _missing = [f for f in ['name'] if f not in body or body[f] is None]
488
+ if _missing:
489
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
490
+ raise typer.Exit(1)
491
+ try:
492
+ result = api.session.rest_post(url, json=body, params=params)
493
+ except WebexError as e:
494
+ handle_rest_error(e)
495
+ if output == "json":
496
+ print_json(result)
497
+ elif isinstance(result, dict) and "id" in result:
498
+ typer.echo(f"Created: {result['id']}")
499
+ elif not result or result == {}:
500
+ typer.echo("Created.")
501
+ else:
502
+ print_json(result)
503
+
504
+
505
+
506
+ @app.command("show-selective-rules")
507
+ def show_selective_rules(
508
+ location_id: str = typer.Argument(help="locationId"),
509
+ queue_id: str = typer.Argument(help="queueId"),
510
+ rule_id: str = typer.Argument(help="ruleId"),
511
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
512
+ debug: bool = typer.Option(False, "--debug"),
513
+ ):
514
+ """Get Selective Call Forwarding Rule for a Call Queue."""
515
+ api = get_api(debug=debug)
516
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/callForwarding/selectiveRules/{rule_id}"
517
+ params = {}
518
+ org_id = get_org_id()
519
+ if org_id is not None:
520
+ params["orgId"] = org_id
521
+ try:
522
+ result = api.session.rest_get(url, params=params)
523
+ except WebexError as e:
524
+ handle_rest_error(e)
525
+ if output == "json":
526
+ print_json(result)
527
+ else:
528
+ if isinstance(result, dict):
529
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
530
+ elif isinstance(result, list):
531
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
532
+ else:
533
+ print_json(result)
534
+
535
+
536
+
537
+ @app.command("update-selective-rules")
538
+ def update_selective_rules(
539
+ location_id: str = typer.Argument(help="locationId"),
540
+ queue_id: str = typer.Argument(help="queueId"),
541
+ rule_id: str = typer.Argument(help="ruleId"),
542
+ name: str = typer.Option(None, "--name", help="Unique name for the selective rule in the hunt group."),
543
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="Reflects if rule is enabled."),
544
+ holiday_schedule: str = typer.Option(None, "--holiday-schedule", help="Name of the location's holiday schedule which determines whe"),
545
+ business_schedule: str = typer.Option(None, "--business-schedule", help="Name of the location's business schedule which determines wh"),
546
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
547
+ debug: bool = typer.Option(False, "--debug"),
548
+ ):
549
+ """Update a Selective Call Forwarding Rule for a Call Queue\n\nExample --json-body:\n '{"name":"...","enabled":true,"holidaySchedule":"...","businessSchedule":"...","forwardTo":{"selection":"FORWARD_TO_DEFAULT_NUMBER","phoneNumber":"..."},"callsFrom":{"selection":"ANY","customNumbers":{"privateNumberEnabled":"...","unavailableNumberEnabled":"...","numbers":"..."}},"callsTo":{"numbers":["..."]}}'."""
550
+ api = get_api(debug=debug)
551
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/callForwarding/selectiveRules/{rule_id}"
552
+ params = {}
553
+ org_id = get_org_id()
554
+ if org_id is not None:
555
+ params["orgId"] = org_id
556
+ if json_body:
557
+ body = json.loads(json_body)
558
+ else:
559
+ body = {}
560
+ if name is not None:
561
+ body["name"] = name
562
+ if enabled is not None:
563
+ body["enabled"] = enabled
564
+ if holiday_schedule is not None:
565
+ body["holidaySchedule"] = holiday_schedule
566
+ if business_schedule is not None:
567
+ body["businessSchedule"] = business_schedule
568
+ try:
569
+ result = api.session.rest_put(url, json=body, params=params)
570
+ except WebexError as e:
571
+ handle_rest_error(e)
572
+ typer.echo(f"Updated.")
573
+
574
+
575
+
576
+ @app.command("delete-selective-rules")
577
+ def delete_selective_rules(
578
+ location_id: str = typer.Argument(help="locationId"),
579
+ queue_id: str = typer.Argument(help="queueId"),
580
+ rule_id: str = typer.Argument(help="ruleId"),
581
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
582
+ debug: bool = typer.Option(False, "--debug"),
583
+ ):
584
+ """Delete a Selective Call Forwarding Rule for a Call Queue."""
585
+ if not force:
586
+ typer.confirm(f"Delete {rule_id}?", abort=True)
587
+ api = get_api(debug=debug)
588
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/callForwarding/selectiveRules/{rule_id}"
589
+ params = {}
590
+ org_id = get_org_id()
591
+ if org_id is not None:
592
+ params["orgId"] = org_id
593
+ try:
594
+ api.session.rest_delete(url, params=params)
595
+ except WebexError as e:
596
+ handle_rest_error(e)
597
+ typer.echo(f"Deleted: {rule_id}")
598
+
599
+
600
+
601
+ @app.command("list-holiday-service")
602
+ def list_holiday_service(
603
+ location_id: str = typer.Argument(help="locationId"),
604
+ queue_id: str = typer.Argument(help="queueId"),
605
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
606
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
607
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
608
+ debug: bool = typer.Option(False, "--debug"),
609
+ ):
610
+ """Get Details for a Call Queue Holiday Service."""
611
+ api = get_api(debug=debug)
612
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/holidayService"
613
+ params = {}
614
+ if limit > 0:
615
+ params["max"] = limit
616
+ if offset > 0:
617
+ params["start"] = offset
618
+ org_id = get_org_id()
619
+ if org_id is not None:
620
+ params["orgId"] = org_id
621
+ result = None
622
+ try:
623
+ result = api.session.rest_get(url, params=params)
624
+ except WebexError as e:
625
+ handle_rest_error(e)
626
+ result = result or []
627
+ items = result.get("audioFiles", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
628
+ if output == "json":
629
+ print_json(items)
630
+ else:
631
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
632
+
633
+
634
+
635
+ @app.command("update-holiday-service")
636
+ def update_holiday_service(
637
+ location_id: str = typer.Argument(help="locationId"),
638
+ queue_id: str = typer.Argument(help="queueId"),
639
+ holiday_service_enabled: bool = typer.Option(None, "--holiday-service-enabled/--no-holiday-service-enabled", help="Enable or Disable the call queue holiday service routing pol"),
640
+ action: str = typer.Option(None, "--action", help="Choices: BUSY, TRANSFER"),
641
+ holiday_schedule_level: str = typer.Option(None, "--holiday-schedule-level", help="Choices: LOCATION, ORGANIZATION"),
642
+ holiday_schedule_name: str = typer.Option(None, "--holiday-schedule-name", help="Name of the schedule configured for a holiday service as one"),
643
+ transfer_phone_number: str = typer.Option(None, "--transfer-phone-number", help="Call gets transferred to this number when action is set to `"),
644
+ play_announcement_before_enabled: bool = typer.Option(None, "--play-announcement-before-enabled/--no-play-announcement-before-enabled", help="Indicates whether an announcement plays to callers before th"),
645
+ audio_message_selection: str = typer.Option(None, "--audio-message-selection", help="Choices: DEFAULT, CUSTOM"),
646
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
647
+ debug: bool = typer.Option(False, "--debug"),
648
+ ):
649
+ """Update a Call Queue Holiday Service\n\nExample --json-body:\n '{"holidayServiceEnabled":true,"action":"BUSY","holidayScheduleLevel":"LOCATION","playAnnouncementBeforeEnabled":true,"audioMessageSelection":"DEFAULT","holidayScheduleName":"...","transferPhoneNumber":"...","audioFiles":[{"id":"...","fileName":"...","mediaFileType":"...","level":"..."}]}'."""
650
+ api = get_api(debug=debug)
651
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/holidayService"
652
+ params = {}
653
+ org_id = get_org_id()
654
+ if org_id is not None:
655
+ params["orgId"] = org_id
656
+ if json_body:
657
+ body = json.loads(json_body)
658
+ else:
659
+ body = {}
660
+ if holiday_service_enabled is not None:
661
+ body["holidayServiceEnabled"] = holiday_service_enabled
662
+ if action is not None:
663
+ body["action"] = action
664
+ if holiday_schedule_level is not None:
665
+ body["holidayScheduleLevel"] = holiday_schedule_level
666
+ if holiday_schedule_name is not None:
667
+ body["holidayScheduleName"] = holiday_schedule_name
668
+ if transfer_phone_number is not None:
669
+ body["transferPhoneNumber"] = transfer_phone_number
670
+ if play_announcement_before_enabled is not None:
671
+ body["playAnnouncementBeforeEnabled"] = play_announcement_before_enabled
672
+ if audio_message_selection is not None:
673
+ body["audioMessageSelection"] = audio_message_selection
674
+ try:
675
+ result = api.session.rest_put(url, json=body, params=params)
676
+ except WebexError as e:
677
+ handle_rest_error(e)
678
+ typer.echo(f"Updated.")
679
+
680
+
681
+
682
+ @app.command("list-night-service")
683
+ def list_night_service(
684
+ location_id: str = typer.Argument(help="locationId"),
685
+ queue_id: str = typer.Argument(help="queueId"),
686
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
687
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
688
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
689
+ debug: bool = typer.Option(False, "--debug"),
690
+ ):
691
+ """Get Details for a Call Queue Night Service."""
692
+ api = get_api(debug=debug)
693
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/nightService"
694
+ params = {}
695
+ if limit > 0:
696
+ params["max"] = limit
697
+ if offset > 0:
698
+ params["start"] = offset
699
+ org_id = get_org_id()
700
+ if org_id is not None:
701
+ params["orgId"] = org_id
702
+ result = None
703
+ try:
704
+ result = api.session.rest_get(url, params=params)
705
+ except WebexError as e:
706
+ handle_rest_error(e)
707
+ result = result or []
708
+ items = result.get("audioFiles", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
709
+ if output == "json":
710
+ print_json(items)
711
+ else:
712
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
713
+
714
+
715
+
716
+ @app.command("update-night-service")
717
+ def update_night_service(
718
+ location_id: str = typer.Argument(help="locationId"),
719
+ queue_id: str = typer.Argument(help="queueId"),
720
+ night_service_enabled: bool = typer.Option(None, "--night-service-enabled/--no-night-service-enabled", help="Enable or disable call queue night service routing policy."),
721
+ action: str = typer.Option(None, "--action", help="Choices: BUSY, TRANSFER"),
722
+ transfer_phone_number: str = typer.Option(None, "--transfer-phone-number", help="Call gets transferred to this number when action is set to `"),
723
+ play_announcement_before_enabled: bool = typer.Option(None, "--play-announcement-before-enabled/--no-play-announcement-before-enabled", help="Indicates whether an announcement plays to callers before th"),
724
+ announcement_mode: str = typer.Option(None, "--announcement-mode", help="Choices: NORMAL, MANUAL"),
725
+ audio_message_selection: str = typer.Option(None, "--audio-message-selection", help="Choices: DEFAULT, CUSTOM"),
726
+ business_hours_name: str = typer.Option(None, "--business-hours-name", help="Name of the schedule configured for a night service as one o"),
727
+ business_hours_level: str = typer.Option(None, "--business-hours-level", help="Choices: ORGANIZATION, LOCATION"),
728
+ force_night_service_enabled: bool = typer.Option(None, "--force-night-service-enabled/--no-force-night-service-enabled", help="Force night service regardless of business hour schedule."),
729
+ manual_audio_message_selection: str = typer.Option(None, "--manual-audio-message-selection", help="Choices: DEFAULT, CUSTOM"),
730
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
731
+ debug: bool = typer.Option(False, "--debug"),
732
+ ):
733
+ """Update a Call Queue Night Service\n\nExample --json-body:\n '{"nightServiceEnabled":true,"playAnnouncementBeforeEnabled":true,"announcementMode":"NORMAL","audioMessageSelection":"DEFAULT","forceNightServiceEnabled":true,"manualAudioMessageSelection":"DEFAULT","action":"BUSY","transferPhoneNumber":"..."}'."""
734
+ api = get_api(debug=debug)
735
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/nightService"
736
+ params = {}
737
+ org_id = get_org_id()
738
+ if org_id is not None:
739
+ params["orgId"] = org_id
740
+ if json_body:
741
+ body = json.loads(json_body)
742
+ else:
743
+ body = {}
744
+ if night_service_enabled is not None:
745
+ body["nightServiceEnabled"] = night_service_enabled
746
+ if action is not None:
747
+ body["action"] = action
748
+ if transfer_phone_number is not None:
749
+ body["transferPhoneNumber"] = transfer_phone_number
750
+ if play_announcement_before_enabled is not None:
751
+ body["playAnnouncementBeforeEnabled"] = play_announcement_before_enabled
752
+ if announcement_mode is not None:
753
+ body["announcementMode"] = announcement_mode
754
+ if audio_message_selection is not None:
755
+ body["audioMessageSelection"] = audio_message_selection
756
+ if business_hours_name is not None:
757
+ body["businessHoursName"] = business_hours_name
758
+ if business_hours_level is not None:
759
+ body["businessHoursLevel"] = business_hours_level
760
+ if force_night_service_enabled is not None:
761
+ body["forceNightServiceEnabled"] = force_night_service_enabled
762
+ if manual_audio_message_selection is not None:
763
+ body["manualAudioMessageSelection"] = manual_audio_message_selection
764
+ try:
765
+ result = api.session.rest_put(url, json=body, params=params)
766
+ except WebexError as e:
767
+ handle_rest_error(e)
768
+ typer.echo(f"Updated.")
769
+
770
+
771
+
772
+ @app.command("list-forced-forward")
773
+ def list_forced_forward(
774
+ location_id: str = typer.Argument(help="locationId"),
775
+ queue_id: str = typer.Argument(help="queueId"),
776
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
777
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
778
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
779
+ debug: bool = typer.Option(False, "--debug"),
780
+ ):
781
+ """Get Details for a Call Queue Forced Forward."""
782
+ api = get_api(debug=debug)
783
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/forcedForward"
784
+ params = {}
785
+ if limit > 0:
786
+ params["max"] = limit
787
+ if offset > 0:
788
+ params["start"] = offset
789
+ org_id = get_org_id()
790
+ if org_id is not None:
791
+ params["orgId"] = org_id
792
+ result = None
793
+ try:
794
+ result = api.session.rest_get(url, params=params)
795
+ except WebexError as e:
796
+ handle_rest_error(e)
797
+ result = result or []
798
+ items = result.get("audioFiles", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
799
+ if output == "json":
800
+ print_json(items)
801
+ else:
802
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
803
+
804
+
805
+
806
+ @app.command("update-forced-forward")
807
+ def update_forced_forward(
808
+ location_id: str = typer.Argument(help="locationId"),
809
+ queue_id: str = typer.Argument(help="queueId"),
810
+ forced_forward_enabled: bool = typer.Option(None, "--forced-forward-enabled/--no-forced-forward-enabled", help="Enable or disable call forced forward service routing policy"),
811
+ transfer_phone_number: str = typer.Option(None, "--transfer-phone-number", help="Call gets transferred to this number when action is set to `"),
812
+ play_announcement_before_enabled: bool = typer.Option(None, "--play-announcement-before-enabled/--no-play-announcement-before-enabled", help="Indicates whether an announcement plays to callers before th"),
813
+ audio_message_selection: str = typer.Option(None, "--audio-message-selection", help="Choices: DEFAULT, CUSTOM"),
814
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
815
+ debug: bool = typer.Option(False, "--debug"),
816
+ ):
817
+ """Update a Call Queue Forced Forward Service\n\nExample --json-body:\n '{"forcedForwardEnabled":true,"playAnnouncementBeforeEnabled":true,"audioMessageSelection":"DEFAULT","transferPhoneNumber":"...","audioFiles":[{"id":"...","fileName":"...","mediaFileType":"...","level":"..."}]}'."""
818
+ api = get_api(debug=debug)
819
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/forcedForward"
820
+ params = {}
821
+ org_id = get_org_id()
822
+ if org_id is not None:
823
+ params["orgId"] = org_id
824
+ if json_body:
825
+ body = json.loads(json_body)
826
+ else:
827
+ body = {}
828
+ if forced_forward_enabled is not None:
829
+ body["forcedForwardEnabled"] = forced_forward_enabled
830
+ if transfer_phone_number is not None:
831
+ body["transferPhoneNumber"] = transfer_phone_number
832
+ if play_announcement_before_enabled is not None:
833
+ body["playAnnouncementBeforeEnabled"] = play_announcement_before_enabled
834
+ if audio_message_selection is not None:
835
+ body["audioMessageSelection"] = audio_message_selection
836
+ try:
837
+ result = api.session.rest_put(url, json=body, params=params)
838
+ except WebexError as e:
839
+ handle_rest_error(e)
840
+ typer.echo(f"Updated.")
841
+
842
+
843
+
844
+ @app.command("list-stranded-calls")
845
+ def list_stranded_calls(
846
+ location_id: str = typer.Argument(help="locationId"),
847
+ queue_id: str = typer.Argument(help="queueId"),
848
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
849
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
850
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
851
+ debug: bool = typer.Option(False, "--debug"),
852
+ ):
853
+ """Get Details for a Call Queue Stranded Calls."""
854
+ api = get_api(debug=debug)
855
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/strandedCalls"
856
+ params = {}
857
+ if limit > 0:
858
+ params["max"] = limit
859
+ if offset > 0:
860
+ params["start"] = offset
861
+ org_id = get_org_id()
862
+ if org_id is not None:
863
+ params["orgId"] = org_id
864
+ result = None
865
+ try:
866
+ result = api.session.rest_get(url, params=params)
867
+ except WebexError as e:
868
+ handle_rest_error(e)
869
+ result = result or []
870
+ items = result.get("audioFiles", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
871
+ if output == "json":
872
+ print_json(items)
873
+ else:
874
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
875
+
876
+
877
+
878
+ @app.command("update-stranded-calls")
879
+ def update_stranded_calls(
880
+ location_id: str = typer.Argument(help="locationId"),
881
+ queue_id: str = typer.Argument(help="queueId"),
882
+ action: str = typer.Option(None, "--action", help="Choices: NONE, BUSY, TRANSFER, NIGHT_SERVICE, RINGING, ANNOUNCEMENT"),
883
+ transfer_phone_number: str = typer.Option(None, "--transfer-phone-number", help="Call gets transferred to this number when action is set to `"),
884
+ audio_message_selection: str = typer.Option(None, "--audio-message-selection", help="Choices: DEFAULT, CUSTOM"),
885
+ trigger_policy_when_all_agents_are_unreachable_enabled: bool = typer.Option(None, "--trigger-policy-when-all-agents-are-unreachable-enabled/--no-trigger-policy-when-all-agents-are-unreachable-enabled", help="Trigger stranded calls queue policy when all agents are unre"),
886
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
887
+ debug: bool = typer.Option(False, "--debug"),
888
+ ):
889
+ """Update a Call Queue Stranded Calls Service\n\nExample --json-body:\n '{"action":"NONE","audioMessageSelection":"DEFAULT","transferPhoneNumber":"...","audioFiles":[{"id":"...","fileName":"...","mediaFileType":"...","level":"..."}],"triggerPolicyWhenAllAgentsAreUnreachableEnabled":true}'."""
890
+ api = get_api(debug=debug)
891
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/strandedCalls"
892
+ params = {}
893
+ org_id = get_org_id()
894
+ if org_id is not None:
895
+ params["orgId"] = org_id
896
+ if json_body:
897
+ body = json.loads(json_body)
898
+ else:
899
+ body = {}
900
+ if action is not None:
901
+ body["action"] = action
902
+ if transfer_phone_number is not None:
903
+ body["transferPhoneNumber"] = transfer_phone_number
904
+ if audio_message_selection is not None:
905
+ body["audioMessageSelection"] = audio_message_selection
906
+ if trigger_policy_when_all_agents_are_unreachable_enabled is not None:
907
+ body["triggerPolicyWhenAllAgentsAreUnreachableEnabled"] = trigger_policy_when_all_agents_are_unreachable_enabled
908
+ try:
909
+ result = api.session.rest_put(url, json=body, params=params)
910
+ except WebexError as e:
911
+ handle_rest_error(e)
912
+ typer.echo(f"Updated.")
913
+
914
+
915
+
916
+ @app.command("list-available-numbers-queues")
917
+ def list_available_numbers_queues(
918
+ location_id: str = typer.Argument(help="locationId"),
919
+ phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
920
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
921
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
922
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
923
+ debug: bool = typer.Option(False, "--debug"),
924
+ ):
925
+ """Get Call Queue Primary Available Phone Numbers."""
926
+ api = get_api(debug=debug)
927
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/availableNumbers"
928
+ params = {}
929
+ if phone_number is not None:
930
+ params["phoneNumber"] = phone_number
931
+ if limit > 0:
932
+ params["max"] = limit
933
+ if offset > 0:
934
+ params["start"] = offset
935
+ org_id = get_org_id()
936
+ if org_id is not None:
937
+ params["orgId"] = org_id
938
+ result = None
939
+ try:
940
+ result = api.session.rest_get(url, params=params)
941
+ except WebexError as e:
942
+ handle_rest_error(e)
943
+ result = result or []
944
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
945
+ if output == "json":
946
+ print_json(items)
947
+ else:
948
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
949
+
950
+
951
+
952
+ @app.command("list-available-numbers-alternate")
953
+ def list_available_numbers_alternate(
954
+ location_id: str = typer.Argument(help="locationId"),
955
+ phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
956
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
957
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
958
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
959
+ debug: bool = typer.Option(False, "--debug"),
960
+ ):
961
+ """Get Call Queue Alternate Available Phone Numbers."""
962
+ api = get_api(debug=debug)
963
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/alternate/availableNumbers"
964
+ params = {}
965
+ if phone_number is not None:
966
+ params["phoneNumber"] = phone_number
967
+ if limit > 0:
968
+ params["max"] = limit
969
+ if offset > 0:
970
+ params["start"] = offset
971
+ org_id = get_org_id()
972
+ if org_id is not None:
973
+ params["orgId"] = org_id
974
+ result = None
975
+ try:
976
+ result = api.session.rest_get(url, params=params)
977
+ except WebexError as e:
978
+ handle_rest_error(e)
979
+ result = result or []
980
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
981
+ if output == "json":
982
+ print_json(items)
983
+ else:
984
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
985
+
986
+
987
+
988
+ @app.command("list-available-numbers-call-forwarding")
989
+ def list_available_numbers_call_forwarding(
990
+ location_id: str = typer.Argument(help="locationId"),
991
+ phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
992
+ owner_name: str = typer.Option(None, "--owner-name", help="Return the list of phone numbers that are owned by the given"),
993
+ extension: str = typer.Option(None, "--extension", help="Returns the list of PSTN phone numbers with the given `exten"),
994
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
995
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
996
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
997
+ debug: bool = typer.Option(False, "--debug"),
998
+ ):
999
+ """Get Call Queue Call Forward Available Phone Numbers."""
1000
+ api = get_api(debug=debug)
1001
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/callForwarding/availableNumbers"
1002
+ params = {}
1003
+ if phone_number is not None:
1004
+ params["phoneNumber"] = phone_number
1005
+ if owner_name is not None:
1006
+ params["ownerName"] = owner_name
1007
+ if extension is not None:
1008
+ params["extension"] = extension
1009
+ if limit > 0:
1010
+ params["max"] = limit
1011
+ if offset > 0:
1012
+ params["start"] = offset
1013
+ org_id = get_org_id()
1014
+ if org_id is not None:
1015
+ params["orgId"] = org_id
1016
+ result = None
1017
+ try:
1018
+ result = api.session.rest_get(url, params=params)
1019
+ except WebexError as e:
1020
+ handle_rest_error(e)
1021
+ result = result or []
1022
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1023
+ if output == "json":
1024
+ print_json(items)
1025
+ else:
1026
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
1027
+
1028
+
1029
+
1030
+ @app.command("list-available-agents-queues")
1031
+ def list_available_agents_queues(
1032
+ location_id: str = typer.Option(..., "--location-id", help="The location ID of the call queue. Temporary mandatory query"),
1033
+ name: str = typer.Option(None, "--name", help="Search based on name (user first and last name combination)."),
1034
+ phone_number: str = typer.Option(None, "--phone-number", help="Search based on number or extension."),
1035
+ order: str = typer.Option(None, "--order", help="Order the available agents according to the designated field"),
1036
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1037
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1038
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1039
+ debug: bool = typer.Option(False, "--debug"),
1040
+ ):
1041
+ """Get Call Queue Available Agents."""
1042
+ api = get_api(debug=debug)
1043
+ url = f"https://webexapis.com/v1/telephony/config/queues/agents/availableAgents"
1044
+ params = {}
1045
+ if location_id is not None:
1046
+ params["locationId"] = location_id
1047
+ if name is not None:
1048
+ params["name"] = name
1049
+ if phone_number is not None:
1050
+ params["phoneNumber"] = phone_number
1051
+ if order is not None:
1052
+ params["order"] = order
1053
+ if limit > 0:
1054
+ params["max"] = limit
1055
+ if offset > 0:
1056
+ params["start"] = offset
1057
+ org_id = get_org_id()
1058
+ if org_id is not None:
1059
+ params["orgId"] = org_id
1060
+ result = None
1061
+ try:
1062
+ result = api.session.rest_get(url, params=params)
1063
+ except WebexError as e:
1064
+ handle_rest_error(e)
1065
+ result = result or []
1066
+ items = result.get("agents", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1067
+ if output == "json":
1068
+ print_json(items)
1069
+ else:
1070
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
1071
+
1072
+
1073
+
1074
+ @app.command("list-supervisors")
1075
+ def list_supervisors(
1076
+ name: str = typer.Option(None, "--name", help="Only return the supervisors that match the given name."),
1077
+ phone_number: str = typer.Option(None, "--phone-number", help="Only return the supervisors that match the given phone numbe"),
1078
+ order: str = typer.Option(None, "--order", help="Sort results alphabetically by supervisor name, in ascending"),
1079
+ has_cx_essentials: str = typer.Option(None, "--has-cx-essentials", help="Returns only the list of supervisors with Customer Assist li"),
1080
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1081
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1082
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1083
+ debug: bool = typer.Option(False, "--debug"),
1084
+ ):
1085
+ """Get List of Supervisors with Customer Assist."""
1086
+ api = get_api(debug=debug)
1087
+ url = f"https://webexapis.com/v1/telephony/config/supervisors"
1088
+ params = {}
1089
+ if name is not None:
1090
+ params["name"] = name
1091
+ if phone_number is not None:
1092
+ params["phoneNumber"] = phone_number
1093
+ if order is not None:
1094
+ params["order"] = order
1095
+ if has_cx_essentials is not None:
1096
+ params["hasCxEssentials"] = has_cx_essentials
1097
+ if limit > 0:
1098
+ params["max"] = limit
1099
+ if offset > 0:
1100
+ params["start"] = offset
1101
+ org_id = get_org_id()
1102
+ if org_id is not None:
1103
+ params["orgId"] = org_id
1104
+ result = None
1105
+ try:
1106
+ result = api.session.rest_get(url, params=params)
1107
+ except WebexError as e:
1108
+ handle_rest_error(e)
1109
+ result = result or []
1110
+ items = result.get("supervisors", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1111
+ if output == "json":
1112
+ print_json(items)
1113
+ else:
1114
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
1115
+
1116
+
1117
+
1118
+ @app.command("create-supervisors")
1119
+ def create_supervisors(
1120
+ has_cx_essentials: str = typer.Option(None, "--has-cx-essentials", help="Creates a Customer Assist queue supervisor, when `true`. Cus"),
1121
+ id_param: str = typer.Option(None, "--id", help="(required) A unique identifier for the supervisor."),
1122
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1123
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
1124
+ debug: bool = typer.Option(False, "--debug"),
1125
+ ):
1126
+ """Create a Supervisor with Customer Assist\n\nExample --json-body:\n '{"id":"...","agents":[{"id":"..."}]}'."""
1127
+ api = get_api(debug=debug)
1128
+ url = f"https://webexapis.com/v1/telephony/config/supervisors"
1129
+ params = {}
1130
+ if has_cx_essentials is not None:
1131
+ params["hasCxEssentials"] = has_cx_essentials
1132
+ org_id = get_org_id()
1133
+ if org_id is not None:
1134
+ params["orgId"] = org_id
1135
+ if json_body:
1136
+ body = json.loads(json_body)
1137
+ else:
1138
+ body = {}
1139
+ if id_param is not None:
1140
+ body["id"] = id_param
1141
+ _missing = [f for f in ['id'] if f not in body or body[f] is None]
1142
+ if _missing:
1143
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
1144
+ raise typer.Exit(1)
1145
+ try:
1146
+ result = api.session.rest_post(url, json=body, params=params)
1147
+ except WebexError as e:
1148
+ handle_rest_error(e)
1149
+ if output == "json":
1150
+ print_json(result)
1151
+ elif isinstance(result, dict) and "id" in result:
1152
+ typer.echo(f"Created: {result['id']}")
1153
+ elif not result or result == {}:
1154
+ typer.echo("Created.")
1155
+ else:
1156
+ print_json(result)
1157
+
1158
+
1159
+
1160
+ @app.command("delete-supervisors-config")
1161
+ def delete_supervisors_config(
1162
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
1163
+ debug: bool = typer.Option(False, "--debug"),
1164
+ ):
1165
+ """Delete the Call Queue or Customer Assist Supervisors."""
1166
+ if not force:
1167
+ typer.confirm("Delete this resource?", abort=True)
1168
+ api = get_api(debug=debug)
1169
+ url = f"https://webexapis.com/v1/telephony/config/supervisors"
1170
+ params = {}
1171
+ org_id = get_org_id()
1172
+ if org_id is not None:
1173
+ params["orgId"] = org_id
1174
+ try:
1175
+ api.session.rest_delete(url, params=params)
1176
+ except WebexError as e:
1177
+ handle_rest_error(e)
1178
+ typer.echo("Deleted.")
1179
+
1180
+
1181
+
1182
+ @app.command("show-supervisors")
1183
+ def show_supervisors(
1184
+ supervisor_id: str = typer.Argument(help="supervisorId"),
1185
+ max: str = typer.Option(None, "--max", help="Limit the number of objects returned to this maximum count."),
1186
+ start: str = typer.Option(None, "--start", help="Start at the zero-based offset in the list of matching objec"),
1187
+ name: str = typer.Option(None, "--name", help="Only return the agents that match the given name."),
1188
+ phone_number: str = typer.Option(None, "--phone-number", help="Only return agents that match the given phone number, extens"),
1189
+ order: str = typer.Option(None, "--order", help="Sort results alphabetically by supervisor name, in ascending"),
1190
+ has_cx_essentials: str = typer.Option(None, "--has-cx-essentials", help="Must be set to `true`, to view the details of a supervisor w"),
1191
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1192
+ debug: bool = typer.Option(False, "--debug"),
1193
+ ):
1194
+ """Get Supervisor Detail with Customer Assist."""
1195
+ api = get_api(debug=debug)
1196
+ url = f"https://webexapis.com/v1/telephony/config/supervisors/{supervisor_id}"
1197
+ params = {}
1198
+ if max is not None:
1199
+ params["max"] = max
1200
+ if start is not None:
1201
+ params["start"] = start
1202
+ if name is not None:
1203
+ params["name"] = name
1204
+ if phone_number is not None:
1205
+ params["phoneNumber"] = phone_number
1206
+ if order is not None:
1207
+ params["order"] = order
1208
+ if has_cx_essentials is not None:
1209
+ params["hasCxEssentials"] = has_cx_essentials
1210
+ org_id = get_org_id()
1211
+ if org_id is not None:
1212
+ params["orgId"] = org_id
1213
+ try:
1214
+ result = api.session.rest_get(url, params=params)
1215
+ except WebexError as e:
1216
+ handle_rest_error(e)
1217
+ if output == "json":
1218
+ print_json(result)
1219
+ else:
1220
+ if isinstance(result, dict):
1221
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1222
+ elif isinstance(result, list):
1223
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1224
+ else:
1225
+ print_json(result)
1226
+
1227
+
1228
+
1229
+ @app.command("update-supervisors")
1230
+ def update_supervisors(
1231
+ supervisor_id: str = typer.Argument(help="supervisorId"),
1232
+ has_cx_essentials: str = typer.Option(None, "--has-cx-essentials", help="Must be set to `true` to modify a supervisor with Customer A"),
1233
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1234
+ debug: bool = typer.Option(False, "--debug"),
1235
+ ):
1236
+ """Assign or Unassign Agents to Supervisor with Customer Assist\n\nExample --json-body:\n '{"agents":[{"id":"...","action":"..."}]}'."""
1237
+ api = get_api(debug=debug)
1238
+ url = f"https://webexapis.com/v1/telephony/config/supervisors/{supervisor_id}"
1239
+ params = {}
1240
+ if has_cx_essentials is not None:
1241
+ params["hasCxEssentials"] = has_cx_essentials
1242
+ org_id = get_org_id()
1243
+ if org_id is not None:
1244
+ params["orgId"] = org_id
1245
+ if json_body:
1246
+ body = json.loads(json_body)
1247
+ else:
1248
+ body = {}
1249
+ try:
1250
+ result = api.session.rest_put(url, json=body, params=params)
1251
+ except WebexError as e:
1252
+ handle_rest_error(e)
1253
+ typer.echo(f"Updated.")
1254
+
1255
+
1256
+
1257
+ @app.command("delete-supervisors-config-1")
1258
+ def delete_supervisors_config_1(
1259
+ supervisor_id: str = typer.Argument(help="supervisorId"),
1260
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
1261
+ debug: bool = typer.Option(False, "--debug"),
1262
+ ):
1263
+ """Delete a Supervisor."""
1264
+ if not force:
1265
+ typer.confirm(f"Delete {supervisor_id}?", abort=True)
1266
+ api = get_api(debug=debug)
1267
+ url = f"https://webexapis.com/v1/telephony/config/supervisors/{supervisor_id}"
1268
+ params = {}
1269
+ org_id = get_org_id()
1270
+ if org_id is not None:
1271
+ params["orgId"] = org_id
1272
+ try:
1273
+ api.session.rest_delete(url, params=params)
1274
+ except WebexError as e:
1275
+ handle_rest_error(e)
1276
+ typer.echo(f"Deleted: {supervisor_id}")
1277
+
1278
+
1279
+
1280
+ @app.command("list-available-supervisors")
1281
+ def list_available_supervisors(
1282
+ name: str = typer.Option(None, "--name", help="Only return the supervisors that match the given name."),
1283
+ phone_number: str = typer.Option(None, "--phone-number", help="Only return the supervisors that match the given phone numbe"),
1284
+ order: str = typer.Option(None, "--order", help="Sort results alphabetically by supervisor name, in ascending"),
1285
+ has_cx_essentials: str = typer.Option(None, "--has-cx-essentials", help="Returns only the list of available supervisors with Customer"),
1286
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1287
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1288
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1289
+ debug: bool = typer.Option(False, "--debug"),
1290
+ ):
1291
+ """List Available Supervisors with Customer Assist."""
1292
+ api = get_api(debug=debug)
1293
+ url = f"https://webexapis.com/v1/telephony/config/supervisors/availableSupervisors"
1294
+ params = {}
1295
+ if name is not None:
1296
+ params["name"] = name
1297
+ if phone_number is not None:
1298
+ params["phoneNumber"] = phone_number
1299
+ if order is not None:
1300
+ params["order"] = order
1301
+ if has_cx_essentials is not None:
1302
+ params["hasCxEssentials"] = has_cx_essentials
1303
+ if limit > 0:
1304
+ params["max"] = limit
1305
+ if offset > 0:
1306
+ params["start"] = offset
1307
+ org_id = get_org_id()
1308
+ if org_id is not None:
1309
+ params["orgId"] = org_id
1310
+ result = None
1311
+ try:
1312
+ result = api.session.rest_get(url, params=params)
1313
+ except WebexError as e:
1314
+ handle_rest_error(e)
1315
+ result = result or []
1316
+ items = result.get("supervisors", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1317
+ if output == "json":
1318
+ print_json(items)
1319
+ else:
1320
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
1321
+
1322
+
1323
+
1324
+ @app.command("list-available-agents-supervisors")
1325
+ def list_available_agents_supervisors(
1326
+ name: str = typer.Option(None, "--name", help="Returns only the agents that match the given name."),
1327
+ phone_number: str = typer.Option(None, "--phone-number", help="Returns only the agents that match the phone number, extensi"),
1328
+ order: str = typer.Option(None, "--order", help="Sort results alphabetically by supervisor name, in ascending"),
1329
+ has_cx_essentials: str = typer.Option(None, "--has-cx-essentials", help="Returns only the list of available agents with Customer Assi"),
1330
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1331
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1332
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1333
+ debug: bool = typer.Option(False, "--debug"),
1334
+ ):
1335
+ """List Available Agents with Customer Assist."""
1336
+ api = get_api(debug=debug)
1337
+ url = f"https://webexapis.com/v1/telephony/config/supervisors/availableAgents"
1338
+ params = {}
1339
+ if name is not None:
1340
+ params["name"] = name
1341
+ if phone_number is not None:
1342
+ params["phoneNumber"] = phone_number
1343
+ if order is not None:
1344
+ params["order"] = order
1345
+ if has_cx_essentials is not None:
1346
+ params["hasCxEssentials"] = has_cx_essentials
1347
+ if limit > 0:
1348
+ params["max"] = limit
1349
+ if offset > 0:
1350
+ params["start"] = offset
1351
+ org_id = get_org_id()
1352
+ if org_id is not None:
1353
+ params["orgId"] = org_id
1354
+ result = None
1355
+ try:
1356
+ result = api.session.rest_get(url, params=params)
1357
+ except WebexError as e:
1358
+ handle_rest_error(e)
1359
+ result = result or []
1360
+ items = result.get("agents", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1361
+ if output == "json":
1362
+ print_json(items)
1363
+ else:
1364
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
1365
+
1366
+
1367
+
1368
+ @app.command("list-agents")
1369
+ def list_agents(
1370
+ location_id: str = typer.Option(None, "--location-id", help="Return only the call queue agents in this location."),
1371
+ queue_id: str = typer.Option(None, "--queue-id", help="Only return call queue agents with the matching queue ID."),
1372
+ name: str = typer.Option(None, "--name", help="Returns only the list of call queue agents that match the gi"),
1373
+ phone_number: str = typer.Option(None, "--phone-number", help="Returns only the list of call queue agents that match the gi"),
1374
+ join_enabled: str = typer.Option(None, "--join-enabled", help="Returns only the list of call queue agents that match the gi"),
1375
+ has_cx_essentials: str = typer.Option(None, "--has-cx-essentials", help="Returns only the list of call queues with Customer Assist li"),
1376
+ order: str = typer.Option(None, "--order", help="Sort results alphabetically by call queue agent's name, in a"),
1377
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1378
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1379
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1380
+ debug: bool = typer.Option(False, "--debug"),
1381
+ ):
1382
+ """Read the List of Call Queue Agents with Customer Assist."""
1383
+ api = get_api(debug=debug)
1384
+ url = f"https://webexapis.com/v1/telephony/config/queues/agents"
1385
+ params = {}
1386
+ if location_id is not None:
1387
+ params["locationId"] = location_id
1388
+ if queue_id is not None:
1389
+ params["queueId"] = queue_id
1390
+ if name is not None:
1391
+ params["name"] = name
1392
+ if phone_number is not None:
1393
+ params["phoneNumber"] = phone_number
1394
+ if join_enabled is not None:
1395
+ params["joinEnabled"] = join_enabled
1396
+ if has_cx_essentials is not None:
1397
+ params["hasCxEssentials"] = has_cx_essentials
1398
+ if order is not None:
1399
+ params["order"] = order
1400
+ if limit > 0:
1401
+ params["max"] = limit
1402
+ if offset > 0:
1403
+ params["start"] = offset
1404
+ org_id = get_org_id()
1405
+ if org_id is not None:
1406
+ params["orgId"] = org_id
1407
+ try:
1408
+ if limit > 0:
1409
+ result = api.session.rest_get(url, params=params)
1410
+ result = result or {}
1411
+ items = result.get("agents", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1412
+ else:
1413
+ items = list(api.session.follow_pagination(url=url, params=params, item_key="agents"))
1414
+ except WebexError as e:
1415
+ handle_rest_error(e)
1416
+ if output == "json":
1417
+ print_json(items)
1418
+ else:
1419
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
1420
+
1421
+
1422
+
1423
+ @app.command("show-agents")
1424
+ def show_agents(
1425
+ id: str = typer.Argument(help="id"),
1426
+ has_cx_essentials: str = typer.Option(None, "--has-cx-essentials", help="Must be set to `true` to view the details of an agent with C"),
1427
+ max: str = typer.Option(..., "--max", help="Limit the number of objects returned to this maximum count."),
1428
+ start: str = typer.Option(..., "--start", help="Start at the zero-based offset in the list of matching objec"),
1429
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1430
+ debug: bool = typer.Option(False, "--debug"),
1431
+ ):
1432
+ """Get Details for a Call Queue Agent with Customer Assist."""
1433
+ api = get_api(debug=debug)
1434
+ url = f"https://webexapis.com/v1/telephony/config/queues/agents/{id}"
1435
+ params = {}
1436
+ if has_cx_essentials is not None:
1437
+ params["hasCxEssentials"] = has_cx_essentials
1438
+ if max is not None:
1439
+ params["max"] = max
1440
+ if start is not None:
1441
+ params["start"] = start
1442
+ org_id = get_org_id()
1443
+ if org_id is not None:
1444
+ params["orgId"] = org_id
1445
+ try:
1446
+ result = api.session.rest_get(url, params=params)
1447
+ except WebexError as e:
1448
+ handle_rest_error(e)
1449
+ if output == "json":
1450
+ print_json(result)
1451
+ else:
1452
+ if isinstance(result, dict):
1453
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1454
+ elif isinstance(result, list):
1455
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1456
+ else:
1457
+ print_json(result)
1458
+
1459
+
1460
+
1461
+ @app.command("update-settings-agents")
1462
+ def update_settings_agents(
1463
+ id: str = typer.Argument(help="id"),
1464
+ has_cx_essentials: str = typer.Option(None, "--has-cx-essentials", help="Must be set to `true` to modify an agent that has Customer A"),
1465
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1466
+ debug: bool = typer.Option(False, "--debug"),
1467
+ ):
1468
+ """Update an Agent's Settings of One or More Call Queues with Customer Assist\n\nExample --json-body:\n '{"settings":[{"queueId":"...","joinEnabled":"..."}]}'."""
1469
+ api = get_api(debug=debug)
1470
+ url = f"https://webexapis.com/v1/telephony/config/queues/agents/{id}/settings"
1471
+ params = {}
1472
+ if has_cx_essentials is not None:
1473
+ params["hasCxEssentials"] = has_cx_essentials
1474
+ org_id = get_org_id()
1475
+ if org_id is not None:
1476
+ params["orgId"] = org_id
1477
+ if json_body:
1478
+ body = json.loads(json_body)
1479
+ else:
1480
+ body = {}
1481
+ try:
1482
+ result = api.session.rest_put(url, json=body, params=params)
1483
+ except WebexError as e:
1484
+ handle_rest_error(e)
1485
+ typer.echo(f"Updated.")
1486
+
1487
+
1488
+
1489
+ @app.command("switch-mode-for")
1490
+ def switch_mode_for(
1491
+ location_id: str = typer.Argument(help="locationId"),
1492
+ queue_id: str = typer.Argument(help="queueId"),
1493
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body"),
1494
+ debug: bool = typer.Option(False, "--debug"),
1495
+ ):
1496
+ """Switch Mode for Call Forwarding Settings for a Call Queue."""
1497
+ api = get_api(debug=debug)
1498
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/callForwarding/actions/switchMode/invoke"
1499
+ params = {}
1500
+ org_id = get_org_id()
1501
+ if org_id is not None:
1502
+ params["orgId"] = org_id
1503
+ if json_body:
1504
+ body = json.loads(json_body)
1505
+ else:
1506
+ body = {}
1507
+ try:
1508
+ result = api.session.rest_post(url, json=body, params=params)
1509
+ except WebexError as e:
1510
+ handle_rest_error(e)
1511
+ print_json(result)
1512
+
1513
+
1514
+
1515
+ @app.command("list-dnis")
1516
+ def list_dnis(
1517
+ location_id: str = typer.Argument(help="locationId"),
1518
+ queue_id: str = typer.Argument(help="queueId"),
1519
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1520
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1521
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1522
+ debug: bool = typer.Option(False, "--debug"),
1523
+ ):
1524
+ """Get List of DNIS for a Call Queue."""
1525
+ api = get_api(debug=debug)
1526
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/dnis"
1527
+ params = {}
1528
+ if limit > 0:
1529
+ params["max"] = limit
1530
+ if offset > 0:
1531
+ params["start"] = offset
1532
+ org_id = get_org_id()
1533
+ if org_id is not None:
1534
+ params["orgId"] = org_id
1535
+ result = None
1536
+ try:
1537
+ result = api.session.rest_get(url, params=params)
1538
+ except WebexError as e:
1539
+ handle_rest_error(e)
1540
+ result = result or []
1541
+ items = result.get("dnisList", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1542
+ if output == "json":
1543
+ print_json(items)
1544
+ else:
1545
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
1546
+
1547
+
1548
+
1549
+ @app.command("create-dnis")
1550
+ def create_dnis(
1551
+ location_id: str = typer.Argument(help="locationId"),
1552
+ queue_id: str = typer.Argument(help="queueId"),
1553
+ name: str = typer.Option(None, "--name", help="(required) Name of the DNIS. Must be unique across the call queue."),
1554
+ phone_number: str = typer.Option(None, "--phone-number", help="Phone number of the DNIS. Must be a valid phone number from"),
1555
+ extension: str = typer.Option(None, "--extension", help="Extension of the DNIS. Either phoneNumber or extension is re"),
1556
+ ring_pattern: str = typer.Option(None, "--ring-pattern", help="(required) Choices: NORMAL, LONG_LONG, SHORT_SHORT_LONG, SHORT_LONG_SHORT"),
1557
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1558
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
1559
+ debug: bool = typer.Option(False, "--debug"),
1560
+ ):
1561
+ """Create a DNIS for a Call Queue."""
1562
+ api = get_api(debug=debug)
1563
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/dnis"
1564
+ params = {}
1565
+ org_id = get_org_id()
1566
+ if org_id is not None:
1567
+ params["orgId"] = org_id
1568
+ if json_body:
1569
+ body = json.loads(json_body)
1570
+ else:
1571
+ body = {}
1572
+ if name is not None:
1573
+ body["name"] = name
1574
+ if phone_number is not None:
1575
+ body["phoneNumber"] = phone_number
1576
+ if extension is not None:
1577
+ body["extension"] = extension
1578
+ if ring_pattern is not None:
1579
+ body["ringPattern"] = ring_pattern
1580
+ _missing = [f for f in ['name', 'ringPattern'] if f not in body or body[f] is None]
1581
+ if _missing:
1582
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
1583
+ raise typer.Exit(1)
1584
+ try:
1585
+ result = api.session.rest_post(url, json=body, params=params)
1586
+ except WebexError as e:
1587
+ handle_rest_error(e)
1588
+ if output == "json":
1589
+ print_json(result)
1590
+ elif isinstance(result, dict) and "id" in result:
1591
+ typer.echo(f"Created: {result['id']}")
1592
+ elif not result or result == {}:
1593
+ typer.echo("Created.")
1594
+ else:
1595
+ print_json(result)
1596
+
1597
+
1598
+
1599
+ @app.command("delete-dnis-queues")
1600
+ def delete_dnis_queues(
1601
+ location_id: str = typer.Argument(help="locationId"),
1602
+ queue_id: str = typer.Argument(help="queueId"),
1603
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
1604
+ debug: bool = typer.Option(False, "--debug"),
1605
+ ):
1606
+ """Bulk Delete DNIS for a Call Queue."""
1607
+ if not force:
1608
+ typer.confirm(f"Delete {queue_id}?", abort=True)
1609
+ api = get_api(debug=debug)
1610
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/dnis"
1611
+ params = {}
1612
+ org_id = get_org_id()
1613
+ if org_id is not None:
1614
+ params["orgId"] = org_id
1615
+ try:
1616
+ api.session.rest_delete(url, params=params)
1617
+ except WebexError as e:
1618
+ handle_rest_error(e)
1619
+ typer.echo(f"Deleted: {queue_id}")
1620
+
1621
+
1622
+
1623
+ @app.command("show-dnis")
1624
+ def show_dnis(
1625
+ location_id: str = typer.Argument(help="locationId"),
1626
+ queue_id: str = typer.Argument(help="queueId"),
1627
+ dnis_id: str = typer.Argument(help="dnisId"),
1628
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1629
+ debug: bool = typer.Option(False, "--debug"),
1630
+ ):
1631
+ """Get a DNIS for a Call Queue."""
1632
+ api = get_api(debug=debug)
1633
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/dnis/{dnis_id}"
1634
+ params = {}
1635
+ org_id = get_org_id()
1636
+ if org_id is not None:
1637
+ params["orgId"] = org_id
1638
+ try:
1639
+ result = api.session.rest_get(url, params=params)
1640
+ except WebexError as e:
1641
+ handle_rest_error(e)
1642
+ if output == "json":
1643
+ print_json(result)
1644
+ else:
1645
+ if isinstance(result, dict):
1646
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1647
+ elif isinstance(result, list):
1648
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1649
+ else:
1650
+ print_json(result)
1651
+
1652
+
1653
+
1654
+ @app.command("update-dnis")
1655
+ def update_dnis(
1656
+ location_id: str = typer.Argument(help="locationId"),
1657
+ queue_id: str = typer.Argument(help="queueId"),
1658
+ dnis_id: str = typer.Argument(help="dnisId"),
1659
+ name: str = typer.Option(None, "--name", help="Name of the DNIS. Must be unique across the call queue."),
1660
+ phone_number: str = typer.Option(None, "--phone-number", help="Phone number of the DNIS. Set to `null` to remove the phone"),
1661
+ extension: str = typer.Option(None, "--extension", help="Extension of the DNIS. Set to `null` to remove the extension"),
1662
+ ring_pattern: str = typer.Option(None, "--ring-pattern", help="Choices: NORMAL, LONG_LONG, SHORT_SHORT_LONG, SHORT_LONG_SHORT"),
1663
+ custom_dnis_announcement_settings_enabled: bool = typer.Option(None, "--custom-dnis-announcement-settings-enabled/--no-custom-dnis-announcement-settings-enabled", help="Use custom announcement settings for the DNIS."),
1664
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1665
+ debug: bool = typer.Option(False, "--debug"),
1666
+ ):
1667
+ """Modify a DNIS for a Call Queue."""
1668
+ api = get_api(debug=debug)
1669
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/dnis/{dnis_id}"
1670
+ params = {}
1671
+ org_id = get_org_id()
1672
+ if org_id is not None:
1673
+ params["orgId"] = org_id
1674
+ if json_body:
1675
+ body = json.loads(json_body)
1676
+ else:
1677
+ body = {}
1678
+ if name is not None:
1679
+ body["name"] = name
1680
+ if phone_number is not None:
1681
+ body["phoneNumber"] = phone_number
1682
+ if extension is not None:
1683
+ body["extension"] = extension
1684
+ if ring_pattern is not None:
1685
+ body["ringPattern"] = ring_pattern
1686
+ if custom_dnis_announcement_settings_enabled is not None:
1687
+ body["customDnisAnnouncementSettingsEnabled"] = custom_dnis_announcement_settings_enabled
1688
+ try:
1689
+ result = api.session.rest_put(url, json=body, params=params)
1690
+ except WebexError as e:
1691
+ handle_rest_error(e)
1692
+ typer.echo(f"Updated.")
1693
+
1694
+
1695
+
1696
+ @app.command("delete-dnis-queues-1")
1697
+ def delete_dnis_queues_1(
1698
+ location_id: str = typer.Argument(help="locationId"),
1699
+ queue_id: str = typer.Argument(help="queueId"),
1700
+ dnis_id: str = typer.Argument(help="dnisId"),
1701
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
1702
+ debug: bool = typer.Option(False, "--debug"),
1703
+ ):
1704
+ """Delete a DNIS for a Call Queue."""
1705
+ if not force:
1706
+ typer.confirm(f"Delete {dnis_id}?", abort=True)
1707
+ api = get_api(debug=debug)
1708
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/dnis/{dnis_id}"
1709
+ params = {}
1710
+ org_id = get_org_id()
1711
+ if org_id is not None:
1712
+ params["orgId"] = org_id
1713
+ try:
1714
+ api.session.rest_delete(url, params=params)
1715
+ except WebexError as e:
1716
+ handle_rest_error(e)
1717
+ typer.echo(f"Deleted: {dnis_id}")
1718
+
1719
+
1720
+
1721
+ @app.command("show-settings")
1722
+ def show_settings(
1723
+ location_id: str = typer.Argument(help="locationId"),
1724
+ queue_id: str = typer.Argument(help="queueId"),
1725
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1726
+ debug: bool = typer.Option(False, "--debug"),
1727
+ ):
1728
+ """Get DNIS Settings for a Call Queue."""
1729
+ api = get_api(debug=debug)
1730
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/dnis/settings"
1731
+ params = {}
1732
+ org_id = get_org_id()
1733
+ if org_id is not None:
1734
+ params["orgId"] = org_id
1735
+ try:
1736
+ result = api.session.rest_get(url, params=params)
1737
+ except WebexError as e:
1738
+ handle_rest_error(e)
1739
+ if output == "json":
1740
+ print_json(result)
1741
+ else:
1742
+ if isinstance(result, dict):
1743
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1744
+ elif isinstance(result, list):
1745
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1746
+ else:
1747
+ print_json(result)
1748
+
1749
+
1750
+
1751
+ @app.command("update-settings-dnis")
1752
+ def update_settings_dnis(
1753
+ location_id: str = typer.Argument(help="locationId"),
1754
+ queue_id: str = typer.Argument(help="queueId"),
1755
+ distinctive_ringing_enabled: bool = typer.Option(None, "--distinctive-ringing-enabled/--no-distinctive-ringing-enabled", help="Whether distinctive ringing is enabled for the queue."),
1756
+ display_dnis_name_and_number_enabled: bool = typer.Option(None, "--display-dnis-name-and-number-enabled/--no-display-dnis-name-and-number-enabled", help="Whether the DNIS name and number is displayed to agents."),
1757
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1758
+ debug: bool = typer.Option(False, "--debug"),
1759
+ ):
1760
+ """Modify DNIS Settings for a Call Queue."""
1761
+ api = get_api(debug=debug)
1762
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/dnis/settings"
1763
+ params = {}
1764
+ org_id = get_org_id()
1765
+ if org_id is not None:
1766
+ params["orgId"] = org_id
1767
+ if json_body:
1768
+ body = json.loads(json_body)
1769
+ else:
1770
+ body = {}
1771
+ if distinctive_ringing_enabled is not None:
1772
+ body["distinctiveRingingEnabled"] = distinctive_ringing_enabled
1773
+ if display_dnis_name_and_number_enabled is not None:
1774
+ body["displayDnisNameAndNumberEnabled"] = display_dnis_name_and_number_enabled
1775
+ try:
1776
+ result = api.session.rest_put(url, json=body, params=params)
1777
+ except WebexError as e:
1778
+ handle_rest_error(e)
1779
+ typer.echo(f"Updated.")
1780
+
1781
+
1782
+
1783
+ @app.command("show-announcements")
1784
+ def show_announcements(
1785
+ location_id: str = typer.Argument(help="locationId"),
1786
+ queue_id: str = typer.Argument(help="queueId"),
1787
+ dnis_id: str = typer.Argument(help="dnisId"),
1788
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1789
+ debug: bool = typer.Option(False, "--debug"),
1790
+ ):
1791
+ """Get DNIS Announcements for a Call Queue."""
1792
+ api = get_api(debug=debug)
1793
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/dnis/{dnis_id}/announcements"
1794
+ params = {}
1795
+ org_id = get_org_id()
1796
+ if org_id is not None:
1797
+ params["orgId"] = org_id
1798
+ try:
1799
+ result = api.session.rest_get(url, params=params)
1800
+ except WebexError as e:
1801
+ handle_rest_error(e)
1802
+ if output == "json":
1803
+ print_json(result)
1804
+ else:
1805
+ if isinstance(result, dict):
1806
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1807
+ elif isinstance(result, list):
1808
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1809
+ else:
1810
+ print_json(result)
1811
+
1812
+
1813
+
1814
+ @app.command("update-announcements")
1815
+ def update_announcements(
1816
+ location_id: str = typer.Argument(help="locationId"),
1817
+ queue_id: str = typer.Argument(help="queueId"),
1818
+ dnis_id: str = typer.Argument(help="dnisId"),
1819
+ custom_dnis_announcement_settings_enabled: bool = typer.Option(None, "--custom-dnis-announcement-settings-enabled/--no-custom-dnis-announcement-settings-enabled", help="Whether custom DNIS announcement settings are enabled for th"),
1820
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1821
+ debug: bool = typer.Option(False, "--debug"),
1822
+ ):
1823
+ """Modify DNIS Announcements for a Call Queue\n\nExample --json-body:\n '{"customDnisAnnouncementSettingsEnabled":true,"welcomeMessage":{"enabled":true,"alwaysEnabled":true,"greeting":"DEFAULT","audioAnnouncementFiles":["..."]},"comfortMessage":{"enabled":true,"timeBetweenMessages":0,"greeting":"DEFAULT","audioAnnouncementFiles":["..."]},"comfortMessageBypass":{"enabled":true,"callWaitingAgeThreshold":0,"greeting":"DEFAULT","audioAnnouncementFiles":["..."]},"mohMessage":{"normalSource":{"enabled":"...","greeting":"...","audioAnnouncementFiles":"...","audioPlaylistId":"..."},"alternateSource":{"enabled":"...","greeting":"...","audioAnnouncementFiles":"...","audioPlaylistId":"..."}},"waitMessage":{"enabled":true,"waitMode":"TIME","handlingTime":0,"defaultHandlingTime":0,"queuePosition":0,"highVolumeMessageEnabled":true,"estimatedWaitingTime":0,"callbackOptionEnabled":true},"whisperMessage":{"enabled":true,"greeting":"DEFAULT","audioAnnouncementFiles":["..."]}}'."""
1824
+ api = get_api(debug=debug)
1825
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/dnis/{dnis_id}/announcements"
1826
+ params = {}
1827
+ org_id = get_org_id()
1828
+ if org_id is not None:
1829
+ params["orgId"] = org_id
1830
+ if json_body:
1831
+ body = json.loads(json_body)
1832
+ else:
1833
+ body = {}
1834
+ if custom_dnis_announcement_settings_enabled is not None:
1835
+ body["customDnisAnnouncementSettingsEnabled"] = custom_dnis_announcement_settings_enabled
1836
+ try:
1837
+ result = api.session.rest_put(url, json=body, params=params)
1838
+ except WebexError as e:
1839
+ handle_rest_error(e)
1840
+ typer.echo(f"Updated.")
1841
+
1842
+
1843
+
1844
+ @app.command("list-available-numbers-dnis")
1845
+ def list_available_numbers_dnis(
1846
+ location_id: str = typer.Argument(help="locationId"),
1847
+ phone_number: str = typer.Option(None, "--phone-number", help="Filter by phone number."),
1848
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1849
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1850
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1851
+ debug: bool = typer.Option(False, "--debug"),
1852
+ ):
1853
+ """Get Available Phone Numbers for DNIS."""
1854
+ api = get_api(debug=debug)
1855
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/dnis/availableNumbers"
1856
+ params = {}
1857
+ if phone_number is not None:
1858
+ params["phoneNumber"] = phone_number
1859
+ if limit > 0:
1860
+ params["max"] = limit
1861
+ if offset > 0:
1862
+ params["start"] = offset
1863
+ org_id = get_org_id()
1864
+ if org_id is not None:
1865
+ params["orgId"] = org_id
1866
+ result = None
1867
+ try:
1868
+ result = api.session.rest_get(url, params=params)
1869
+ except WebexError as e:
1870
+ handle_rest_error(e)
1871
+ result = result or []
1872
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1873
+ if output == "json":
1874
+ print_json(items)
1875
+ else:
1876
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
1877
+
1878
+