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,2072 @@
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 virtual-line-settings.")
10
+
11
+
12
+ @app.command("list")
13
+ def cmd_list(
14
+ location_id: str = typer.Option(None, "--location-id", help="Return the list of virtual lines matching these location ids"),
15
+ id_param: str = typer.Option(None, "--id", help="Return the list of virtual lines matching these virtualLineI"),
16
+ owner_name: str = typer.Option(None, "--owner-name", help="Return the list of virtual lines matching these owner names."),
17
+ phone_number: str = typer.Option(None, "--phone-number", help="Return the list of virtual lines matching these phone number"),
18
+ location_name: str = typer.Option(None, "--location-name", help="Return the list of virtual lines matching the location names"),
19
+ order: str = typer.Option(None, "--order", help="Return the list of virtual lines based on the order. Default"),
20
+ has_device_assigned: str = typer.Option(None, "--has-device-assigned", help="If `true`, includes only virtual lines with devices assigned"),
21
+ has_extension_assigned: str = typer.Option(None, "--has-extension-assigned", help="If `true`, includes only virtual lines with an extension ass"),
22
+ has_dn_assigned: str = typer.Option(None, "--has-dn-assigned", help="If `true`, includes only virtual lines with an assigned dire"),
23
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
24
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
25
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
26
+ debug: bool = typer.Option(False, "--debug"),
27
+ ):
28
+ """Read the List of Virtual Lines."""
29
+ api = get_api(debug=debug)
30
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines"
31
+ params = {}
32
+ if location_id is not None:
33
+ params["locationId"] = location_id
34
+ if id_param is not None:
35
+ params["id"] = id_param
36
+ if owner_name is not None:
37
+ params["ownerName"] = owner_name
38
+ if phone_number is not None:
39
+ params["phoneNumber"] = phone_number
40
+ if location_name is not None:
41
+ params["locationName"] = location_name
42
+ if order is not None:
43
+ params["order"] = order
44
+ if has_device_assigned is not None:
45
+ params["hasDeviceAssigned"] = has_device_assigned
46
+ if has_extension_assigned is not None:
47
+ params["hasExtensionAssigned"] = has_extension_assigned
48
+ if has_dn_assigned is not None:
49
+ params["hasDnAssigned"] = has_dn_assigned
50
+ if limit > 0:
51
+ params["max"] = limit
52
+ if offset > 0:
53
+ params["start"] = offset
54
+ org_id = get_org_id()
55
+ if org_id is not None:
56
+ params["orgId"] = org_id
57
+ try:
58
+ if limit > 0:
59
+ result = api.session.rest_get(url, params=params)
60
+ result = result or {}
61
+ items = result.get("virtualLines", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
62
+ else:
63
+ items = list(api.session.follow_pagination(url=url, params=params, item_key="virtualLines"))
64
+ except WebexError as e:
65
+ handle_rest_error(e)
66
+ if output == "json":
67
+ print_json(items)
68
+ else:
69
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
70
+
71
+
72
+
73
+ @app.command("create")
74
+ def create(
75
+ first_name: str = typer.Option(None, "--first-name", help="(required) First name defined for a virtual line. Minimum length is 1."),
76
+ last_name: str = typer.Option(None, "--last-name", help="(required) Last name defined for a virtual line. Minimum length is 1. M"),
77
+ display_name: str = typer.Option(None, "--display-name", help="Display name defined for a virtual line."),
78
+ phone_number: str = typer.Option(None, "--phone-number", help="Phone number of a virtual line. Minimum length is 1. Maximum"),
79
+ extension: str = typer.Option(None, "--extension", help="Extension of a virtual line. Minimum length is 2. Maximum le"),
80
+ location_id: str = typer.Option(None, "--location-id", help="(required) ID of location for virtual line."),
81
+ caller_id_last_name: str = typer.Option(None, "--caller-id-last-name", help="Last name used in the Calling Line ID and for dial-by-name f"),
82
+ caller_id_first_name: str = typer.Option(None, "--caller-id-first-name", help="First name used in the Calling Line ID and for dial-by-name"),
83
+ caller_id_number: str = typer.Option(None, "--caller-id-number", help="Phone number to appear as the CLID for all calls. Minimum le"),
84
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
85
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
86
+ debug: bool = typer.Option(False, "--debug"),
87
+ ):
88
+ """Create a Virtual Line."""
89
+ api = get_api(debug=debug)
90
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines"
91
+ params = {}
92
+ org_id = get_org_id()
93
+ if org_id is not None:
94
+ params["orgId"] = org_id
95
+ if json_body:
96
+ body = json.loads(json_body)
97
+ else:
98
+ body = {}
99
+ if first_name is not None:
100
+ body["firstName"] = first_name
101
+ if last_name is not None:
102
+ body["lastName"] = last_name
103
+ if display_name is not None:
104
+ body["displayName"] = display_name
105
+ if phone_number is not None:
106
+ body["phoneNumber"] = phone_number
107
+ if extension is not None:
108
+ body["extension"] = extension
109
+ if location_id is not None:
110
+ body["locationId"] = location_id
111
+ if caller_id_last_name is not None:
112
+ body["callerIdLastName"] = caller_id_last_name
113
+ if caller_id_first_name is not None:
114
+ body["callerIdFirstName"] = caller_id_first_name
115
+ if caller_id_number is not None:
116
+ body["callerIdNumber"] = caller_id_number
117
+ _missing = [f for f in ['firstName', 'lastName', 'locationId'] if f not in body or body[f] is None]
118
+ if _missing:
119
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
120
+ raise typer.Exit(1)
121
+ try:
122
+ result = api.session.rest_post(url, json=body, params=params)
123
+ except WebexError as e:
124
+ handle_rest_error(e)
125
+ if output == "json":
126
+ print_json(result)
127
+ elif isinstance(result, dict) and "id" in result:
128
+ typer.echo(f"Created: {result['id']}")
129
+ elif not result or result == {}:
130
+ typer.echo("Created.")
131
+ else:
132
+ print_json(result)
133
+
134
+
135
+
136
+ @app.command("show")
137
+ def show(
138
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
139
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
140
+ debug: bool = typer.Option(False, "--debug"),
141
+ ):
142
+ """Read Call Recording Settings for a Virtual Line."""
143
+ api = get_api(debug=debug)
144
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/callRecording"
145
+ params = {}
146
+ org_id = get_org_id()
147
+ if org_id is not None:
148
+ params["orgId"] = org_id
149
+ try:
150
+ result = api.session.rest_get(url, params=params)
151
+ except WebexError as e:
152
+ handle_rest_error(e)
153
+ if output == "json":
154
+ print_json(result)
155
+ else:
156
+ if isinstance(result, dict):
157
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
158
+ elif isinstance(result, list):
159
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
160
+ else:
161
+ print_json(result)
162
+
163
+
164
+
165
+ @app.command("update")
166
+ def update(
167
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
168
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="`true` if call recording is enabled."),
169
+ record: str = typer.Option(None, "--record", help="Choices: Always, Never, Always with Pause/Resume, On Demand with User Initiated Start"),
170
+ record_voicemail_enabled: bool = typer.Option(None, "--record-voicemail-enabled/--no-record-voicemail-enabled", help="When `true`, voicemail messages are also recorded."),
171
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
172
+ debug: bool = typer.Option(False, "--debug"),
173
+ ):
174
+ """Configure Call Recording Settings for a Virtual Line\n\nExample --json-body:\n '{"enabled":true,"record":"Always","recordVoicemailEnabled":true,"notification":{"type":"Beep","enabled":true},"repeat":{"interval":0,"enabled":true},"startStopAnnouncement":{"internalCallsEnabled":true,"pstnCallsEnabled":true}}'."""
175
+ api = get_api(debug=debug)
176
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/callRecording"
177
+ params = {}
178
+ org_id = get_org_id()
179
+ if org_id is not None:
180
+ params["orgId"] = org_id
181
+ if json_body:
182
+ body = json.loads(json_body)
183
+ else:
184
+ body = {}
185
+ if enabled is not None:
186
+ body["enabled"] = enabled
187
+ if record is not None:
188
+ body["record"] = record
189
+ if record_voicemail_enabled is not None:
190
+ body["recordVoicemailEnabled"] = record_voicemail_enabled
191
+ try:
192
+ result = api.session.rest_put(url, json=body, params=params)
193
+ except WebexError as e:
194
+ handle_rest_error(e)
195
+ typer.echo(f"Updated.")
196
+
197
+
198
+
199
+ @app.command("show-virtual-lines")
200
+ def show_virtual_lines(
201
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
202
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
203
+ debug: bool = typer.Option(False, "--debug"),
204
+ ):
205
+ """Get Details for a Virtual Line."""
206
+ api = get_api(debug=debug)
207
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}"
208
+ params = {}
209
+ org_id = get_org_id()
210
+ if org_id is not None:
211
+ params["orgId"] = org_id
212
+ try:
213
+ result = api.session.rest_get(url, params=params)
214
+ except WebexError as e:
215
+ handle_rest_error(e)
216
+ if output == "json":
217
+ print_json(result)
218
+ else:
219
+ if isinstance(result, dict):
220
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
221
+ elif isinstance(result, list):
222
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
223
+ else:
224
+ print_json(result)
225
+
226
+
227
+
228
+ @app.command("update-virtual-lines")
229
+ def update_virtual_lines(
230
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
231
+ first_name: str = typer.Option(None, "--first-name", help="First name defined for a virtual line. Minimum length is 1."),
232
+ last_name: str = typer.Option(None, "--last-name", help="Last name defined for a virtual line. Minimum length is 1. M"),
233
+ display_name: str = typer.Option(None, "--display-name", help="Display name defined for a virtual line."),
234
+ phone_number: str = typer.Option(None, "--phone-number", help="Phone number of a virtual line. Minimum length is 1. Maximum"),
235
+ extension: str = typer.Option(None, "--extension", help="Extension of a virtual line. Minimum length is 2. Maximum le"),
236
+ announcement_language: str = typer.Option(None, "--announcement-language", help="Virtual Line's announcement language."),
237
+ caller_id_last_name: str = typer.Option(None, "--caller-id-last-name", help="Last name used in the Calling Line ID and for dial-by-name f"),
238
+ caller_id_first_name: str = typer.Option(None, "--caller-id-first-name", help="First name used in the Calling Line ID and for dial-by-name"),
239
+ caller_id_number: str = typer.Option(None, "--caller-id-number", help="Phone number to appear as the CLID for all calls. Minimum le"),
240
+ time_zone: str = typer.Option(None, "--time-zone", help="Time zone defined for the virtual line."),
241
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
242
+ debug: bool = typer.Option(False, "--debug"),
243
+ ):
244
+ """Update a Virtual Line."""
245
+ api = get_api(debug=debug)
246
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}"
247
+ params = {}
248
+ org_id = get_org_id()
249
+ if org_id is not None:
250
+ params["orgId"] = org_id
251
+ if json_body:
252
+ body = json.loads(json_body)
253
+ else:
254
+ body = {}
255
+ if first_name is not None:
256
+ body["firstName"] = first_name
257
+ if last_name is not None:
258
+ body["lastName"] = last_name
259
+ if display_name is not None:
260
+ body["displayName"] = display_name
261
+ if phone_number is not None:
262
+ body["phoneNumber"] = phone_number
263
+ if extension is not None:
264
+ body["extension"] = extension
265
+ if announcement_language is not None:
266
+ body["announcementLanguage"] = announcement_language
267
+ if caller_id_last_name is not None:
268
+ body["callerIdLastName"] = caller_id_last_name
269
+ if caller_id_first_name is not None:
270
+ body["callerIdFirstName"] = caller_id_first_name
271
+ if caller_id_number is not None:
272
+ body["callerIdNumber"] = caller_id_number
273
+ if time_zone is not None:
274
+ body["timeZone"] = time_zone
275
+ try:
276
+ result = api.session.rest_put(url, json=body, params=params)
277
+ except WebexError as e:
278
+ handle_rest_error(e)
279
+ typer.echo(f"Updated.")
280
+
281
+
282
+
283
+ @app.command("delete")
284
+ def delete(
285
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
286
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
287
+ debug: bool = typer.Option(False, "--debug"),
288
+ ):
289
+ """Delete a Virtual Line."""
290
+ if not force:
291
+ typer.confirm(f"Delete {virtual_line_id}?", abort=True)
292
+ api = get_api(debug=debug)
293
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}"
294
+ params = {}
295
+ org_id = get_org_id()
296
+ if org_id is not None:
297
+ params["orgId"] = org_id
298
+ try:
299
+ api.session.rest_delete(url, params=params)
300
+ except WebexError as e:
301
+ handle_rest_error(e)
302
+ typer.echo(f"Deleted: {virtual_line_id}")
303
+
304
+
305
+
306
+ @app.command("show-number")
307
+ def show_number(
308
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
309
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
310
+ debug: bool = typer.Option(False, "--debug"),
311
+ ):
312
+ """Get Phone Number assigned for a Virtual Line."""
313
+ api = get_api(debug=debug)
314
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/number"
315
+ params = {}
316
+ org_id = get_org_id()
317
+ if org_id is not None:
318
+ params["orgId"] = org_id
319
+ try:
320
+ result = api.session.rest_get(url, params=params)
321
+ except WebexError as e:
322
+ handle_rest_error(e)
323
+ if output == "json":
324
+ print_json(result)
325
+ else:
326
+ if isinstance(result, dict):
327
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
328
+ elif isinstance(result, list):
329
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
330
+ else:
331
+ print_json(result)
332
+
333
+
334
+
335
+ @app.command("update-directory-search")
336
+ def update_directory_search(
337
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
338
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="Whether or not the directory search for a virtual line is en"),
339
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
340
+ debug: bool = typer.Option(False, "--debug"),
341
+ ):
342
+ """Update Directory search for a Virtual Line."""
343
+ api = get_api(debug=debug)
344
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/directorySearch"
345
+ params = {}
346
+ org_id = get_org_id()
347
+ if org_id is not None:
348
+ params["orgId"] = org_id
349
+ if json_body:
350
+ body = json.loads(json_body)
351
+ else:
352
+ body = {}
353
+ if enabled is not None:
354
+ body["enabled"] = enabled
355
+ try:
356
+ result = api.session.rest_put(url, json=body, params=params)
357
+ except WebexError as e:
358
+ handle_rest_error(e)
359
+ typer.echo(f"Updated.")
360
+
361
+
362
+
363
+ @app.command("list-devices")
364
+ def list_devices(
365
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
366
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
367
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
368
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
369
+ debug: bool = typer.Option(False, "--debug"),
370
+ ):
371
+ """Get List of Devices assigned for a Virtual Line."""
372
+ api = get_api(debug=debug)
373
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/devices"
374
+ params = {}
375
+ if limit > 0:
376
+ params["max"] = limit
377
+ if offset > 0:
378
+ params["start"] = offset
379
+ org_id = get_org_id()
380
+ if org_id is not None:
381
+ params["orgId"] = org_id
382
+ result = None
383
+ try:
384
+ result = api.session.rest_get(url, params=params)
385
+ except WebexError as e:
386
+ handle_rest_error(e)
387
+ result = result or []
388
+ items = result.get("devices", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
389
+ if output == "json":
390
+ print_json(items)
391
+ else:
392
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
393
+
394
+
395
+
396
+ @app.command("list-dect-networks")
397
+ def list_dect_networks(
398
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
399
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
400
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
401
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
402
+ debug: bool = typer.Option(False, "--debug"),
403
+ ):
404
+ """Get List of DECT Networks Handsets for a Virtual Line."""
405
+ api = get_api(debug=debug)
406
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/dectNetworks"
407
+ params = {}
408
+ if limit > 0:
409
+ params["max"] = limit
410
+ if offset > 0:
411
+ params["start"] = offset
412
+ org_id = get_org_id()
413
+ if org_id is not None:
414
+ params["orgId"] = org_id
415
+ result = None
416
+ try:
417
+ result = api.session.rest_get(url, params=params)
418
+ except WebexError as e:
419
+ handle_rest_error(e)
420
+ result = result or []
421
+ items = result.get("dectNetworks", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
422
+ if output == "json":
423
+ print_json(items)
424
+ else:
425
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
426
+
427
+
428
+
429
+ @app.command("list-caller-id")
430
+ def list_caller_id(
431
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
432
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
433
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
434
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
435
+ debug: bool = typer.Option(False, "--debug"),
436
+ ):
437
+ """Read Caller ID Settings for a Virtual Line."""
438
+ api = get_api(debug=debug)
439
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/callerId"
440
+ params = {}
441
+ if limit > 0:
442
+ params["max"] = limit
443
+ if offset > 0:
444
+ params["start"] = offset
445
+ org_id = get_org_id()
446
+ if org_id is not None:
447
+ params["orgId"] = org_id
448
+ result = None
449
+ try:
450
+ result = api.session.rest_get(url, params=params)
451
+ except WebexError as e:
452
+ handle_rest_error(e)
453
+ result = result or []
454
+ items = result.get("types", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
455
+ if output == "json":
456
+ print_json(items)
457
+ else:
458
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
459
+
460
+
461
+
462
+ @app.command("update-caller-id-virtual-lines")
463
+ def update_caller_id_virtual_lines(
464
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
465
+ selected: str = typer.Option(None, "--selected", help="Choices: DIRECT_LINE, LOCATION_NUMBER, CUSTOM"),
466
+ custom_number: str = typer.Option(None, "--custom-number", help="Custom number which will be shown if CUSTOM is selected. Thi"),
467
+ first_name: str = typer.Option(None, "--first-name", help="Virtual line's Caller ID first name. The characters `%`, `+"),
468
+ last_name: str = typer.Option(None, "--last-name", help="Virtual line's Caller ID last name. The characters `%`, `+`"),
469
+ block_in_forward_calls_enabled: bool = typer.Option(None, "--block-in-forward-calls-enabled/--no-block-in-forward-calls-enabled", help="Block this virtual line's identity when receiving a call."),
470
+ external_caller_id_name_policy: str = typer.Option(None, "--external-caller-id-name-policy", help="Choices: DIRECT_LINE, LOCATION, OTHER"),
471
+ custom_external_caller_id_name: str = typer.Option(None, "--custom-external-caller-id-name", help="Custom external caller ID name which will be shown if extern"),
472
+ additional_external_caller_id_direct_line_enabled: bool = typer.Option(None, "--additional-external-caller-id-direct-line-enabled/--no-additional-external-caller-id-direct-line-enabled", help="Set the virtual line's direct line number as additional exte"),
473
+ additional_external_caller_id_location_number_enabled: bool = typer.Option(None, "--additional-external-caller-id-location-number-enabled/--no-additional-external-caller-id-location-number-enabled", help="Set the Location main number as additional external caller I"),
474
+ additional_external_caller_id_custom_number: str = typer.Option(None, "--additional-external-caller-id-custom-number", help="To set a custom number as additional external caller ID for"),
475
+ dial_by_first_name: str = typer.Option(None, "--dial-by-first-name", help="Sets or clears the first name to be used for dial-by-name fu"),
476
+ dial_by_last_name: str = typer.Option(None, "--dial-by-last-name", help="Sets or clears the last name to be used for dial-by-name fun"),
477
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
478
+ debug: bool = typer.Option(False, "--debug"),
479
+ ):
480
+ """Configure Caller ID Settings for a Virtual Line\n\nExample --json-body:\n '{"selected":"DIRECT_LINE","customNumber":"...","firstName":"...","lastName":"...","blockInForwardCallsEnabled":true,"externalCallerIdNamePolicy":"DIRECT_LINE","customExternalCallerIdName":"...","additionalExternalCallerIdDirectLineEnabled":true}'."""
481
+ api = get_api(debug=debug)
482
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/callerId"
483
+ params = {}
484
+ org_id = get_org_id()
485
+ if org_id is not None:
486
+ params["orgId"] = org_id
487
+ if json_body:
488
+ body = json.loads(json_body)
489
+ else:
490
+ body = {}
491
+ if selected is not None:
492
+ body["selected"] = selected
493
+ if custom_number is not None:
494
+ body["customNumber"] = custom_number
495
+ if first_name is not None:
496
+ body["firstName"] = first_name
497
+ if last_name is not None:
498
+ body["lastName"] = last_name
499
+ if block_in_forward_calls_enabled is not None:
500
+ body["blockInForwardCallsEnabled"] = block_in_forward_calls_enabled
501
+ if external_caller_id_name_policy is not None:
502
+ body["externalCallerIdNamePolicy"] = external_caller_id_name_policy
503
+ if custom_external_caller_id_name is not None:
504
+ body["customExternalCallerIdName"] = custom_external_caller_id_name
505
+ if additional_external_caller_id_direct_line_enabled is not None:
506
+ body["additionalExternalCallerIdDirectLineEnabled"] = additional_external_caller_id_direct_line_enabled
507
+ if additional_external_caller_id_location_number_enabled is not None:
508
+ body["additionalExternalCallerIdLocationNumberEnabled"] = additional_external_caller_id_location_number_enabled
509
+ if additional_external_caller_id_custom_number is not None:
510
+ body["additionalExternalCallerIdCustomNumber"] = additional_external_caller_id_custom_number
511
+ if dial_by_first_name is not None:
512
+ body["dialByFirstName"] = dial_by_first_name
513
+ if dial_by_last_name is not None:
514
+ body["dialByLastName"] = dial_by_last_name
515
+ try:
516
+ result = api.session.rest_put(url, json=body, params=params)
517
+ except WebexError as e:
518
+ handle_rest_error(e)
519
+ typer.echo(f"Updated.")
520
+
521
+
522
+
523
+ @app.command("show-call-waiting")
524
+ def show_call_waiting(
525
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
526
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
527
+ debug: bool = typer.Option(False, "--debug"),
528
+ ):
529
+ """Read Call Waiting Settings for a Virtual Line."""
530
+ api = get_api(debug=debug)
531
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/callWaiting"
532
+ params = {}
533
+ org_id = get_org_id()
534
+ if org_id is not None:
535
+ params["orgId"] = org_id
536
+ try:
537
+ result = api.session.rest_get(url, params=params)
538
+ except WebexError as e:
539
+ handle_rest_error(e)
540
+ if output == "json":
541
+ print_json(result)
542
+ else:
543
+ if isinstance(result, dict):
544
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
545
+ elif isinstance(result, list):
546
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
547
+ else:
548
+ print_json(result)
549
+
550
+
551
+
552
+ @app.command("update-call-waiting")
553
+ def update_call_waiting(
554
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
555
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="`true` if the Call Waiting feature is enabled."),
556
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
557
+ debug: bool = typer.Option(False, "--debug"),
558
+ ):
559
+ """Configure Call Waiting Settings for a Virtual Line."""
560
+ api = get_api(debug=debug)
561
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/callWaiting"
562
+ params = {}
563
+ org_id = get_org_id()
564
+ if org_id is not None:
565
+ params["orgId"] = org_id
566
+ if json_body:
567
+ body = json.loads(json_body)
568
+ else:
569
+ body = {}
570
+ if enabled is not None:
571
+ body["enabled"] = enabled
572
+ try:
573
+ result = api.session.rest_put(url, json=body, params=params)
574
+ except WebexError as e:
575
+ handle_rest_error(e)
576
+ typer.echo(f"Updated.")
577
+
578
+
579
+
580
+ @app.command("show-call-forwarding")
581
+ def show_call_forwarding(
582
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
583
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
584
+ debug: bool = typer.Option(False, "--debug"),
585
+ ):
586
+ """Read Call Forwarding Settings for a Virtual Line."""
587
+ api = get_api(debug=debug)
588
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/callForwarding"
589
+ params = {}
590
+ org_id = get_org_id()
591
+ if org_id is not None:
592
+ params["orgId"] = org_id
593
+ try:
594
+ result = api.session.rest_get(url, params=params)
595
+ except WebexError as e:
596
+ handle_rest_error(e)
597
+ if output == "json":
598
+ print_json(result)
599
+ else:
600
+ if isinstance(result, dict):
601
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
602
+ elif isinstance(result, list):
603
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
604
+ else:
605
+ print_json(result)
606
+
607
+
608
+
609
+ @app.command("update-call-forwarding")
610
+ def update_call_forwarding(
611
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
612
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
613
+ debug: bool = typer.Option(False, "--debug"),
614
+ ):
615
+ """Configure Call Forwarding Settings for a Virtual Line\n\nExample --json-body:\n '{"callForwarding":{"always":{"enabled":"...","destination":"...","ringReminderEnabled":"...","destinationVoicemailEnabled":"..."},"busy":{"enabled":"...","destination":"...","destinationVoicemailEnabled":"..."},"noAnswer":{"enabled":"...","destination":"...","numberOfRings":"...","destinationVoicemailEnabled":"..."}},"businessContinuity":{"enabled":true,"destination":"...","destinationVoicemailEnabled":true}}'."""
616
+ api = get_api(debug=debug)
617
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/callForwarding"
618
+ params = {}
619
+ org_id = get_org_id()
620
+ if org_id is not None:
621
+ params["orgId"] = org_id
622
+ if json_body:
623
+ body = json.loads(json_body)
624
+ else:
625
+ body = {}
626
+ try:
627
+ result = api.session.rest_put(url, json=body, params=params)
628
+ except WebexError as e:
629
+ handle_rest_error(e)
630
+ typer.echo(f"Updated.")
631
+
632
+
633
+
634
+ @app.command("show-incoming-permission")
635
+ def show_incoming_permission(
636
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
637
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
638
+ debug: bool = typer.Option(False, "--debug"),
639
+ ):
640
+ """Read Incoming Permission Settings for a Virtual Line."""
641
+ api = get_api(debug=debug)
642
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/incomingPermission"
643
+ params = {}
644
+ org_id = get_org_id()
645
+ if org_id is not None:
646
+ params["orgId"] = org_id
647
+ try:
648
+ result = api.session.rest_get(url, params=params)
649
+ except WebexError as e:
650
+ handle_rest_error(e)
651
+ if output == "json":
652
+ print_json(result)
653
+ else:
654
+ if isinstance(result, dict):
655
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
656
+ elif isinstance(result, list):
657
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
658
+ else:
659
+ print_json(result)
660
+
661
+
662
+
663
+ @app.command("update-incoming-permission")
664
+ def update_incoming_permission(
665
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
666
+ use_custom_enabled: bool = typer.Option(None, "--use-custom-enabled/--no-use-custom-enabled", help="When true, indicates that this virtual line uses the specifi"),
667
+ external_transfer: str = typer.Option(None, "--external-transfer", help="Choices: ALLOW_ALL_EXTERNAL, ALLOW_ONLY_TRANSFERRED_EXTERNAL, BLOCK_ALL_EXTERNAL"),
668
+ internal_calls_enabled: bool = typer.Option(None, "--internal-calls-enabled/--no-internal-calls-enabled", help="Internal calls are allowed to be received."),
669
+ collect_calls_enabled: bool = typer.Option(None, "--collect-calls-enabled/--no-collect-calls-enabled", help="Collect calls are allowed to be received."),
670
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
671
+ debug: bool = typer.Option(False, "--debug"),
672
+ ):
673
+ """Configure Incoming Permission Settings for a Virtual Line."""
674
+ api = get_api(debug=debug)
675
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/incomingPermission"
676
+ params = {}
677
+ org_id = get_org_id()
678
+ if org_id is not None:
679
+ params["orgId"] = org_id
680
+ if json_body:
681
+ body = json.loads(json_body)
682
+ else:
683
+ body = {}
684
+ if use_custom_enabled is not None:
685
+ body["useCustomEnabled"] = use_custom_enabled
686
+ if external_transfer is not None:
687
+ body["externalTransfer"] = external_transfer
688
+ if internal_calls_enabled is not None:
689
+ body["internalCallsEnabled"] = internal_calls_enabled
690
+ if collect_calls_enabled is not None:
691
+ body["collectCallsEnabled"] = collect_calls_enabled
692
+ try:
693
+ result = api.session.rest_put(url, json=body, params=params)
694
+ except WebexError as e:
695
+ handle_rest_error(e)
696
+ typer.echo(f"Updated.")
697
+
698
+
699
+
700
+ @app.command("list-outgoing-permission")
701
+ def list_outgoing_permission(
702
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
703
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
704
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
705
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
706
+ debug: bool = typer.Option(False, "--debug"),
707
+ ):
708
+ """Retrieve a virtual line's Outgoing Calling Permissions Settings."""
709
+ api = get_api(debug=debug)
710
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/outgoingPermission"
711
+ params = {}
712
+ if limit > 0:
713
+ params["max"] = limit
714
+ if offset > 0:
715
+ params["start"] = offset
716
+ org_id = get_org_id()
717
+ if org_id is not None:
718
+ params["orgId"] = org_id
719
+ result = None
720
+ try:
721
+ result = api.session.rest_get(url, params=params)
722
+ except WebexError as e:
723
+ handle_rest_error(e)
724
+ result = result or []
725
+ items = result.get("callingPermissions", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
726
+ if output == "json":
727
+ print_json(items)
728
+ else:
729
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
730
+
731
+
732
+
733
+ @app.command("update-outgoing-permission")
734
+ def update_outgoing_permission(
735
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
736
+ use_custom_enabled: bool = typer.Option(None, "--use-custom-enabled/--no-use-custom-enabled", help="When true, indicates that this user uses the shared control"),
737
+ use_custom_permissions: bool = typer.Option(None, "--use-custom-permissions/--no-use-custom-permissions", help="When true, indicates that this user uses the specified outgo"),
738
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
739
+ debug: bool = typer.Option(False, "--debug"),
740
+ ):
741
+ """Modify a virtual line's Outgoing Calling Permissions Settings\n\nExample --json-body:\n '{"callingPermissions":[{"transferEnabled":"...","callType":"...","action":"..."}],"useCustomEnabled":true,"useCustomPermissions":true}'."""
742
+ api = get_api(debug=debug)
743
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/outgoingPermission"
744
+ params = {}
745
+ org_id = get_org_id()
746
+ if org_id is not None:
747
+ params["orgId"] = org_id
748
+ if json_body:
749
+ body = json.loads(json_body)
750
+ else:
751
+ body = {}
752
+ if use_custom_enabled is not None:
753
+ body["useCustomEnabled"] = use_custom_enabled
754
+ if use_custom_permissions is not None:
755
+ body["useCustomPermissions"] = use_custom_permissions
756
+ try:
757
+ result = api.session.rest_put(url, json=body, params=params)
758
+ except WebexError as e:
759
+ handle_rest_error(e)
760
+ typer.echo(f"Updated.")
761
+
762
+
763
+
764
+ @app.command("list-access-codes")
765
+ def list_access_codes(
766
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
767
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
768
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
769
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
770
+ debug: bool = typer.Option(False, "--debug"),
771
+ ):
772
+ """Retrieve Access Codes for a Virtual Line."""
773
+ api = get_api(debug=debug)
774
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/outgoingPermission/accessCodes"
775
+ params = {}
776
+ if limit > 0:
777
+ params["max"] = limit
778
+ if offset > 0:
779
+ params["start"] = offset
780
+ org_id = get_org_id()
781
+ if org_id is not None:
782
+ params["orgId"] = org_id
783
+ result = None
784
+ try:
785
+ result = api.session.rest_get(url, params=params)
786
+ except WebexError as e:
787
+ handle_rest_error(e)
788
+ result = result or []
789
+ items = result.get("accessCodes", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
790
+ if output == "json":
791
+ print_json(items)
792
+ else:
793
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
794
+
795
+
796
+
797
+ @app.command("create-access-codes")
798
+ def create_access_codes(
799
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
800
+ code: str = typer.Option(None, "--code", help="(required) An Access code."),
801
+ description: str = typer.Option(None, "--description", help="(required) The description of the access code."),
802
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
803
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
804
+ debug: bool = typer.Option(False, "--debug"),
805
+ ):
806
+ """Create Access Codes for a Virtual Line."""
807
+ api = get_api(debug=debug)
808
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/outgoingPermission/accessCodes"
809
+ params = {}
810
+ org_id = get_org_id()
811
+ if org_id is not None:
812
+ params["orgId"] = org_id
813
+ if json_body:
814
+ body = json.loads(json_body)
815
+ else:
816
+ body = {}
817
+ if code is not None:
818
+ body["code"] = code
819
+ if description is not None:
820
+ body["description"] = description
821
+ _missing = [f for f in ['code', 'description'] if f not in body or body[f] is None]
822
+ if _missing:
823
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
824
+ raise typer.Exit(1)
825
+ try:
826
+ result = api.session.rest_post(url, json=body, params=params)
827
+ except WebexError as e:
828
+ handle_rest_error(e)
829
+ if output == "json":
830
+ print_json(result)
831
+ elif isinstance(result, dict) and "id" in result:
832
+ typer.echo(f"Created: {result['id']}")
833
+ elif not result or result == {}:
834
+ typer.echo("Created.")
835
+ else:
836
+ print_json(result)
837
+
838
+
839
+
840
+ @app.command("update-access-codes")
841
+ def update_access_codes(
842
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
843
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
844
+ debug: bool = typer.Option(False, "--debug"),
845
+ ):
846
+ """Modify Access Codes for a Virtual Line\n\nExample --json-body:\n '{"deleteCodes":["..."]}'."""
847
+ api = get_api(debug=debug)
848
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/outgoingPermission/accessCodes"
849
+ params = {}
850
+ org_id = get_org_id()
851
+ if org_id is not None:
852
+ params["orgId"] = org_id
853
+ if json_body:
854
+ body = json.loads(json_body)
855
+ else:
856
+ body = {}
857
+ try:
858
+ result = api.session.rest_put(url, json=body, params=params)
859
+ except WebexError as e:
860
+ handle_rest_error(e)
861
+ typer.echo(f"Updated.")
862
+
863
+
864
+
865
+ @app.command("delete-access-codes")
866
+ def delete_access_codes(
867
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
868
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
869
+ debug: bool = typer.Option(False, "--debug"),
870
+ ):
871
+ """Delete Access Codes for a Virtual Line."""
872
+ if not force:
873
+ typer.confirm(f"Delete {virtual_line_id}?", abort=True)
874
+ api = get_api(debug=debug)
875
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/outgoingPermission/accessCodes"
876
+ params = {}
877
+ org_id = get_org_id()
878
+ if org_id is not None:
879
+ params["orgId"] = org_id
880
+ try:
881
+ api.session.rest_delete(url, params=params)
882
+ except WebexError as e:
883
+ handle_rest_error(e)
884
+ typer.echo(f"Deleted: {virtual_line_id}")
885
+
886
+
887
+
888
+ @app.command("show-auto-transfer-numbers")
889
+ def show_auto_transfer_numbers(
890
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
891
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
892
+ debug: bool = typer.Option(False, "--debug"),
893
+ ):
894
+ """Retrieve Transfer Numbers for a Virtual Line."""
895
+ api = get_api(debug=debug)
896
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/outgoingPermission/autoTransferNumbers"
897
+ params = {}
898
+ org_id = get_org_id()
899
+ if org_id is not None:
900
+ params["orgId"] = org_id
901
+ try:
902
+ result = api.session.rest_get(url, params=params)
903
+ except WebexError as e:
904
+ handle_rest_error(e)
905
+ if output == "json":
906
+ print_json(result)
907
+ else:
908
+ if isinstance(result, dict):
909
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
910
+ elif isinstance(result, list):
911
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
912
+ else:
913
+ print_json(result)
914
+
915
+
916
+
917
+ @app.command("update-auto-transfer-numbers")
918
+ def update_auto_transfer_numbers(
919
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
920
+ use_custom_transfer_numbers: bool = typer.Option(None, "--use-custom-transfer-numbers/--no-use-custom-transfer-numbers", help="When `true`, use custom settings for the transfer numbers ca"),
921
+ auto_transfer_number1: str = typer.Option(None, "--auto-transfer-number1", help="When calling a specific call type, this workspace will be au"),
922
+ auto_transfer_number2: str = typer.Option(None, "--auto-transfer-number2", help="When calling a specific call type, this workspace will be au"),
923
+ auto_transfer_number3: str = typer.Option(None, "--auto-transfer-number3", help="When calling a specific call type, this workspace will be au"),
924
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
925
+ debug: bool = typer.Option(False, "--debug"),
926
+ ):
927
+ """Modify Transfer Numbers for a Virtual Line."""
928
+ api = get_api(debug=debug)
929
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/outgoingPermission/autoTransferNumbers"
930
+ params = {}
931
+ org_id = get_org_id()
932
+ if org_id is not None:
933
+ params["orgId"] = org_id
934
+ if json_body:
935
+ body = json.loads(json_body)
936
+ else:
937
+ body = {}
938
+ if use_custom_transfer_numbers is not None:
939
+ body["useCustomTransferNumbers"] = use_custom_transfer_numbers
940
+ if auto_transfer_number1 is not None:
941
+ body["autoTransferNumber1"] = auto_transfer_number1
942
+ if auto_transfer_number2 is not None:
943
+ body["autoTransferNumber2"] = auto_transfer_number2
944
+ if auto_transfer_number3 is not None:
945
+ body["autoTransferNumber3"] = auto_transfer_number3
946
+ try:
947
+ result = api.session.rest_put(url, json=body, params=params)
948
+ except WebexError as e:
949
+ handle_rest_error(e)
950
+ typer.echo(f"Updated.")
951
+
952
+
953
+
954
+ @app.command("list-digit-patterns")
955
+ def list_digit_patterns(
956
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
957
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
958
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
959
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
960
+ debug: bool = typer.Option(False, "--debug"),
961
+ ):
962
+ """Retrieve Digit Patterns for a Virtual Profile."""
963
+ api = get_api(debug=debug)
964
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/outgoingPermission/digitPatterns"
965
+ params = {}
966
+ if limit > 0:
967
+ params["max"] = limit
968
+ if offset > 0:
969
+ params["start"] = offset
970
+ org_id = get_org_id()
971
+ if org_id is not None:
972
+ params["orgId"] = org_id
973
+ result = None
974
+ try:
975
+ result = api.session.rest_get(url, params=params)
976
+ except WebexError as e:
977
+ handle_rest_error(e)
978
+ result = result or []
979
+ items = result.get("digitPatterns", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
980
+ if output == "json":
981
+ print_json(items)
982
+ else:
983
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
984
+
985
+
986
+
987
+ @app.command("create-digit-patterns")
988
+ def create_digit_patterns(
989
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
990
+ name: str = typer.Option(None, "--name", help="(required) A unique name for the digit pattern."),
991
+ pattern: str = typer.Option(None, "--pattern", help="(required) The digit pattern to be matched with the input number."),
992
+ action: str = typer.Option(None, "--action", help="(required) Choices: ALLOW, BLOCK, AUTH_CODE, TRANSFER_NUMBER_1, TRANSFER_NUMBER_2, TRANSFER_NUMBER_3"),
993
+ transfer_enabled: bool = typer.Option(None, "--transfer-enabled/--no-transfer-enabled", help="(required) If `true`, allows transfer and forwarding for the call type."),
994
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
995
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
996
+ debug: bool = typer.Option(False, "--debug"),
997
+ ):
998
+ """Create Digit Pattern for a Virtual Profile."""
999
+ api = get_api(debug=debug)
1000
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/outgoingPermission/digitPatterns"
1001
+ params = {}
1002
+ org_id = get_org_id()
1003
+ if org_id is not None:
1004
+ params["orgId"] = org_id
1005
+ if json_body:
1006
+ body = json.loads(json_body)
1007
+ else:
1008
+ body = {}
1009
+ if name is not None:
1010
+ body["name"] = name
1011
+ if pattern is not None:
1012
+ body["pattern"] = pattern
1013
+ if action is not None:
1014
+ body["action"] = action
1015
+ if transfer_enabled is not None:
1016
+ body["transferEnabled"] = transfer_enabled
1017
+ _missing = [f for f in ['name', 'pattern', 'action', 'transferEnabled'] if f not in body or body[f] is None]
1018
+ if _missing:
1019
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
1020
+ raise typer.Exit(1)
1021
+ try:
1022
+ result = api.session.rest_post(url, json=body, params=params)
1023
+ except WebexError as e:
1024
+ handle_rest_error(e)
1025
+ if output == "json":
1026
+ print_json(result)
1027
+ elif isinstance(result, dict) and "id" in result:
1028
+ typer.echo(f"Created: {result['id']}")
1029
+ elif not result or result == {}:
1030
+ typer.echo("Created.")
1031
+ else:
1032
+ print_json(result)
1033
+
1034
+
1035
+
1036
+ @app.command("update-digit-patterns-outgoing-permission")
1037
+ def update_digit_patterns_outgoing_permission(
1038
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1039
+ use_custom_digit_patterns: bool = typer.Option(None, "--use-custom-digit-patterns/--no-use-custom-digit-patterns", help="When `true`, use custom settings for the digit patterns cate"),
1040
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1041
+ debug: bool = typer.Option(False, "--debug"),
1042
+ ):
1043
+ """Modify the Digit Pattern Category Control Settings for a Virtual Profile."""
1044
+ api = get_api(debug=debug)
1045
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/outgoingPermission/digitPatterns"
1046
+ params = {}
1047
+ org_id = get_org_id()
1048
+ if org_id is not None:
1049
+ params["orgId"] = org_id
1050
+ if json_body:
1051
+ body = json.loads(json_body)
1052
+ else:
1053
+ body = {}
1054
+ if use_custom_digit_patterns is not None:
1055
+ body["useCustomDigitPatterns"] = use_custom_digit_patterns
1056
+ try:
1057
+ result = api.session.rest_put(url, json=body, params=params)
1058
+ except WebexError as e:
1059
+ handle_rest_error(e)
1060
+ typer.echo(f"Updated.")
1061
+
1062
+
1063
+
1064
+ @app.command("delete-digit-patterns-outgoing-permission")
1065
+ def delete_digit_patterns_outgoing_permission(
1066
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1067
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
1068
+ debug: bool = typer.Option(False, "--debug"),
1069
+ ):
1070
+ """Delete all Digit Patterns for a Virtual Profile."""
1071
+ if not force:
1072
+ typer.confirm(f"Delete {virtual_line_id}?", abort=True)
1073
+ api = get_api(debug=debug)
1074
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/outgoingPermission/digitPatterns"
1075
+ params = {}
1076
+ org_id = get_org_id()
1077
+ if org_id is not None:
1078
+ params["orgId"] = org_id
1079
+ try:
1080
+ api.session.rest_delete(url, params=params)
1081
+ except WebexError as e:
1082
+ handle_rest_error(e)
1083
+ typer.echo(f"Deleted: {virtual_line_id}")
1084
+
1085
+
1086
+
1087
+ @app.command("show-digit-patterns")
1088
+ def show_digit_patterns(
1089
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1090
+ digit_pattern_id: str = typer.Argument(help="digitPatternId"),
1091
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1092
+ debug: bool = typer.Option(False, "--debug"),
1093
+ ):
1094
+ """Retrieve Specified Digit Pattern Details for a Virtual Profile."""
1095
+ api = get_api(debug=debug)
1096
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/outgoingPermission/digitPatterns/{digit_pattern_id}"
1097
+ params = {}
1098
+ org_id = get_org_id()
1099
+ if org_id is not None:
1100
+ params["orgId"] = org_id
1101
+ try:
1102
+ result = api.session.rest_get(url, params=params)
1103
+ except WebexError as e:
1104
+ handle_rest_error(e)
1105
+ if output == "json":
1106
+ print_json(result)
1107
+ else:
1108
+ if isinstance(result, dict):
1109
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1110
+ elif isinstance(result, list):
1111
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1112
+ else:
1113
+ print_json(result)
1114
+
1115
+
1116
+
1117
+ @app.command("update-digit-patterns-outgoing-permission-1")
1118
+ def update_digit_patterns_outgoing_permission_1(
1119
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1120
+ digit_pattern_id: str = typer.Argument(help="digitPatternId"),
1121
+ name: str = typer.Option(None, "--name", help="A unique name for the digit pattern."),
1122
+ pattern: str = typer.Option(None, "--pattern", help="The digit pattern to be matched with the input number."),
1123
+ action: str = typer.Option(None, "--action", help="Choices: ALLOW, BLOCK, AUTH_CODE, TRANSFER_NUMBER_1, TRANSFER_NUMBER_2, TRANSFER_NUMBER_3"),
1124
+ transfer_enabled: bool = typer.Option(None, "--transfer-enabled/--no-transfer-enabled", help="If `true`, allows transfer and forwarding for the call type."),
1125
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1126
+ debug: bool = typer.Option(False, "--debug"),
1127
+ ):
1128
+ """Modify a Digit Pattern for a Virtual Profile."""
1129
+ api = get_api(debug=debug)
1130
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/outgoingPermission/digitPatterns/{digit_pattern_id}"
1131
+ params = {}
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 name is not None:
1140
+ body["name"] = name
1141
+ if pattern is not None:
1142
+ body["pattern"] = pattern
1143
+ if action is not None:
1144
+ body["action"] = action
1145
+ if transfer_enabled is not None:
1146
+ body["transferEnabled"] = transfer_enabled
1147
+ try:
1148
+ result = api.session.rest_put(url, json=body, params=params)
1149
+ except WebexError as e:
1150
+ handle_rest_error(e)
1151
+ typer.echo(f"Updated.")
1152
+
1153
+
1154
+
1155
+ @app.command("delete-digit-patterns-outgoing-permission-1")
1156
+ def delete_digit_patterns_outgoing_permission_1(
1157
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1158
+ digit_pattern_id: str = typer.Argument(help="digitPatternId"),
1159
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
1160
+ debug: bool = typer.Option(False, "--debug"),
1161
+ ):
1162
+ """Delete a Digit Pattern for a Virtual Profile."""
1163
+ if not force:
1164
+ typer.confirm(f"Delete {digit_pattern_id}?", abort=True)
1165
+ api = get_api(debug=debug)
1166
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/outgoingPermission/digitPatterns/{digit_pattern_id}"
1167
+ params = {}
1168
+ org_id = get_org_id()
1169
+ if org_id is not None:
1170
+ params["orgId"] = org_id
1171
+ try:
1172
+ api.session.rest_delete(url, params=params)
1173
+ except WebexError as e:
1174
+ handle_rest_error(e)
1175
+ typer.echo(f"Deleted: {digit_pattern_id}")
1176
+
1177
+
1178
+
1179
+ @app.command("show-intercept")
1180
+ def show_intercept(
1181
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1182
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1183
+ debug: bool = typer.Option(False, "--debug"),
1184
+ ):
1185
+ """Read Call Intercept Settings for a Virtual Line."""
1186
+ api = get_api(debug=debug)
1187
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/intercept"
1188
+ params = {}
1189
+ org_id = get_org_id()
1190
+ if org_id is not None:
1191
+ params["orgId"] = org_id
1192
+ try:
1193
+ result = api.session.rest_get(url, params=params)
1194
+ except WebexError as e:
1195
+ handle_rest_error(e)
1196
+ if output == "json":
1197
+ print_json(result)
1198
+ else:
1199
+ if isinstance(result, dict):
1200
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1201
+ elif isinstance(result, list):
1202
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1203
+ else:
1204
+ print_json(result)
1205
+
1206
+
1207
+
1208
+ @app.command("update-intercept")
1209
+ def update_intercept(
1210
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1211
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="`true` if the intercept feature is enabled."),
1212
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1213
+ debug: bool = typer.Option(False, "--debug"),
1214
+ ):
1215
+ """Configure Call Intercept Settings for a Virtual Line\n\nExample --json-body:\n '{"enabled":true,"incoming":{"type":"INTERCEPT_ALL","voicemailEnabled":true,"announcements":{"greeting":"...","newNumber":"...","zeroTransfer":"..."}},"outgoing":{"type":"INTERCEPT_ALL","transferEnabled":true,"destination":"..."}}'."""
1216
+ api = get_api(debug=debug)
1217
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/intercept"
1218
+ params = {}
1219
+ org_id = get_org_id()
1220
+ if org_id is not None:
1221
+ params["orgId"] = org_id
1222
+ if json_body:
1223
+ body = json.loads(json_body)
1224
+ else:
1225
+ body = {}
1226
+ if enabled is not None:
1227
+ body["enabled"] = enabled
1228
+ try:
1229
+ result = api.session.rest_put(url, json=body, params=params)
1230
+ except WebexError as e:
1231
+ handle_rest_error(e)
1232
+ typer.echo(f"Updated.")
1233
+
1234
+
1235
+
1236
+ @app.command("configure-call-intercept")
1237
+ def configure_call_intercept(
1238
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1239
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body"),
1240
+ debug: bool = typer.Option(False, "--debug"),
1241
+ ):
1242
+ """Configure Call Intercept Greeting for a Virtual Line."""
1243
+ api = get_api(debug=debug)
1244
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/intercept/actions/announcementUpload/invoke"
1245
+ params = {}
1246
+ org_id = get_org_id()
1247
+ if org_id is not None:
1248
+ params["orgId"] = org_id
1249
+ if json_body:
1250
+ body = json.loads(json_body)
1251
+ else:
1252
+ body = {}
1253
+ try:
1254
+ result = api.session.rest_post(url, json=body, params=params)
1255
+ except WebexError as e:
1256
+ handle_rest_error(e)
1257
+ print_json(result)
1258
+
1259
+
1260
+
1261
+ @app.command("list-available-caller-ids")
1262
+ def list_available_caller_ids(
1263
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1264
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1265
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1266
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1267
+ debug: bool = typer.Option(False, "--debug"),
1268
+ ):
1269
+ """Retrieve Agent's List of Available Caller IDs."""
1270
+ api = get_api(debug=debug)
1271
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/agent/availableCallerIds"
1272
+ params = {}
1273
+ if limit > 0:
1274
+ params["max"] = limit
1275
+ if offset > 0:
1276
+ params["start"] = offset
1277
+ org_id = get_org_id()
1278
+ if org_id is not None:
1279
+ params["orgId"] = org_id
1280
+ result = None
1281
+ try:
1282
+ result = api.session.rest_get(url, params=params)
1283
+ except WebexError as e:
1284
+ handle_rest_error(e)
1285
+ result = result or []
1286
+ items = result.get("availableCallerIds", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1287
+ if output == "json":
1288
+ print_json(items)
1289
+ else:
1290
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
1291
+
1292
+
1293
+
1294
+ @app.command("show-caller-id")
1295
+ def show_caller_id(
1296
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1297
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1298
+ debug: bool = typer.Option(False, "--debug"),
1299
+ ):
1300
+ """Retrieve Agent's Caller ID Information."""
1301
+ api = get_api(debug=debug)
1302
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/agent/callerId"
1303
+ try:
1304
+ result = api.session.rest_get(url)
1305
+ except WebexError as e:
1306
+ handle_rest_error(e)
1307
+ if output == "json":
1308
+ print_json(result)
1309
+ else:
1310
+ if isinstance(result, dict):
1311
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1312
+ elif isinstance(result, list):
1313
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1314
+ else:
1315
+ print_json(result)
1316
+
1317
+
1318
+
1319
+ @app.command("update-caller-id-agent")
1320
+ def update_caller_id_agent(
1321
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1322
+ selected_caller_id: str = typer.Option(None, "--selected-caller-id", help="The unique identifier of the call queue or hunt group to use"),
1323
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1324
+ debug: bool = typer.Option(False, "--debug"),
1325
+ ):
1326
+ """Modify Agent's Caller ID Information."""
1327
+ api = get_api(debug=debug)
1328
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/agent/callerId"
1329
+ if json_body:
1330
+ body = json.loads(json_body)
1331
+ else:
1332
+ body = {}
1333
+ if selected_caller_id is not None:
1334
+ body["selectedCallerId"] = selected_caller_id
1335
+ try:
1336
+ result = api.session.rest_put(url, json=body)
1337
+ except WebexError as e:
1338
+ handle_rest_error(e)
1339
+ typer.echo(f"Updated.")
1340
+
1341
+
1342
+
1343
+ @app.command("show-voicemail")
1344
+ def show_voicemail(
1345
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1346
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1347
+ debug: bool = typer.Option(False, "--debug"),
1348
+ ):
1349
+ """Read Voicemail Settings for a Virtual Line."""
1350
+ api = get_api(debug=debug)
1351
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/voicemail"
1352
+ params = {}
1353
+ org_id = get_org_id()
1354
+ if org_id is not None:
1355
+ params["orgId"] = org_id
1356
+ try:
1357
+ result = api.session.rest_get(url, params=params)
1358
+ except WebexError as e:
1359
+ handle_rest_error(e)
1360
+ if output == "json":
1361
+ print_json(result)
1362
+ else:
1363
+ if isinstance(result, dict):
1364
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1365
+ elif isinstance(result, list):
1366
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1367
+ else:
1368
+ print_json(result)
1369
+
1370
+
1371
+
1372
+ @app.command("update-voicemail")
1373
+ def update_voicemail(
1374
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1375
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="Voicemail is enabled or disabled."),
1376
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1377
+ debug: bool = typer.Option(False, "--debug"),
1378
+ ):
1379
+ """Configure Voicemail Settings for a Virtual Line\n\nExample --json-body:\n '{"notifications":{"enabled":true,"destination":"...","smsDestination":"..."},"transferToNumber":{"enabled":true,"destination":"..."},"enabled":true,"sendAllCalls":{"enabled":true},"sendBusyCalls":{"enabled":true,"greeting":"DEFAULT"},"sendUnansweredCalls":{"enabled":true,"greeting":"DEFAULT","numberOfRings":0},"emailCopyOfMessage":{"enabled":true,"emailId":"..."},"messageStorage":{"mwiEnabled":true,"storageType":"INTERNAL","externalEmail":"..."}}'."""
1380
+ api = get_api(debug=debug)
1381
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/voicemail"
1382
+ params = {}
1383
+ org_id = get_org_id()
1384
+ if org_id is not None:
1385
+ params["orgId"] = org_id
1386
+ if json_body:
1387
+ body = json.loads(json_body)
1388
+ else:
1389
+ body = {}
1390
+ if enabled is not None:
1391
+ body["enabled"] = enabled
1392
+ try:
1393
+ result = api.session.rest_put(url, json=body, params=params)
1394
+ except WebexError as e:
1395
+ handle_rest_error(e)
1396
+ typer.echo(f"Updated.")
1397
+
1398
+
1399
+
1400
+ @app.command("configure-busy-voicemail")
1401
+ def configure_busy_voicemail(
1402
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1403
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body"),
1404
+ debug: bool = typer.Option(False, "--debug"),
1405
+ ):
1406
+ """Configure Busy Voicemail Greeting for a Virtual Line."""
1407
+ api = get_api(debug=debug)
1408
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/voicemail/actions/uploadBusyGreeting/invoke"
1409
+ params = {}
1410
+ org_id = get_org_id()
1411
+ if org_id is not None:
1412
+ params["orgId"] = org_id
1413
+ if json_body:
1414
+ body = json.loads(json_body)
1415
+ else:
1416
+ body = {}
1417
+ try:
1418
+ result = api.session.rest_post(url, json=body, params=params)
1419
+ except WebexError as e:
1420
+ handle_rest_error(e)
1421
+ print_json(result)
1422
+
1423
+
1424
+
1425
+ @app.command("configure-no-answer")
1426
+ def configure_no_answer(
1427
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1428
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body"),
1429
+ debug: bool = typer.Option(False, "--debug"),
1430
+ ):
1431
+ """Configure No Answer Voicemail Greeting for a Virtual Line."""
1432
+ api = get_api(debug=debug)
1433
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/voicemail/actions/uploadNoAnswerGreeting/invoke"
1434
+ params = {}
1435
+ org_id = get_org_id()
1436
+ if org_id is not None:
1437
+ params["orgId"] = org_id
1438
+ if json_body:
1439
+ body = json.loads(json_body)
1440
+ else:
1441
+ body = {}
1442
+ try:
1443
+ result = api.session.rest_post(url, json=body, params=params)
1444
+ except WebexError as e:
1445
+ handle_rest_error(e)
1446
+ print_json(result)
1447
+
1448
+
1449
+
1450
+ @app.command("reset-voicemail-pin")
1451
+ def reset_voicemail_pin(
1452
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1453
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body"),
1454
+ debug: bool = typer.Option(False, "--debug"),
1455
+ ):
1456
+ """Reset Voicemail PIN for a Virtual Line."""
1457
+ api = get_api(debug=debug)
1458
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/voicemail/actions/resetPin/invoke"
1459
+ params = {}
1460
+ org_id = get_org_id()
1461
+ if org_id is not None:
1462
+ params["orgId"] = org_id
1463
+ if json_body:
1464
+ body = json.loads(json_body)
1465
+ else:
1466
+ body = {}
1467
+ try:
1468
+ result = api.session.rest_post(url, json=body, params=params)
1469
+ except WebexError as e:
1470
+ handle_rest_error(e)
1471
+ print_json(result)
1472
+
1473
+
1474
+
1475
+ @app.command("update-passcode")
1476
+ def update_passcode(
1477
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1478
+ passcode: str = typer.Option(None, "--passcode", help="Voicemail access passcode. The minimum length of the passcod"),
1479
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1480
+ debug: bool = typer.Option(False, "--debug"),
1481
+ ):
1482
+ """Modify a virtual line's voicemail passcode."""
1483
+ api = get_api(debug=debug)
1484
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/voicemail/passcode"
1485
+ params = {}
1486
+ org_id = get_org_id()
1487
+ if org_id is not None:
1488
+ params["orgId"] = org_id
1489
+ if json_body:
1490
+ body = json.loads(json_body)
1491
+ else:
1492
+ body = {}
1493
+ if passcode is not None:
1494
+ body["passcode"] = passcode
1495
+ try:
1496
+ result = api.session.rest_put(url, json=body, params=params)
1497
+ except WebexError as e:
1498
+ handle_rest_error(e)
1499
+ typer.echo(f"Updated.")
1500
+
1501
+
1502
+
1503
+ @app.command("show-music-on-hold")
1504
+ def show_music_on_hold(
1505
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1506
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1507
+ debug: bool = typer.Option(False, "--debug"),
1508
+ ):
1509
+ """Retrieve Music On Hold Settings for a Virtual Line."""
1510
+ api = get_api(debug=debug)
1511
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/musicOnHold"
1512
+ params = {}
1513
+ org_id = get_org_id()
1514
+ if org_id is not None:
1515
+ params["orgId"] = org_id
1516
+ try:
1517
+ result = api.session.rest_get(url, params=params)
1518
+ except WebexError as e:
1519
+ handle_rest_error(e)
1520
+ if output == "json":
1521
+ print_json(result)
1522
+ else:
1523
+ if isinstance(result, dict):
1524
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1525
+ elif isinstance(result, list):
1526
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1527
+ else:
1528
+ print_json(result)
1529
+
1530
+
1531
+
1532
+ @app.command("update-music-on-hold")
1533
+ def update_music_on_hold(
1534
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1535
+ moh_enabled: bool = typer.Option(None, "--moh-enabled/--no-moh-enabled", help="Music on hold is enabled or disabled for the workspace."),
1536
+ greeting: str = typer.Option(None, "--greeting", help="Choices: DEFAULT, CUSTOM"),
1537
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1538
+ debug: bool = typer.Option(False, "--debug"),
1539
+ ):
1540
+ """Configure Music On Hold Settings for a Virtual Line\n\nExample --json-body:\n '{"mohEnabled":true,"greeting":"DEFAULT","audioAnnouncementFile":{"id":"...","fileName":"...","mediaFileType":"WAV","level":"ORGANIZATION"}}'."""
1541
+ api = get_api(debug=debug)
1542
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/musicOnHold"
1543
+ params = {}
1544
+ org_id = get_org_id()
1545
+ if org_id is not None:
1546
+ params["orgId"] = org_id
1547
+ if json_body:
1548
+ body = json.loads(json_body)
1549
+ else:
1550
+ body = {}
1551
+ if moh_enabled is not None:
1552
+ body["mohEnabled"] = moh_enabled
1553
+ if greeting is not None:
1554
+ body["greeting"] = greeting
1555
+ try:
1556
+ result = api.session.rest_put(url, json=body, params=params)
1557
+ except WebexError as e:
1558
+ handle_rest_error(e)
1559
+ typer.echo(f"Updated.")
1560
+
1561
+
1562
+
1563
+ @app.command("list-push-to-talk")
1564
+ def list_push_to_talk(
1565
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1566
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1567
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1568
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1569
+ debug: bool = typer.Option(False, "--debug"),
1570
+ ):
1571
+ """Read Push-to-Talk Settings for a Virtual Line."""
1572
+ api = get_api(debug=debug)
1573
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/pushToTalk"
1574
+ params = {}
1575
+ if limit > 0:
1576
+ params["max"] = limit
1577
+ if offset > 0:
1578
+ params["start"] = offset
1579
+ org_id = get_org_id()
1580
+ if org_id is not None:
1581
+ params["orgId"] = org_id
1582
+ result = None
1583
+ try:
1584
+ result = api.session.rest_get(url, params=params)
1585
+ except WebexError as e:
1586
+ handle_rest_error(e)
1587
+ result = result or []
1588
+ items = result.get("members", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1589
+ if output == "json":
1590
+ print_json(items)
1591
+ else:
1592
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
1593
+
1594
+
1595
+
1596
+ @app.command("update-push-to-talk")
1597
+ def update_push_to_talk(
1598
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1599
+ allow_auto_answer: bool = typer.Option(None, "--allow-auto-answer/--no-allow-auto-answer", help="`true` if Push-to-Talk feature is enabled."),
1600
+ connection_type: str = typer.Option(None, "--connection-type", help="Choices: ONE_WAY, TWO_WAY"),
1601
+ access_type: str = typer.Option(None, "--access-type", help="Choices: ALLOW_MEMBERS, BLOCK_MEMBERS"),
1602
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1603
+ debug: bool = typer.Option(False, "--debug"),
1604
+ ):
1605
+ """Configure Push-to-Talk Settings for a Virtual Line\n\nExample --json-body:\n '{"allowAutoAnswer":true,"connectionType":"ONE_WAY","accessType":"ALLOW_MEMBERS","members":["..."]}'."""
1606
+ api = get_api(debug=debug)
1607
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/pushToTalk"
1608
+ params = {}
1609
+ org_id = get_org_id()
1610
+ if org_id is not None:
1611
+ params["orgId"] = org_id
1612
+ if json_body:
1613
+ body = json.loads(json_body)
1614
+ else:
1615
+ body = {}
1616
+ if allow_auto_answer is not None:
1617
+ body["allowAutoAnswer"] = allow_auto_answer
1618
+ if connection_type is not None:
1619
+ body["connectionType"] = connection_type
1620
+ if access_type is not None:
1621
+ body["accessType"] = access_type
1622
+ try:
1623
+ result = api.session.rest_put(url, json=body, params=params)
1624
+ except WebexError as e:
1625
+ handle_rest_error(e)
1626
+ typer.echo(f"Updated.")
1627
+
1628
+
1629
+
1630
+ @app.command("show-call-bridge")
1631
+ def show_call_bridge(
1632
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1633
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1634
+ debug: bool = typer.Option(False, "--debug"),
1635
+ ):
1636
+ """Read Call Bridge Settings for a Virtual Line."""
1637
+ api = get_api(debug=debug)
1638
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/callBridge"
1639
+ params = {}
1640
+ org_id = get_org_id()
1641
+ if org_id is not None:
1642
+ params["orgId"] = org_id
1643
+ try:
1644
+ result = api.session.rest_get(url, params=params)
1645
+ except WebexError as e:
1646
+ handle_rest_error(e)
1647
+ if output == "json":
1648
+ print_json(result)
1649
+ else:
1650
+ if isinstance(result, dict):
1651
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1652
+ elif isinstance(result, list):
1653
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1654
+ else:
1655
+ print_json(result)
1656
+
1657
+
1658
+
1659
+ @app.command("update-call-bridge")
1660
+ def update_call_bridge(
1661
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1662
+ warning_tone_enabled: bool = typer.Option(None, "--warning-tone-enabled/--no-warning-tone-enabled", help="Set to enable or disable a stutter dial tone being played to"),
1663
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1664
+ debug: bool = typer.Option(False, "--debug"),
1665
+ ):
1666
+ """Configure Call Bridge Settings for a Virtual Line."""
1667
+ api = get_api(debug=debug)
1668
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/callBridge"
1669
+ params = {}
1670
+ org_id = get_org_id()
1671
+ if org_id is not None:
1672
+ params["orgId"] = org_id
1673
+ if json_body:
1674
+ body = json.loads(json_body)
1675
+ else:
1676
+ body = {}
1677
+ if warning_tone_enabled is not None:
1678
+ body["warningToneEnabled"] = warning_tone_enabled
1679
+ try:
1680
+ result = api.session.rest_put(url, json=body, params=params)
1681
+ except WebexError as e:
1682
+ handle_rest_error(e)
1683
+ typer.echo(f"Updated.")
1684
+
1685
+
1686
+
1687
+ @app.command("show-barge-in")
1688
+ def show_barge_in(
1689
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1690
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1691
+ debug: bool = typer.Option(False, "--debug"),
1692
+ ):
1693
+ """Read Barge In Settings for a Virtual Line."""
1694
+ api = get_api(debug=debug)
1695
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/bargeIn"
1696
+ params = {}
1697
+ org_id = get_org_id()
1698
+ if org_id is not None:
1699
+ params["orgId"] = org_id
1700
+ try:
1701
+ result = api.session.rest_get(url, params=params)
1702
+ except WebexError as e:
1703
+ handle_rest_error(e)
1704
+ if output == "json":
1705
+ print_json(result)
1706
+ else:
1707
+ if isinstance(result, dict):
1708
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1709
+ elif isinstance(result, list):
1710
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1711
+ else:
1712
+ print_json(result)
1713
+
1714
+
1715
+
1716
+ @app.command("update-barge-in")
1717
+ def update_barge_in(
1718
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1719
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="Set to enable or disable the Barge In feature."),
1720
+ tone_enabled: bool = typer.Option(None, "--tone-enabled/--no-tone-enabled", help="Set to enable or disable a stutter dial tone being played wh"),
1721
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1722
+ debug: bool = typer.Option(False, "--debug"),
1723
+ ):
1724
+ """Configure Barge In Settings for a Virtual Line."""
1725
+ api = get_api(debug=debug)
1726
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/bargeIn"
1727
+ params = {}
1728
+ org_id = get_org_id()
1729
+ if org_id is not None:
1730
+ params["orgId"] = org_id
1731
+ if json_body:
1732
+ body = json.loads(json_body)
1733
+ else:
1734
+ body = {}
1735
+ if enabled is not None:
1736
+ body["enabled"] = enabled
1737
+ if tone_enabled is not None:
1738
+ body["toneEnabled"] = tone_enabled
1739
+ try:
1740
+ result = api.session.rest_put(url, json=body, params=params)
1741
+ except WebexError as e:
1742
+ handle_rest_error(e)
1743
+ typer.echo(f"Updated.")
1744
+
1745
+
1746
+
1747
+ @app.command("list-privacy")
1748
+ def list_privacy(
1749
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1750
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1751
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1752
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1753
+ debug: bool = typer.Option(False, "--debug"),
1754
+ ):
1755
+ """Get a Virtual Line's Privacy Settings."""
1756
+ api = get_api(debug=debug)
1757
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/privacy"
1758
+ params = {}
1759
+ if limit > 0:
1760
+ params["max"] = limit
1761
+ if offset > 0:
1762
+ params["start"] = offset
1763
+ org_id = get_org_id()
1764
+ if org_id is not None:
1765
+ params["orgId"] = org_id
1766
+ result = None
1767
+ try:
1768
+ result = api.session.rest_get(url, params=params)
1769
+ except WebexError as e:
1770
+ handle_rest_error(e)
1771
+ result = result or []
1772
+ items = result.get("monitoringAgents", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1773
+ if output == "json":
1774
+ print_json(items)
1775
+ else:
1776
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
1777
+
1778
+
1779
+
1780
+ @app.command("update-privacy")
1781
+ def update_privacy(
1782
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1783
+ aa_extension_dialing_enabled: bool = typer.Option(None, "--aa-extension-dialing-enabled/--no-aa-extension-dialing-enabled", help="When `true` auto attendant extension dialing is enabled."),
1784
+ aa_naming_dialing_enabled: bool = typer.Option(None, "--aa-naming-dialing-enabled/--no-aa-naming-dialing-enabled", help="When `true` auto attendant dialing by first or last name is"),
1785
+ enable_phone_status_directory_privacy: bool = typer.Option(None, "--enable-phone-status-directory-privacy/--no-enable-phone-status-directory-privacy", help="When `true` phone status directory privacy is enabled."),
1786
+ enable_phone_status_pickup_barge_in_privacy: bool = typer.Option(None, "--enable-phone-status-pickup-barge-in-privacy/--no-enable-phone-status-pickup-barge-in-privacy", help="When `true` privacy is enforced for call pickup and barge-in"),
1787
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1788
+ debug: bool = typer.Option(False, "--debug"),
1789
+ ):
1790
+ """Configure a Virtual Line's Privacy Settings\n\nExample --json-body:\n '{"aaExtensionDialingEnabled":true,"aaNamingDialingEnabled":true,"enablePhoneStatusDirectoryPrivacy":true,"enablePhoneStatusPickupBargeInPrivacy":true,"monitoringAgents":["..."]}'."""
1791
+ api = get_api(debug=debug)
1792
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/privacy"
1793
+ params = {}
1794
+ org_id = get_org_id()
1795
+ if org_id is not None:
1796
+ params["orgId"] = org_id
1797
+ if json_body:
1798
+ body = json.loads(json_body)
1799
+ else:
1800
+ body = {}
1801
+ if aa_extension_dialing_enabled is not None:
1802
+ body["aaExtensionDialingEnabled"] = aa_extension_dialing_enabled
1803
+ if aa_naming_dialing_enabled is not None:
1804
+ body["aaNamingDialingEnabled"] = aa_naming_dialing_enabled
1805
+ if enable_phone_status_directory_privacy is not None:
1806
+ body["enablePhoneStatusDirectoryPrivacy"] = enable_phone_status_directory_privacy
1807
+ if enable_phone_status_pickup_barge_in_privacy is not None:
1808
+ body["enablePhoneStatusPickupBargeInPrivacy"] = enable_phone_status_pickup_barge_in_privacy
1809
+ try:
1810
+ result = api.session.rest_put(url, json=body, params=params)
1811
+ except WebexError as e:
1812
+ handle_rest_error(e)
1813
+ typer.echo(f"Updated.")
1814
+
1815
+
1816
+
1817
+ @app.command("list-available-numbers-fax-message")
1818
+ def list_available_numbers_fax_message(
1819
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1820
+ phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
1821
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1822
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1823
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1824
+ debug: bool = typer.Option(False, "--debug"),
1825
+ ):
1826
+ """Get Virtual Line Fax Message Available Phone Numbers."""
1827
+ api = get_api(debug=debug)
1828
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/faxMessage/availableNumbers"
1829
+ params = {}
1830
+ if phone_number is not None:
1831
+ params["phoneNumber"] = phone_number
1832
+ if limit > 0:
1833
+ params["max"] = limit
1834
+ if offset > 0:
1835
+ params["start"] = offset
1836
+ org_id = get_org_id()
1837
+ if org_id is not None:
1838
+ params["orgId"] = org_id
1839
+ result = None
1840
+ try:
1841
+ result = api.session.rest_get(url, params=params)
1842
+ except WebexError as e:
1843
+ handle_rest_error(e)
1844
+ result = result or []
1845
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1846
+ if output == "json":
1847
+ print_json(items)
1848
+ else:
1849
+ print_table(items, columns=[('Phone Number', 'phoneNumber'), ('State', 'state')], limit=limit)
1850
+
1851
+
1852
+
1853
+ @app.command("list-available-numbers-call-forwarding")
1854
+ def list_available_numbers_call_forwarding(
1855
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1856
+ phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
1857
+ owner_name: str = typer.Option(None, "--owner-name", help="Return the list of phone numbers that are owned by the given"),
1858
+ extension: str = typer.Option(None, "--extension", help="Returns the list of PSTN phone numbers with the given `exten"),
1859
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1860
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1861
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1862
+ debug: bool = typer.Option(False, "--debug"),
1863
+ ):
1864
+ """Get Virtual Line Call Forward Available Phone Numbers."""
1865
+ api = get_api(debug=debug)
1866
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/callForwarding/availableNumbers"
1867
+ params = {}
1868
+ if phone_number is not None:
1869
+ params["phoneNumber"] = phone_number
1870
+ if owner_name is not None:
1871
+ params["ownerName"] = owner_name
1872
+ if extension is not None:
1873
+ params["extension"] = extension
1874
+ if limit > 0:
1875
+ params["max"] = limit
1876
+ if offset > 0:
1877
+ params["start"] = offset
1878
+ org_id = get_org_id()
1879
+ if org_id is not None:
1880
+ params["orgId"] = org_id
1881
+ result = None
1882
+ try:
1883
+ result = api.session.rest_get(url, params=params)
1884
+ except WebexError as e:
1885
+ handle_rest_error(e)
1886
+ result = result or []
1887
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1888
+ if output == "json":
1889
+ print_json(items)
1890
+ else:
1891
+ print_table(items, columns=[('Phone Number', 'phoneNumber'), ('State', 'state')], limit=limit)
1892
+
1893
+
1894
+
1895
+ @app.command("list-available-numbers-virtual-lines")
1896
+ def list_available_numbers_virtual_lines(
1897
+ location_id: str = typer.Option(None, "--location-id", help="Return the list of phone numbers for this location within th"),
1898
+ phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
1899
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1900
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1901
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1902
+ debug: bool = typer.Option(False, "--debug"),
1903
+ ):
1904
+ """Get Virtual Line Available Phone Numbers."""
1905
+ api = get_api(debug=debug)
1906
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/availableNumbers"
1907
+ params = {}
1908
+ if location_id is not None:
1909
+ params["locationId"] = location_id
1910
+ if phone_number is not None:
1911
+ params["phoneNumber"] = phone_number
1912
+ if limit > 0:
1913
+ params["max"] = limit
1914
+ if offset > 0:
1915
+ params["start"] = offset
1916
+ org_id = get_org_id()
1917
+ if org_id is not None:
1918
+ params["orgId"] = org_id
1919
+ result = None
1920
+ try:
1921
+ result = api.session.rest_get(url, params=params)
1922
+ except WebexError as e:
1923
+ handle_rest_error(e)
1924
+ result = result or []
1925
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1926
+ if output == "json":
1927
+ print_json(items)
1928
+ else:
1929
+ print_table(items, columns=[('Phone Number', 'phoneNumber'), ('State', 'state')], limit=limit)
1930
+
1931
+
1932
+
1933
+ @app.command("list-available-numbers-emergency-callback-number")
1934
+ def list_available_numbers_emergency_callback_number(
1935
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1936
+ phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
1937
+ owner_name: str = typer.Option(None, "--owner-name", help="Return the list of phone numbers that are owned by the given"),
1938
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1939
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1940
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1941
+ debug: bool = typer.Option(False, "--debug"),
1942
+ ):
1943
+ """Get Virtual Line ECBN Available Phone Numbers."""
1944
+ api = get_api(debug=debug)
1945
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/emergencyCallbackNumber/availableNumbers"
1946
+ params = {}
1947
+ if phone_number is not None:
1948
+ params["phoneNumber"] = phone_number
1949
+ if owner_name is not None:
1950
+ params["ownerName"] = owner_name
1951
+ if limit > 0:
1952
+ params["max"] = limit
1953
+ if offset > 0:
1954
+ params["start"] = offset
1955
+ org_id = get_org_id()
1956
+ if org_id is not None:
1957
+ params["orgId"] = org_id
1958
+ result = None
1959
+ try:
1960
+ result = api.session.rest_get(url, params=params)
1961
+ except WebexError as e:
1962
+ handle_rest_error(e)
1963
+ result = result or []
1964
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1965
+ if output == "json":
1966
+ print_json(items)
1967
+ else:
1968
+ print_table(items, columns=[('Phone Number', 'phoneNumber'), ('State', 'state')], limit=limit)
1969
+
1970
+
1971
+
1972
+ @app.command("list-available-numbers-call-intercept")
1973
+ def list_available_numbers_call_intercept(
1974
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
1975
+ phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
1976
+ owner_name: str = typer.Option(None, "--owner-name", help="Return the list of phone numbers that are owned by the given"),
1977
+ extension: str = typer.Option(None, "--extension", help="Returns the list of PSTN phone numbers with the given `exten"),
1978
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1979
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1980
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1981
+ debug: bool = typer.Option(False, "--debug"),
1982
+ ):
1983
+ """Get Virtual Line Call Intercept Available Phone Numbers."""
1984
+ api = get_api(debug=debug)
1985
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/callIntercept/availableNumbers"
1986
+ params = {}
1987
+ if phone_number is not None:
1988
+ params["phoneNumber"] = phone_number
1989
+ if owner_name is not None:
1990
+ params["ownerName"] = owner_name
1991
+ if extension is not None:
1992
+ params["extension"] = extension
1993
+ if limit > 0:
1994
+ params["max"] = limit
1995
+ if offset > 0:
1996
+ params["start"] = offset
1997
+ org_id = get_org_id()
1998
+ if org_id is not None:
1999
+ params["orgId"] = org_id
2000
+ result = None
2001
+ try:
2002
+ result = api.session.rest_get(url, params=params)
2003
+ except WebexError as e:
2004
+ handle_rest_error(e)
2005
+ result = result or []
2006
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
2007
+ if output == "json":
2008
+ print_json(items)
2009
+ else:
2010
+ print_table(items, columns=[('Phone Number', 'phoneNumber'), ('State', 'state')], limit=limit)
2011
+
2012
+
2013
+
2014
+ @app.command("show-do-not-disturb")
2015
+ def show_do_not_disturb(
2016
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
2017
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
2018
+ debug: bool = typer.Option(False, "--debug"),
2019
+ ):
2020
+ """Retrieve DoNotDisturb Settings for a Virtual Line."""
2021
+ api = get_api(debug=debug)
2022
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/doNotDisturb"
2023
+ params = {}
2024
+ org_id = get_org_id()
2025
+ if org_id is not None:
2026
+ params["orgId"] = org_id
2027
+ try:
2028
+ result = api.session.rest_get(url, params=params)
2029
+ except WebexError as e:
2030
+ handle_rest_error(e)
2031
+ if output == "json":
2032
+ print_json(result)
2033
+ else:
2034
+ if isinstance(result, dict):
2035
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
2036
+ elif isinstance(result, list):
2037
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
2038
+ else:
2039
+ print_json(result)
2040
+
2041
+
2042
+
2043
+ @app.command("update-do-not-disturb")
2044
+ def update_do_not_disturb(
2045
+ virtual_line_id: str = typer.Argument(help="virtualLineId"),
2046
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="`true` if the DoNotDisturb feature is enabled."),
2047
+ ring_splash_enabled: bool = typer.Option(None, "--ring-splash-enabled/--no-ring-splash-enabled", help="When `true`, enables ring reminder when you receive an incom"),
2048
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
2049
+ debug: bool = typer.Option(False, "--debug"),
2050
+ ):
2051
+ """Modify DoNotDisturb Settings for a Virtual Line."""
2052
+ api = get_api(debug=debug)
2053
+ url = f"https://webexapis.com/v1/telephony/config/virtualLines/{virtual_line_id}/doNotDisturb"
2054
+ params = {}
2055
+ org_id = get_org_id()
2056
+ if org_id is not None:
2057
+ params["orgId"] = org_id
2058
+ if json_body:
2059
+ body = json.loads(json_body)
2060
+ else:
2061
+ body = {}
2062
+ if enabled is not None:
2063
+ body["enabled"] = enabled
2064
+ if ring_splash_enabled is not None:
2065
+ body["ringSplashEnabled"] = ring_splash_enabled
2066
+ try:
2067
+ result = api.session.rest_put(url, json=body, params=params)
2068
+ except WebexError as e:
2069
+ handle_rest_error(e)
2070
+ typer.echo(f"Updated.")
2071
+
2072
+