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.
- wxcli/__init__.py +4 -0
- wxcli/_playbook/.claude/agents/migration-advisor.md +239 -0
- wxcli/_playbook/.claude/agents/wxc-calling-builder.md +946 -0
- wxcli/_playbook/.claude/rules/cleanup.md +29 -0
- wxcli/_playbook/.claude/rules/cucm-migration.md +58 -0
- wxcli/_playbook/.claude/rules/org-health.md +14 -0
- wxcli/_playbook/.claude/settings.json +8 -0
- wxcli/_playbook/.claude/skills/audit-compliance/SKILL.md +515 -0
- wxcli/_playbook/.claude/skills/call-control/SKILL.md +747 -0
- wxcli/_playbook/.claude/skills/configure-features/SKILL.md +623 -0
- wxcli/_playbook/.claude/skills/configure-routing/SKILL.md +627 -0
- wxcli/_playbook/.claude/skills/contact-center/SKILL.md +1181 -0
- wxcli/_playbook/.claude/skills/cucm-migrate/SKILL.md +820 -0
- wxcli/_playbook/.claude/skills/customer-assist/SKILL.md +443 -0
- wxcli/_playbook/.claude/skills/device-platform/SKILL.md +407 -0
- wxcli/_playbook/.claude/skills/manage-call-settings/SKILL.md +757 -0
- wxcli/_playbook/.claude/skills/manage-devices/SKILL.md +790 -0
- wxcli/_playbook/.claude/skills/manage-identity/SKILL.md +638 -0
- wxcli/_playbook/.claude/skills/manage-licensing/SKILL.md +528 -0
- wxcli/_playbook/.claude/skills/manage-meetings/SKILL.md +708 -0
- wxcli/_playbook/.claude/skills/messaging-bots/SKILL.md +365 -0
- wxcli/_playbook/.claude/skills/messaging-spaces/SKILL.md +520 -0
- wxcli/_playbook/.claude/skills/org-health/SKILL.md +159 -0
- wxcli/_playbook/.claude/skills/provision-calling/SKILL.md +421 -0
- wxcli/_playbook/.claude/skills/query-live/SKILL.md +183 -0
- wxcli/_playbook/.claude/skills/query-live/domains/call-flow-trace.md +223 -0
- wxcli/_playbook/.claude/skills/query-live/domains/call-history.md +44 -0
- wxcli/_playbook/.claude/skills/query-live/domains/features.md +169 -0
- wxcli/_playbook/.claude/skills/query-live/domains/numbers.md +93 -0
- wxcli/_playbook/.claude/skills/query-live/domains/people-and-settings.md +146 -0
- wxcli/_playbook/.claude/skills/query-live/domains/routing.md +124 -0
- wxcli/_playbook/.claude/skills/reporting/SKILL.md +501 -0
- wxcli/_playbook/.claude/skills/reporting/references/cdr-recipes.md +1628 -0
- wxcli/_playbook/.claude/skills/reporting-cc/SKILL.md +251 -0
- wxcli/_playbook/.claude/skills/reporting-meetings/SKILL.md +290 -0
- wxcli/_playbook/.claude/skills/teardown/SKILL.md +214 -0
- wxcli/_playbook/.claude/skills/video-mesh/SKILL.md +379 -0
- wxcli/_playbook/.claude/skills/wxc-calling-debug/SKILL.md +475 -0
- wxcli/_playbook/CLAUDE.md +373 -0
- wxcli/_playbook/docs/reference/CLAUDE.md +25 -0
- wxcli/_playbook/docs/reference/admin-apps-data.md +643 -0
- wxcli/_playbook/docs/reference/admin-audit-security.md +322 -0
- wxcli/_playbook/docs/reference/admin-hybrid.md +383 -0
- wxcli/_playbook/docs/reference/admin-identity-scim.md +1007 -0
- wxcli/_playbook/docs/reference/admin-licensing.md +414 -0
- wxcli/_playbook/docs/reference/admin-org-management.md +586 -0
- wxcli/_playbook/docs/reference/admin-partner.md +357 -0
- wxcli/_playbook/docs/reference/archive/wxc-sdk-patterns.md +1229 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-advanced.md +835 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-core.md +743 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-devices-workspaces.md +1305 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-features.md +849 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-locations.md +938 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-person.md +1255 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-routing.md +1262 -0
- wxcli/_playbook/docs/reference/authentication.md +965 -0
- wxcli/_playbook/docs/reference/call-control.md +1205 -0
- wxcli/_playbook/docs/reference/call-features-additional.md +2541 -0
- wxcli/_playbook/docs/reference/call-features-major.md +1320 -0
- wxcli/_playbook/docs/reference/call-routing.md +2304 -0
- wxcli/_playbook/docs/reference/contact-center-analytics.md +1049 -0
- wxcli/_playbook/docs/reference/contact-center-core.md +1614 -0
- wxcli/_playbook/docs/reference/contact-center-journey.md +457 -0
- wxcli/_playbook/docs/reference/contact-center-routing.md +1138 -0
- wxcli/_playbook/docs/reference/devices-core.md +1869 -0
- wxcli/_playbook/docs/reference/devices-dect.md +1150 -0
- wxcli/_playbook/docs/reference/devices-platform.md +467 -0
- wxcli/_playbook/docs/reference/devices-workspaces.md +1426 -0
- wxcli/_playbook/docs/reference/emergency-services.md +1122 -0
- wxcli/_playbook/docs/reference/location-calling-core.md +2037 -0
- wxcli/_playbook/docs/reference/location-calling-media.md +1334 -0
- wxcli/_playbook/docs/reference/location-recording-advanced.md +1482 -0
- wxcli/_playbook/docs/reference/meetings-content.md +473 -0
- wxcli/_playbook/docs/reference/meetings-core.md +903 -0
- wxcli/_playbook/docs/reference/meetings-infrastructure.md +899 -0
- wxcli/_playbook/docs/reference/meetings-settings.md +850 -0
- wxcli/_playbook/docs/reference/messaging-bots.md +1091 -0
- wxcli/_playbook/docs/reference/messaging-spaces.md +695 -0
- wxcli/_playbook/docs/reference/person-call-settings-behavior.md +1639 -0
- wxcli/_playbook/docs/reference/person-call-settings-handling.md +1394 -0
- wxcli/_playbook/docs/reference/person-call-settings-media.md +1898 -0
- wxcli/_playbook/docs/reference/person-call-settings-permissions.md +1329 -0
- wxcli/_playbook/docs/reference/provisioning.md +1366 -0
- wxcli/_playbook/docs/reference/reporting-analytics.md +1318 -0
- wxcli/_playbook/docs/reference/self-service-call-settings.md +1020 -0
- wxcli/_playbook/docs/reference/virtual-lines.md +1132 -0
- wxcli/_playbook/docs/reference/webhooks-events.md +1444 -0
- wxcli/_playbook/docs/reference/wxcadm-xsi-realtime.md +953 -0
- wxcli/_version.py +24 -0
- wxcli/auth.py +137 -0
- wxcli/commands/__init__.py +0 -0
- wxcli/commands/_registry.py +181 -0
- wxcli/commands/activation_email.py +103 -0
- wxcli/commands/admin_recordings.py +585 -0
- wxcli/commands/analytics.py +105 -0
- wxcli/commands/announcement_playlists.py +219 -0
- wxcli/commands/announcements.py +519 -0
- wxcli/commands/archive_users.py +36 -0
- wxcli/commands/attachment_actions.py +60 -0
- wxcli/commands/audit_events.py +83 -0
- wxcli/commands/authorizations.py +113 -0
- wxcli/commands/auto_attendant.py +642 -0
- wxcli/commands/broadworks_billing_reports.py +126 -0
- wxcli/commands/broadworks_enterprises.py +160 -0
- wxcli/commands/broadworks_subscribers.py +319 -0
- wxcli/commands/broadworks_workspaces.py +108 -0
- wxcli/commands/call_controls.py +1304 -0
- wxcli/commands/call_park.py +494 -0
- wxcli/commands/call_pickup.py +229 -0
- wxcli/commands/call_queue.py +1878 -0
- wxcli/commands/call_recording.py +707 -0
- wxcli/commands/call_routing.py +1610 -0
- wxcli/commands/call_settings_for_me_phase_5.py +212 -0
- wxcli/commands/caller_reputation.py +169 -0
- wxcli/commands/calling_service.py +305 -0
- wxcli/commands/cc_address_book.py +770 -0
- wxcli/commands/cc_agent_greetings.py +242 -0
- wxcli/commands/cc_agent_summaries.py +39 -0
- wxcli/commands/cc_agent_wellbeing.py +216 -0
- wxcli/commands/cc_agents.py +425 -0
- wxcli/commands/cc_ai_assistant.py +55 -0
- wxcli/commands/cc_ai_feature.py +294 -0
- wxcli/commands/cc_audio_files.py +149 -0
- wxcli/commands/cc_auto_csat.py +311 -0
- wxcli/commands/cc_aux_code.py +458 -0
- wxcli/commands/cc_business_hour.py +353 -0
- wxcli/commands/cc_call_monitoring.py +228 -0
- wxcli/commands/cc_callbacks.py +194 -0
- wxcli/commands/cc_campaign.py +212 -0
- wxcli/commands/cc_captures.py +39 -0
- wxcli/commands/cc_contact_list.py +169 -0
- wxcli/commands/cc_contact_number.py +295 -0
- wxcli/commands/cc_data_sources.py +235 -0
- wxcli/commands/cc_desktop_layout.py +380 -0
- wxcli/commands/cc_desktop_profile.py +322 -0
- wxcli/commands/cc_dial_number.py +511 -0
- wxcli/commands/cc_dial_plan.py +347 -0
- wxcli/commands/cc_dnc.py +101 -0
- wxcli/commands/cc_entry_point.py +545 -0
- wxcli/commands/cc_ewt.py +51 -0
- wxcli/commands/cc_flow.py +477 -0
- wxcli/commands/cc_global_vars.py +430 -0
- wxcli/commands/cc_holiday_list.py +335 -0
- wxcli/commands/cc_journey.py +1261 -0
- wxcli/commands/cc_legacy_flows.py +49 -0
- wxcli/commands/cc_multimedia_profile.py +436 -0
- wxcli/commands/cc_notification.py +51 -0
- wxcli/commands/cc_outdial_ani.py +604 -0
- wxcli/commands/cc_overrides.py +341 -0
- wxcli/commands/cc_queue.py +1083 -0
- wxcli/commands/cc_queue_stats.py +55 -0
- wxcli/commands/cc_realtime.py +51 -0
- wxcli/commands/cc_resource_collection.py +312 -0
- wxcli/commands/cc_search.py +46 -0
- wxcli/commands/cc_site.py +400 -0
- wxcli/commands/cc_skill.py +350 -0
- wxcli/commands/cc_skill_profile.py +289 -0
- wxcli/commands/cc_subscriptions.py +426 -0
- wxcli/commands/cc_summaries.py +130 -0
- wxcli/commands/cc_tasks.py +844 -0
- wxcli/commands/cc_team.py +325 -0
- wxcli/commands/cc_user_profiles.py +349 -0
- wxcli/commands/cc_users.py +701 -0
- wxcli/commands/cc_work_types.py +363 -0
- wxcli/commands/cdr.py +89 -0
- wxcli/commands/classifications.py +38 -0
- wxcli/commands/cleanup.py +1427 -0
- wxcli/commands/client_settings.py +72 -0
- wxcli/commands/conference.py +328 -0
- wxcli/commands/configure.py +72 -0
- wxcli/commands/converged_recordings.py +347 -0
- wxcli/commands/converged_recordings_export.py +310 -0
- wxcli/commands/cq_playlists.py +42 -0
- wxcli/commands/cucm.py +2794 -0
- wxcli/commands/cucm_config.py +129 -0
- wxcli/commands/customer_assist.py +373 -0
- wxcli/commands/data_sources.py +227 -0
- wxcli/commands/dect_devices.py +787 -0
- wxcli/commands/device_configurations.py +77 -0
- wxcli/commands/device_dynamic_settings.py +320 -0
- wxcli/commands/device_settings.py +1489 -0
- wxcli/commands/devices.py +280 -0
- wxcli/commands/domains.py +142 -0
- wxcli/commands/ecm.py +178 -0
- wxcli/commands/emergency_services.py +827 -0
- wxcli/commands/events.py +83 -0
- wxcli/commands/external_voicemail.py +52 -0
- wxcli/commands/groups.py +206 -0
- wxcli/commands/guest_management.py +76 -0
- wxcli/commands/hds.py +340 -0
- wxcli/commands/hot_desk.py +67 -0
- wxcli/commands/hot_desking_members.py +118 -0
- wxcli/commands/hot_desking_portal.py +124 -0
- wxcli/commands/hunt_group.py +577 -0
- wxcli/commands/hybrid_clusters.py +71 -0
- wxcli/commands/hybrid_connectors.py +67 -0
- wxcli/commands/identity_org.py +159 -0
- wxcli/commands/init_playbook.py +155 -0
- wxcli/commands/licenses.py +106 -0
- wxcli/commands/live_monitoring.py +40 -0
- wxcli/commands/location_call_handling.py +577 -0
- wxcli/commands/location_schedules.py +341 -0
- wxcli/commands/location_settings.py +1297 -0
- wxcli/commands/location_voicemail.py +494 -0
- wxcli/commands/locations.py +336 -0
- wxcli/commands/meeting_captions.py +110 -0
- wxcli/commands/meeting_chats.py +63 -0
- wxcli/commands/meeting_invitees.py +234 -0
- wxcli/commands/meeting_messages.py +28 -0
- wxcli/commands/meeting_participants.py +278 -0
- wxcli/commands/meeting_polls.py +111 -0
- wxcli/commands/meeting_preferences.py +515 -0
- wxcli/commands/meeting_qa.py +78 -0
- wxcli/commands/meeting_qualities.py +43 -0
- wxcli/commands/meeting_reports.py +101 -0
- wxcli/commands/meeting_session_types.py +105 -0
- wxcli/commands/meeting_site.py +57 -0
- wxcli/commands/meeting_slido.py +44 -0
- wxcli/commands/meeting_summaries.py +92 -0
- wxcli/commands/meeting_tracking_codes.py +233 -0
- wxcli/commands/meeting_transcripts.py +230 -0
- wxcli/commands/meetings.py +1997 -0
- wxcli/commands/memberships.py +164 -0
- wxcli/commands/messages.py +213 -0
- wxcli/commands/mode_management.py +244 -0
- wxcli/commands/my_call_settings.py +3577 -0
- wxcli/commands/numbers.py +405 -0
- wxcli/commands/operating_modes.py +410 -0
- wxcli/commands/org_contacts.py +284 -0
- wxcli/commands/org_health_cli.py +55 -0
- wxcli/commands/org_settings.py +71 -0
- wxcli/commands/organizations.py +83 -0
- wxcli/commands/paging_group.py +257 -0
- wxcli/commands/partner_admins.py +141 -0
- wxcli/commands/partner_reports.py +188 -0
- wxcli/commands/partner_tags.py +216 -0
- wxcli/commands/people.py +279 -0
- wxcli/commands/person_call_settings.py +71 -0
- wxcli/commands/pstn.py +280 -0
- wxcli/commands/recording_report.py +154 -0
- wxcli/commands/report_templates.py +38 -0
- wxcli/commands/reports.py +144 -0
- wxcli/commands/resource_group_memberships.py +160 -0
- wxcli/commands/resource_groups.py +67 -0
- wxcli/commands/roles.py +63 -0
- wxcli/commands/room_tabs.py +160 -0
- wxcli/commands/rooms.py +225 -0
- wxcli/commands/scim_bulk.py +46 -0
- wxcli/commands/scim_groups.py +246 -0
- wxcli/commands/scim_schemas.py +82 -0
- wxcli/commands/scim_users.py +283 -0
- wxcli/commands/security_audit.py +54 -0
- wxcli/commands/service_apps.py +51 -0
- wxcli/commands/single_number_reach.py +209 -0
- wxcli/commands/team_memberships.py +155 -0
- wxcli/commands/teams.py +153 -0
- wxcli/commands/ucm_profile.py +42 -0
- wxcli/commands/update.py +219 -0
- wxcli/commands/user_settings.py +5116 -0
- wxcli/commands/video_mesh.py +1090 -0
- wxcli/commands/virtual_extensions.py +502 -0
- wxcli/commands/virtual_line_settings.py +2072 -0
- wxcli/commands/webhooks.py +176 -0
- wxcli/commands/wholesale_billing_reports.py +147 -0
- wxcli/commands/wholesale_provisioning.py +523 -0
- wxcli/commands/workspace_locations.py +339 -0
- wxcli/commands/workspace_metrics.py +103 -0
- wxcli/commands/workspace_personalization.py +70 -0
- wxcli/commands/workspace_settings.py +3347 -0
- wxcli/commands/workspaces.py +279 -0
- wxcli/commands/xapi.py +114 -0
- wxcli/config.py +139 -0
- wxcli/errors.py +77 -0
- wxcli/main.py +210 -0
- wxcli/migration/CLAUDE.md +106 -0
- wxcli/migration/__init__.py +1 -0
- wxcli/migration/advisory/CLAUDE.md +196 -0
- wxcli/migration/advisory/__init__.py +46 -0
- wxcli/migration/advisory/advisor.py +107 -0
- wxcli/migration/advisory/advisory_patterns.py +2552 -0
- wxcli/migration/advisory/recommendation_rules.py +835 -0
- wxcli/migration/cucm/CLAUDE.md +74 -0
- wxcli/migration/cucm/__init__.py +5 -0
- wxcli/migration/cucm/connection.py +265 -0
- wxcli/migration/cucm/discovery.py +225 -0
- wxcli/migration/cucm/extractors/__init__.py +1 -0
- wxcli/migration/cucm/extractors/announcements.py +92 -0
- wxcli/migration/cucm/extractors/base.py +90 -0
- wxcli/migration/cucm/extractors/device_profiles.py +98 -0
- wxcli/migration/cucm/extractors/devices.py +193 -0
- wxcli/migration/cucm/extractors/e911.py +103 -0
- wxcli/migration/cucm/extractors/features.py +309 -0
- wxcli/migration/cucm/extractors/helpers.py +73 -0
- wxcli/migration/cucm/extractors/informational.py +295 -0
- wxcli/migration/cucm/extractors/locations.py +221 -0
- wxcli/migration/cucm/extractors/moh.py +100 -0
- wxcli/migration/cucm/extractors/remote_destinations.py +96 -0
- wxcli/migration/cucm/extractors/routing.py +493 -0
- wxcli/migration/cucm/extractors/shared_lines.py +146 -0
- wxcli/migration/cucm/extractors/templates.py +229 -0
- wxcli/migration/cucm/extractors/tier4.py +173 -0
- wxcli/migration/cucm/extractors/users.py +240 -0
- wxcli/migration/cucm/extractors/voicemail.py +243 -0
- wxcli/migration/cucm/extractors/workspaces.py +88 -0
- wxcli/migration/cucm/unity_connection.py +304 -0
- wxcli/migration/execute/CLAUDE.md +372 -0
- wxcli/migration/execute/__init__.py +303 -0
- wxcli/migration/execute/batch.py +277 -0
- wxcli/migration/execute/dependency.py +483 -0
- wxcli/migration/execute/engine.py +964 -0
- wxcli/migration/execute/handlers.py +2232 -0
- wxcli/migration/execute/planner.py +1939 -0
- wxcli/migration/execute/runtime.py +571 -0
- wxcli/migration/export/CLAUDE.md +49 -0
- wxcli/migration/export/__init__.py +8 -0
- wxcli/migration/export/csv_export.py +126 -0
- wxcli/migration/export/deployment_plan.py +479 -0
- wxcli/migration/export/json_export.py +60 -0
- wxcli/migration/models.py +941 -0
- wxcli/migration/phone_models.py +50 -0
- wxcli/migration/preflight/CLAUDE.md +52 -0
- wxcli/migration/preflight/__init__.py +89 -0
- wxcli/migration/preflight/checks.py +911 -0
- wxcli/migration/preflight/runner.py +293 -0
- wxcli/migration/rate_limiter.py +144 -0
- wxcli/migration/report/CLAUDE.md +245 -0
- wxcli/migration/report/__init__.py +7 -0
- wxcli/migration/report/appendix.py +2437 -0
- wxcli/migration/report/assembler.py +183 -0
- wxcli/migration/report/charts.py +263 -0
- wxcli/migration/report/executive.py +776 -0
- wxcli/migration/report/explainer.py +704 -0
- wxcli/migration/report/helpers.py +74 -0
- wxcli/migration/report/ingest.py +152 -0
- wxcli/migration/report/notice_templates.py +192 -0
- wxcli/migration/report/score.py +375 -0
- wxcli/migration/report/styles.py +1052 -0
- wxcli/migration/report/user_diff.py +1052 -0
- wxcli/migration/report/user_notice.py +425 -0
- wxcli/migration/state.py +139 -0
- wxcli/migration/store.py +860 -0
- wxcli/migration/transform/CLAUDE.md +107 -0
- wxcli/migration/transform/__init__.py +12 -0
- wxcli/migration/transform/analysis_pipeline.py +453 -0
- wxcli/migration/transform/analyzers/__init__.py +142 -0
- wxcli/migration/transform/analyzers/css_permission.py +189 -0
- wxcli/migration/transform/analyzers/css_routing.py +335 -0
- wxcli/migration/transform/analyzers/device_compatibility.py +122 -0
- wxcli/migration/transform/analyzers/dn_ambiguity.py +131 -0
- wxcli/migration/transform/analyzers/duplicate_user.py +221 -0
- wxcli/migration/transform/analyzers/extension_conflict.py +173 -0
- wxcli/migration/transform/analyzers/feature_approximation.py +227 -0
- wxcli/migration/transform/analyzers/layout_overflow.py +174 -0
- wxcli/migration/transform/analyzers/location_ambiguity.py +184 -0
- wxcli/migration/transform/analyzers/missing_data.py +300 -0
- wxcli/migration/transform/analyzers/selective_call_handling.py +394 -0
- wxcli/migration/transform/analyzers/shared_line.py +170 -0
- wxcli/migration/transform/analyzers/voicemail_compatibility.py +267 -0
- wxcli/migration/transform/analyzers/workspace_license.py +212 -0
- wxcli/migration/transform/cross_reference.py +1136 -0
- wxcli/migration/transform/cucm_pattern.py +246 -0
- wxcli/migration/transform/decisions.py +301 -0
- wxcli/migration/transform/e164.py +145 -0
- wxcli/migration/transform/engine.py +256 -0
- wxcli/migration/transform/mappers/CLAUDE.md +123 -0
- wxcli/migration/transform/mappers/__init__.py +76 -0
- wxcli/migration/transform/mappers/announcement_mapper.py +122 -0
- wxcli/migration/transform/mappers/base.py +190 -0
- wxcli/migration/transform/mappers/button_template_mapper.py +186 -0
- wxcli/migration/transform/mappers/call_forwarding_mapper.py +228 -0
- wxcli/migration/transform/mappers/call_settings_mapper.py +144 -0
- wxcli/migration/transform/mappers/css_mapper.py +868 -0
- wxcli/migration/transform/mappers/dect_mapper.py +367 -0
- wxcli/migration/transform/mappers/device_layout_mapper.py +289 -0
- wxcli/migration/transform/mappers/device_mapper.py +227 -0
- wxcli/migration/transform/mappers/device_profile_mapper.py +190 -0
- wxcli/migration/transform/mappers/device_settings_mapper.py +464 -0
- wxcli/migration/transform/mappers/e911_mapper.py +129 -0
- wxcli/migration/transform/mappers/ecbn_mapper.py +263 -0
- wxcli/migration/transform/mappers/executive_assistant_mapper.py +156 -0
- wxcli/migration/transform/mappers/feature_mapper.py +1179 -0
- wxcli/migration/transform/mappers/line_mapper.py +310 -0
- wxcli/migration/transform/mappers/location_mapper.py +235 -0
- wxcli/migration/transform/mappers/moh_mapper.py +114 -0
- wxcli/migration/transform/mappers/monitoring_mapper.py +221 -0
- wxcli/migration/transform/mappers/receptionist_mapper.py +245 -0
- wxcli/migration/transform/mappers/routing_mapper.py +821 -0
- wxcli/migration/transform/mappers/snr_mapper.py +201 -0
- wxcli/migration/transform/mappers/softkey_mapper.py +205 -0
- wxcli/migration/transform/mappers/user_mapper.py +264 -0
- wxcli/migration/transform/mappers/voicemail_group_mapper.py +286 -0
- wxcli/migration/transform/mappers/voicemail_mapper.py +514 -0
- wxcli/migration/transform/mappers/workspace_mapper.py +464 -0
- wxcli/migration/transform/normalizers.py +2008 -0
- wxcli/migration/transform/pattern_converter.py +106 -0
- wxcli/migration/transform/pipeline.py +271 -0
- wxcli/migration/transform/rules.py +255 -0
- wxcli/org_health/CLAUDE.md +63 -0
- wxcli/org_health/__init__.py +7 -0
- wxcli/org_health/analyze.py +93 -0
- wxcli/org_health/checks.py +458 -0
- wxcli/org_health/collector.py +62 -0
- wxcli/org_health/models.py +78 -0
- wxcli/org_health/report.py +300 -0
- wxcli/output.py +101 -0
- wxcli-1.2.0.dist-info/METADATA +505 -0
- wxcli-1.2.0.dist-info/RECORD +413 -0
- wxcli-1.2.0.dist-info/WHEEL +5 -0
- wxcli-1.2.0.dist-info/entry_points.txt +2 -0
- wxcli-1.2.0.dist-info/licenses/LICENSE +191 -0
- wxcli-1.2.0.dist-info/scm_file_list.json +751 -0
- wxcli-1.2.0.dist-info/scm_version.json +8 -0
- wxcli-1.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,642 @@
|
|
|
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 auto-attendant.")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@app.command("list")
|
|
13
|
+
def cmd_list(
|
|
14
|
+
location_id: str = typer.Option(None, "--location-id", help="Return the list of auto attendants for this location."),
|
|
15
|
+
name: str = typer.Option(None, "--name", help="Only return auto attendants with the matching name."),
|
|
16
|
+
phone_number: str = typer.Option(None, "--phone-number", help="Only return auto attendants with the matching phone number."),
|
|
17
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
18
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
19
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
20
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
21
|
+
):
|
|
22
|
+
"""Read the List of Auto Attendants."""
|
|
23
|
+
api = get_api(debug=debug)
|
|
24
|
+
url = f"https://webexapis.com/v1/telephony/config/autoAttendants"
|
|
25
|
+
params = {}
|
|
26
|
+
if location_id is not None:
|
|
27
|
+
params["locationId"] = location_id
|
|
28
|
+
if name is not None:
|
|
29
|
+
params["name"] = name
|
|
30
|
+
if phone_number is not None:
|
|
31
|
+
params["phoneNumber"] = phone_number
|
|
32
|
+
if limit > 0:
|
|
33
|
+
params["max"] = limit
|
|
34
|
+
if offset > 0:
|
|
35
|
+
params["start"] = offset
|
|
36
|
+
org_id = get_org_id()
|
|
37
|
+
if org_id is not None:
|
|
38
|
+
params["orgId"] = org_id
|
|
39
|
+
try:
|
|
40
|
+
if limit > 0:
|
|
41
|
+
result = api.session.rest_get(url, params=params)
|
|
42
|
+
result = result or {}
|
|
43
|
+
items = result.get("autoAttendants", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
44
|
+
else:
|
|
45
|
+
items = list(api.session.follow_pagination(url=url, params=params, item_key="autoAttendants"))
|
|
46
|
+
except WebexError as e:
|
|
47
|
+
handle_rest_error(e)
|
|
48
|
+
if output == "json":
|
|
49
|
+
print_json(items)
|
|
50
|
+
else:
|
|
51
|
+
print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Extension', 'extension'), ('Location', 'locationName'), ('Enabled', 'enabled')], limit=limit)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@app.command("show")
|
|
56
|
+
def show(
|
|
57
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
58
|
+
auto_attendant_id: str = typer.Argument(help="autoAttendantId"),
|
|
59
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
60
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
61
|
+
):
|
|
62
|
+
"""Get Details for an Auto Attendant."""
|
|
63
|
+
api = get_api(debug=debug)
|
|
64
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/autoAttendants/{auto_attendant_id}"
|
|
65
|
+
params = {}
|
|
66
|
+
org_id = get_org_id()
|
|
67
|
+
if org_id is not None:
|
|
68
|
+
params["orgId"] = org_id
|
|
69
|
+
try:
|
|
70
|
+
result = api.session.rest_get(url, params=params)
|
|
71
|
+
except WebexError as e:
|
|
72
|
+
handle_rest_error(e)
|
|
73
|
+
if output == "json":
|
|
74
|
+
print_json(result)
|
|
75
|
+
else:
|
|
76
|
+
if isinstance(result, dict):
|
|
77
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
78
|
+
elif isinstance(result, list):
|
|
79
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
80
|
+
else:
|
|
81
|
+
print_json(result)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@app.command("update")
|
|
86
|
+
def update(
|
|
87
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
88
|
+
auto_attendant_id: str = typer.Argument(help="autoAttendantId"),
|
|
89
|
+
name: str = typer.Option(None, "--name", help="Unique name for the auto attendant."),
|
|
90
|
+
phone_number: str = typer.Option(None, "--phone-number", help="Auto attendant phone number. Either `phoneNumber` or `exten"),
|
|
91
|
+
extension: str = typer.Option(None, "--extension", help="Auto attendant extension. Either `phoneNumber` or `extensio"),
|
|
92
|
+
first_name: str = typer.Option(None, "--first-name", help="First name defined for an auto attendant. This field has bee"),
|
|
93
|
+
last_name: str = typer.Option(None, "--last-name", help="Last name defined for an auto attendant. This field has been"),
|
|
94
|
+
language_code: str = typer.Option(None, "--language-code", help="Announcement language code for the auto attendant."),
|
|
95
|
+
business_schedule: str = typer.Option(None, "--business-schedule", help="Business hours defined for the auto attendant."),
|
|
96
|
+
holiday_schedule: str = typer.Option(None, "--holiday-schedule", help="Holiday defined for the auto attendant."),
|
|
97
|
+
extension_dialing: str = typer.Option(None, "--extension-dialing", help="Choices: ENTERPRISE, GROUP"),
|
|
98
|
+
name_dialing: str = typer.Option(None, "--name-dialing", help="Choices: ENTERPRISE, GROUP"),
|
|
99
|
+
time_zone: str = typer.Option(None, "--time-zone", help="Time zone defined for the auto attendant."),
|
|
100
|
+
dial_by_name: str = typer.Option(None, "--dial-by-name", help="Sets or clears the name to be used for dial by name function"),
|
|
101
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
102
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
103
|
+
):
|
|
104
|
+
"""Update an Auto Attendant\n\nExample --json-body:\n '{"name":"...","phoneNumber":"...","extension":"...","firstName":"...","lastName":"...","alternateNumbers":[{"phoneNumber":"...","ringPattern":"...","tollFreeNumber":"..."}],"languageCode":"...","businessSchedule":"..."}'."""
|
|
105
|
+
api = get_api(debug=debug)
|
|
106
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/autoAttendants/{auto_attendant_id}"
|
|
107
|
+
params = {}
|
|
108
|
+
org_id = get_org_id()
|
|
109
|
+
if org_id is not None:
|
|
110
|
+
params["orgId"] = org_id
|
|
111
|
+
if json_body:
|
|
112
|
+
body = json.loads(json_body)
|
|
113
|
+
else:
|
|
114
|
+
body = {}
|
|
115
|
+
if name is not None:
|
|
116
|
+
body["name"] = name
|
|
117
|
+
if phone_number is not None:
|
|
118
|
+
body["phoneNumber"] = phone_number
|
|
119
|
+
if extension is not None:
|
|
120
|
+
body["extension"] = extension
|
|
121
|
+
if first_name is not None:
|
|
122
|
+
body["firstName"] = first_name
|
|
123
|
+
if last_name is not None:
|
|
124
|
+
body["lastName"] = last_name
|
|
125
|
+
if language_code is not None:
|
|
126
|
+
body["languageCode"] = language_code
|
|
127
|
+
if business_schedule is not None:
|
|
128
|
+
body["businessSchedule"] = business_schedule
|
|
129
|
+
if holiday_schedule is not None:
|
|
130
|
+
body["holidaySchedule"] = holiday_schedule
|
|
131
|
+
if extension_dialing is not None:
|
|
132
|
+
body["extensionDialing"] = extension_dialing
|
|
133
|
+
if name_dialing is not None:
|
|
134
|
+
body["nameDialing"] = name_dialing
|
|
135
|
+
if time_zone is not None:
|
|
136
|
+
body["timeZone"] = time_zone
|
|
137
|
+
if dial_by_name is not None:
|
|
138
|
+
body["dialByName"] = dial_by_name
|
|
139
|
+
try:
|
|
140
|
+
result = api.session.rest_put(url, json=body, params=params)
|
|
141
|
+
except WebexError as e:
|
|
142
|
+
handle_rest_error(e)
|
|
143
|
+
typer.echo(f"Updated.")
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@app.command("delete")
|
|
148
|
+
def delete(
|
|
149
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
150
|
+
auto_attendant_id: str = typer.Argument(help="autoAttendantId"),
|
|
151
|
+
force: bool = typer.Option(False, "--force", help="Skip confirmation"),
|
|
152
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
153
|
+
):
|
|
154
|
+
"""Delete an Auto Attendant."""
|
|
155
|
+
if not force:
|
|
156
|
+
typer.confirm(f"Delete {auto_attendant_id}?", abort=True)
|
|
157
|
+
api = get_api(debug=debug)
|
|
158
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/autoAttendants/{auto_attendant_id}"
|
|
159
|
+
params = {}
|
|
160
|
+
org_id = get_org_id()
|
|
161
|
+
if org_id is not None:
|
|
162
|
+
params["orgId"] = org_id
|
|
163
|
+
try:
|
|
164
|
+
api.session.rest_delete(url, params=params)
|
|
165
|
+
except WebexError as e:
|
|
166
|
+
handle_rest_error(e)
|
|
167
|
+
typer.echo(f"Deleted: {auto_attendant_id}")
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@app.command("create")
|
|
172
|
+
def create(
|
|
173
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
174
|
+
name: str = typer.Option(None, "--name", help="(required) Unique name for the auto attendant."),
|
|
175
|
+
phone_number: str = typer.Option(None, "--phone-number", help="Auto attendant phone number. Either `phoneNumber` or `exten"),
|
|
176
|
+
extension: str = typer.Option(None, "--extension", help="Auto attendant extension. Either `phoneNumber` or `extensio"),
|
|
177
|
+
first_name: str = typer.Option(None, "--first-name", help="First name defined for an auto attendant. This field has bee"),
|
|
178
|
+
last_name: str = typer.Option(None, "--last-name", help="Last name defined for an auto attendant. This field has been"),
|
|
179
|
+
language_code: str = typer.Option(None, "--language-code", help="Announcement language code for the auto attendant."),
|
|
180
|
+
business_schedule: str = typer.Option(None, "--business-schedule", help="(required) Business hours defined for the auto attendant."),
|
|
181
|
+
holiday_schedule: str = typer.Option(None, "--holiday-schedule", help="Holiday defined for the auto attendant."),
|
|
182
|
+
extension_dialing: str = typer.Option(None, "--extension-dialing", help="Choices: ENTERPRISE, GROUP"),
|
|
183
|
+
name_dialing: str = typer.Option(None, "--name-dialing", help="Choices: ENTERPRISE, GROUP"),
|
|
184
|
+
time_zone: str = typer.Option(None, "--time-zone", help="Time zone defined for the auto attendant."),
|
|
185
|
+
dial_by_name: str = typer.Option(None, "--dial-by-name", help="The name to be used for dial by name functions. Characters o"),
|
|
186
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
187
|
+
output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
|
|
188
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
189
|
+
):
|
|
190
|
+
"""Create an Auto Attendant\n\nExample --json-body:\n '{"name":"...","businessSchedule":"...","businessHoursMenu":{"greeting":"DEFAULT","extensionEnabled":true,"keyConfigurations":{"key":"...","action":"...","description":"...","value":"...","audioAnnouncementFile":"..."},"audioAnnouncementFile":{"id":"...","fileName":"...","mediaFileType":"...","level":"..."},"callTreatment":{"retryAttemptForNoInput":"...","noInputTimer":"...","actionToBePerformed":"..."}},"afterHoursMenu":{"greeting":"DEFAULT","extensionEnabled":true,"keyConfigurations":{"key":"...","action":"...","description":"...","value":"...","audioAnnouncementFile":"..."},"audioAnnouncementFile":{"id":"...","fileName":"...","mediaFileType":"...","level":"..."},"callTreatment":{"retryAttemptForNoInput":"...","noInputTimer":"...","actionToBePerformed":"..."}},"phoneNumber":"...","extension":"...","firstName":"...","lastName":"..."}'."""
|
|
191
|
+
api = get_api(debug=debug)
|
|
192
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/autoAttendants"
|
|
193
|
+
params = {}
|
|
194
|
+
org_id = get_org_id()
|
|
195
|
+
if org_id is not None:
|
|
196
|
+
params["orgId"] = org_id
|
|
197
|
+
if json_body:
|
|
198
|
+
body = json.loads(json_body)
|
|
199
|
+
else:
|
|
200
|
+
body = {}
|
|
201
|
+
if name is not None:
|
|
202
|
+
body["name"] = name
|
|
203
|
+
if phone_number is not None:
|
|
204
|
+
body["phoneNumber"] = phone_number
|
|
205
|
+
if extension is not None:
|
|
206
|
+
body["extension"] = extension
|
|
207
|
+
if first_name is not None:
|
|
208
|
+
body["firstName"] = first_name
|
|
209
|
+
if last_name is not None:
|
|
210
|
+
body["lastName"] = last_name
|
|
211
|
+
if language_code is not None:
|
|
212
|
+
body["languageCode"] = language_code
|
|
213
|
+
if business_schedule is not None:
|
|
214
|
+
body["businessSchedule"] = business_schedule
|
|
215
|
+
if holiday_schedule is not None:
|
|
216
|
+
body["holidaySchedule"] = holiday_schedule
|
|
217
|
+
if extension_dialing is not None:
|
|
218
|
+
body["extensionDialing"] = extension_dialing
|
|
219
|
+
if name_dialing is not None:
|
|
220
|
+
body["nameDialing"] = name_dialing
|
|
221
|
+
if time_zone is not None:
|
|
222
|
+
body["timeZone"] = time_zone
|
|
223
|
+
if dial_by_name is not None:
|
|
224
|
+
body["dialByName"] = dial_by_name
|
|
225
|
+
_missing = [f for f in ['name', 'businessSchedule'] if f not in body or body[f] is None]
|
|
226
|
+
if _missing:
|
|
227
|
+
typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
|
|
228
|
+
raise typer.Exit(1)
|
|
229
|
+
try:
|
|
230
|
+
result = api.session.rest_post(url, json=body, params=params)
|
|
231
|
+
except WebexError as e:
|
|
232
|
+
handle_rest_error(e)
|
|
233
|
+
if output == "json":
|
|
234
|
+
print_json(result)
|
|
235
|
+
elif isinstance(result, dict) and "id" in result:
|
|
236
|
+
typer.echo(f"Created: {result['id']}")
|
|
237
|
+
elif not result or result == {}:
|
|
238
|
+
typer.echo("Created.")
|
|
239
|
+
else:
|
|
240
|
+
print_json(result)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
@app.command("show-call-forwarding")
|
|
245
|
+
def show_call_forwarding(
|
|
246
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
247
|
+
auto_attendant_id: str = typer.Argument(help="autoAttendantId"),
|
|
248
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
249
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
250
|
+
):
|
|
251
|
+
"""Get Call Forwarding Settings for an Auto Attendant."""
|
|
252
|
+
api = get_api(debug=debug)
|
|
253
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/autoAttendants/{auto_attendant_id}/callForwarding"
|
|
254
|
+
params = {}
|
|
255
|
+
org_id = get_org_id()
|
|
256
|
+
if org_id is not None:
|
|
257
|
+
params["orgId"] = org_id
|
|
258
|
+
try:
|
|
259
|
+
result = api.session.rest_get(url, params=params)
|
|
260
|
+
except WebexError as e:
|
|
261
|
+
handle_rest_error(e)
|
|
262
|
+
if output == "json":
|
|
263
|
+
print_json(result)
|
|
264
|
+
else:
|
|
265
|
+
if isinstance(result, dict):
|
|
266
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
267
|
+
elif isinstance(result, list):
|
|
268
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
269
|
+
else:
|
|
270
|
+
print_json(result)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
@app.command("update-call-forwarding")
|
|
275
|
+
def update_call_forwarding(
|
|
276
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
277
|
+
auto_attendant_id: str = typer.Argument(help="autoAttendantId"),
|
|
278
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
279
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
280
|
+
):
|
|
281
|
+
"""Update Call Forwarding Settings for an Auto Attendant\n\nExample --json-body:\n '{"callForwarding":{"always":{"enabled":"...","destination":"...","ringReminderEnabled":"...","sendToVoicemailEnabled":"..."},"selective":{"enabled":"...","destination":"...","ringReminderEnabled":"...","sendToVoicemailEnabled":"..."},"rules":["..."],"operatingModes":{"enabled":"...","modes":"..."}}}'."""
|
|
282
|
+
api = get_api(debug=debug)
|
|
283
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/autoAttendants/{auto_attendant_id}/callForwarding"
|
|
284
|
+
params = {}
|
|
285
|
+
org_id = get_org_id()
|
|
286
|
+
if org_id is not None:
|
|
287
|
+
params["orgId"] = org_id
|
|
288
|
+
if json_body:
|
|
289
|
+
body = json.loads(json_body)
|
|
290
|
+
else:
|
|
291
|
+
body = {}
|
|
292
|
+
try:
|
|
293
|
+
result = api.session.rest_put(url, json=body, params=params)
|
|
294
|
+
except WebexError as e:
|
|
295
|
+
handle_rest_error(e)
|
|
296
|
+
typer.echo(f"Updated.")
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
@app.command("create-selective-rules")
|
|
301
|
+
def create_selective_rules(
|
|
302
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
303
|
+
auto_attendant_id: str = typer.Argument(help="autoAttendantId"),
|
|
304
|
+
name: str = typer.Option(None, "--name", help="(required) Unique name for the selective rule in the auto attendant."),
|
|
305
|
+
enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="Reflects if rule is enabled."),
|
|
306
|
+
business_schedule: str = typer.Option(None, "--business-schedule", help="Name of the location's business schedule which determines wh"),
|
|
307
|
+
holiday_schedule: str = typer.Option(None, "--holiday-schedule", help="Name of the location's holiday schedule which determines whe"),
|
|
308
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
309
|
+
output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
|
|
310
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
311
|
+
):
|
|
312
|
+
"""Create a Selective Call Forwarding Rule for an Auto Attendant\n\nExample --json-body:\n '{"name":"...","forwardTo":{"selection":"FORWARD_TO_DEFAULT_NUMBER","phoneNumber":"..."},"callsFrom":{"selection":"ANY","customNumbers":{"privateNumberEnabled":"...","unavailableNumberEnabled":"...","numbers":"..."}},"enabled":true,"businessSchedule":"...","holidaySchedule":"...","callsTo":{"numbers":["..."]}}'."""
|
|
313
|
+
api = get_api(debug=debug)
|
|
314
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/autoAttendants/{auto_attendant_id}/callForwarding/selectiveRules"
|
|
315
|
+
params = {}
|
|
316
|
+
org_id = get_org_id()
|
|
317
|
+
if org_id is not None:
|
|
318
|
+
params["orgId"] = org_id
|
|
319
|
+
if json_body:
|
|
320
|
+
body = json.loads(json_body)
|
|
321
|
+
else:
|
|
322
|
+
body = {}
|
|
323
|
+
if name is not None:
|
|
324
|
+
body["name"] = name
|
|
325
|
+
if enabled is not None:
|
|
326
|
+
body["enabled"] = enabled
|
|
327
|
+
if business_schedule is not None:
|
|
328
|
+
body["businessSchedule"] = business_schedule
|
|
329
|
+
if holiday_schedule is not None:
|
|
330
|
+
body["holidaySchedule"] = holiday_schedule
|
|
331
|
+
_missing = [f for f in ['name'] if f not in body or body[f] is None]
|
|
332
|
+
if _missing:
|
|
333
|
+
typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
|
|
334
|
+
raise typer.Exit(1)
|
|
335
|
+
try:
|
|
336
|
+
result = api.session.rest_post(url, json=body, params=params)
|
|
337
|
+
except WebexError as e:
|
|
338
|
+
handle_rest_error(e)
|
|
339
|
+
if output == "json":
|
|
340
|
+
print_json(result)
|
|
341
|
+
elif isinstance(result, dict) and "id" in result:
|
|
342
|
+
typer.echo(f"Created: {result['id']}")
|
|
343
|
+
elif not result or result == {}:
|
|
344
|
+
typer.echo("Created.")
|
|
345
|
+
else:
|
|
346
|
+
print_json(result)
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
@app.command("show-selective-rules")
|
|
351
|
+
def show_selective_rules(
|
|
352
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
353
|
+
auto_attendant_id: str = typer.Argument(help="autoAttendantId"),
|
|
354
|
+
rule_id: str = typer.Argument(help="ruleId"),
|
|
355
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
356
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
357
|
+
):
|
|
358
|
+
"""Get Selective Call Forwarding Rule for an Auto Attendant."""
|
|
359
|
+
api = get_api(debug=debug)
|
|
360
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/autoAttendants/{auto_attendant_id}/callForwarding/selectiveRules/{rule_id}"
|
|
361
|
+
params = {}
|
|
362
|
+
org_id = get_org_id()
|
|
363
|
+
if org_id is not None:
|
|
364
|
+
params["orgId"] = org_id
|
|
365
|
+
try:
|
|
366
|
+
result = api.session.rest_get(url, params=params)
|
|
367
|
+
except WebexError as e:
|
|
368
|
+
handle_rest_error(e)
|
|
369
|
+
if output == "json":
|
|
370
|
+
print_json(result)
|
|
371
|
+
else:
|
|
372
|
+
if isinstance(result, dict):
|
|
373
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
374
|
+
elif isinstance(result, list):
|
|
375
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
376
|
+
else:
|
|
377
|
+
print_json(result)
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
@app.command("update-selective-rules")
|
|
382
|
+
def update_selective_rules(
|
|
383
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
384
|
+
auto_attendant_id: str = typer.Argument(help="autoAttendantId"),
|
|
385
|
+
rule_id: str = typer.Argument(help="ruleId"),
|
|
386
|
+
name: str = typer.Option(None, "--name", help="Unique name for the selective rule in the auto attendant."),
|
|
387
|
+
enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="Reflects if rule is enabled."),
|
|
388
|
+
business_schedule: str = typer.Option(None, "--business-schedule", help="Name of the location's business schedule which determines wh"),
|
|
389
|
+
holiday_schedule: str = typer.Option(None, "--holiday-schedule", help="Name of the location's holiday schedule which determines whe"),
|
|
390
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
391
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
392
|
+
):
|
|
393
|
+
"""Update Selective Call Forwarding Rule for an Auto Attendant\n\nExample --json-body:\n '{"name":"...","enabled":true,"businessSchedule":"...","holidaySchedule":"...","forwardTo":{"selection":"FORWARD_TO_DEFAULT_NUMBER","phoneNumber":"..."},"callsFrom":{"selection":"ANY","customNumbers":{"privateNumberEnabled":"...","unavailableNumberEnabled":"...","numbers":"..."}},"callsTo":{"numbers":["..."]}}'."""
|
|
394
|
+
api = get_api(debug=debug)
|
|
395
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/autoAttendants/{auto_attendant_id}/callForwarding/selectiveRules/{rule_id}"
|
|
396
|
+
params = {}
|
|
397
|
+
org_id = get_org_id()
|
|
398
|
+
if org_id is not None:
|
|
399
|
+
params["orgId"] = org_id
|
|
400
|
+
if json_body:
|
|
401
|
+
body = json.loads(json_body)
|
|
402
|
+
else:
|
|
403
|
+
body = {}
|
|
404
|
+
if name is not None:
|
|
405
|
+
body["name"] = name
|
|
406
|
+
if enabled is not None:
|
|
407
|
+
body["enabled"] = enabled
|
|
408
|
+
if business_schedule is not None:
|
|
409
|
+
body["businessSchedule"] = business_schedule
|
|
410
|
+
if holiday_schedule is not None:
|
|
411
|
+
body["holidaySchedule"] = holiday_schedule
|
|
412
|
+
try:
|
|
413
|
+
result = api.session.rest_put(url, json=body, params=params)
|
|
414
|
+
except WebexError as e:
|
|
415
|
+
handle_rest_error(e)
|
|
416
|
+
typer.echo(f"Updated.")
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
@app.command("delete-selective-rules")
|
|
421
|
+
def delete_selective_rules(
|
|
422
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
423
|
+
auto_attendant_id: str = typer.Argument(help="autoAttendantId"),
|
|
424
|
+
rule_id: str = typer.Argument(help="ruleId"),
|
|
425
|
+
force: bool = typer.Option(False, "--force", help="Skip confirmation"),
|
|
426
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
427
|
+
):
|
|
428
|
+
"""Delete a Selective Call Forwarding Rule for an Auto Attendant."""
|
|
429
|
+
if not force:
|
|
430
|
+
typer.confirm(f"Delete {rule_id}?", abort=True)
|
|
431
|
+
api = get_api(debug=debug)
|
|
432
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/autoAttendants/{auto_attendant_id}/callForwarding/selectiveRules/{rule_id}"
|
|
433
|
+
params = {}
|
|
434
|
+
org_id = get_org_id()
|
|
435
|
+
if org_id is not None:
|
|
436
|
+
params["orgId"] = org_id
|
|
437
|
+
try:
|
|
438
|
+
api.session.rest_delete(url, params=params)
|
|
439
|
+
except WebexError as e:
|
|
440
|
+
handle_rest_error(e)
|
|
441
|
+
typer.echo(f"Deleted: {rule_id}")
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
@app.command("list-available-numbers-auto-attendants")
|
|
446
|
+
def list_available_numbers_auto_attendants(
|
|
447
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
448
|
+
phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
|
|
449
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
450
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
451
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
452
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
453
|
+
):
|
|
454
|
+
"""Get Auto Attendant Primary Available Phone Numbers."""
|
|
455
|
+
api = get_api(debug=debug)
|
|
456
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/autoAttendants/availableNumbers"
|
|
457
|
+
params = {}
|
|
458
|
+
if phone_number is not None:
|
|
459
|
+
params["phoneNumber"] = phone_number
|
|
460
|
+
if limit > 0:
|
|
461
|
+
params["max"] = limit
|
|
462
|
+
if offset > 0:
|
|
463
|
+
params["start"] = offset
|
|
464
|
+
org_id = get_org_id()
|
|
465
|
+
if org_id is not None:
|
|
466
|
+
params["orgId"] = org_id
|
|
467
|
+
result = None
|
|
468
|
+
try:
|
|
469
|
+
result = api.session.rest_get(url, params=params)
|
|
470
|
+
except WebexError as e:
|
|
471
|
+
handle_rest_error(e)
|
|
472
|
+
result = result or []
|
|
473
|
+
items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
474
|
+
if output == "json":
|
|
475
|
+
print_json(items)
|
|
476
|
+
else:
|
|
477
|
+
print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Extension', 'extension'), ('Location', 'locationName'), ('Enabled', 'enabled')], limit=limit)
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
@app.command("list-available-numbers-alternate")
|
|
482
|
+
def list_available_numbers_alternate(
|
|
483
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
484
|
+
phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
|
|
485
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
486
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
487
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
488
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
489
|
+
):
|
|
490
|
+
"""Get Auto Attendant Alternate Available Phone Numbers."""
|
|
491
|
+
api = get_api(debug=debug)
|
|
492
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/autoAttendants/alternate/availableNumbers"
|
|
493
|
+
params = {}
|
|
494
|
+
if phone_number is not None:
|
|
495
|
+
params["phoneNumber"] = phone_number
|
|
496
|
+
if limit > 0:
|
|
497
|
+
params["max"] = limit
|
|
498
|
+
if offset > 0:
|
|
499
|
+
params["start"] = offset
|
|
500
|
+
org_id = get_org_id()
|
|
501
|
+
if org_id is not None:
|
|
502
|
+
params["orgId"] = org_id
|
|
503
|
+
result = None
|
|
504
|
+
try:
|
|
505
|
+
result = api.session.rest_get(url, params=params)
|
|
506
|
+
except WebexError as e:
|
|
507
|
+
handle_rest_error(e)
|
|
508
|
+
result = result or []
|
|
509
|
+
items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
510
|
+
if output == "json":
|
|
511
|
+
print_json(items)
|
|
512
|
+
else:
|
|
513
|
+
print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Extension', 'extension'), ('Location', 'locationName'), ('Enabled', 'enabled')], limit=limit)
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
@app.command("list-available-numbers-call-forwarding")
|
|
518
|
+
def list_available_numbers_call_forwarding(
|
|
519
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
520
|
+
phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
|
|
521
|
+
owner_name: str = typer.Option(None, "--owner-name", help="Return the list of phone numbers that are owned by the given"),
|
|
522
|
+
extension: str = typer.Option(None, "--extension", help="Returns the list of PSTN phone numbers with the given `exten"),
|
|
523
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
524
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
525
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
526
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
527
|
+
):
|
|
528
|
+
"""Get Auto Attendant Call Forward Available Phone Numbers."""
|
|
529
|
+
api = get_api(debug=debug)
|
|
530
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/autoAttendants/callForwarding/availableNumbers"
|
|
531
|
+
params = {}
|
|
532
|
+
if phone_number is not None:
|
|
533
|
+
params["phoneNumber"] = phone_number
|
|
534
|
+
if owner_name is not None:
|
|
535
|
+
params["ownerName"] = owner_name
|
|
536
|
+
if extension is not None:
|
|
537
|
+
params["extension"] = extension
|
|
538
|
+
if limit > 0:
|
|
539
|
+
params["max"] = limit
|
|
540
|
+
if offset > 0:
|
|
541
|
+
params["start"] = offset
|
|
542
|
+
org_id = get_org_id()
|
|
543
|
+
if org_id is not None:
|
|
544
|
+
params["orgId"] = org_id
|
|
545
|
+
result = None
|
|
546
|
+
try:
|
|
547
|
+
result = api.session.rest_get(url, params=params)
|
|
548
|
+
except WebexError as e:
|
|
549
|
+
handle_rest_error(e)
|
|
550
|
+
result = result or []
|
|
551
|
+
items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
552
|
+
if output == "json":
|
|
553
|
+
print_json(items)
|
|
554
|
+
else:
|
|
555
|
+
print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Extension', 'extension'), ('Location', 'locationName'), ('Enabled', 'enabled')], limit=limit)
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
@app.command("switch-mode-for")
|
|
560
|
+
def switch_mode_for(
|
|
561
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
562
|
+
auto_attendant_id: str = typer.Argument(help="autoAttendantId"),
|
|
563
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body"),
|
|
564
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
565
|
+
):
|
|
566
|
+
"""Switch Mode for Call Forwarding Settings for an Auto Attendant."""
|
|
567
|
+
api = get_api(debug=debug)
|
|
568
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/autoAttendants/{auto_attendant_id}/callForwarding/actions/switchMode/invoke"
|
|
569
|
+
params = {}
|
|
570
|
+
org_id = get_org_id()
|
|
571
|
+
if org_id is not None:
|
|
572
|
+
params["orgId"] = org_id
|
|
573
|
+
if json_body:
|
|
574
|
+
body = json.loads(json_body)
|
|
575
|
+
else:
|
|
576
|
+
body = {}
|
|
577
|
+
try:
|
|
578
|
+
result = api.session.rest_post(url, json=body, params=params)
|
|
579
|
+
except WebexError as e:
|
|
580
|
+
handle_rest_error(e)
|
|
581
|
+
print_json(result)
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
@app.command("delete-announcements")
|
|
586
|
+
def delete_announcements(
|
|
587
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
588
|
+
auto_attendant_id: str = typer.Argument(help="autoAttendantId"),
|
|
589
|
+
file_name: str = typer.Argument(help="fileName"),
|
|
590
|
+
force: bool = typer.Option(False, "--force", help="Skip confirmation"),
|
|
591
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
592
|
+
):
|
|
593
|
+
"""Delete a Auto Attendant Announcement File."""
|
|
594
|
+
if not force:
|
|
595
|
+
typer.confirm(f"Delete {file_name}?", abort=True)
|
|
596
|
+
api = get_api(debug=debug)
|
|
597
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/autoAttendants/{auto_attendant_id}/announcements/{file_name}"
|
|
598
|
+
params = {}
|
|
599
|
+
org_id = get_org_id()
|
|
600
|
+
if org_id is not None:
|
|
601
|
+
params["orgId"] = org_id
|
|
602
|
+
try:
|
|
603
|
+
api.session.rest_delete(url, params=params)
|
|
604
|
+
except WebexError as e:
|
|
605
|
+
handle_rest_error(e)
|
|
606
|
+
typer.echo(f"Deleted: {file_name}")
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
@app.command("list-announcements")
|
|
611
|
+
def list_announcements(
|
|
612
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
613
|
+
auto_attendant_id: str = typer.Argument(help="autoAttendantId"),
|
|
614
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
615
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
616
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
617
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
618
|
+
):
|
|
619
|
+
"""Read the List of Auto Attendant Announcement Files."""
|
|
620
|
+
api = get_api(debug=debug)
|
|
621
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/autoAttendants/{auto_attendant_id}/announcements"
|
|
622
|
+
params = {}
|
|
623
|
+
if limit > 0:
|
|
624
|
+
params["max"] = limit
|
|
625
|
+
if offset > 0:
|
|
626
|
+
params["start"] = offset
|
|
627
|
+
org_id = get_org_id()
|
|
628
|
+
if org_id is not None:
|
|
629
|
+
params["orgId"] = org_id
|
|
630
|
+
result = None
|
|
631
|
+
try:
|
|
632
|
+
result = api.session.rest_get(url, params=params)
|
|
633
|
+
except WebexError as e:
|
|
634
|
+
handle_rest_error(e)
|
|
635
|
+
result = result or []
|
|
636
|
+
items = result.get("announcements", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
637
|
+
if output == "json":
|
|
638
|
+
print_json(items)
|
|
639
|
+
else:
|
|
640
|
+
print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Extension', 'extension'), ('Location', 'locationName'), ('Enabled', 'enabled')], limit=limit)
|
|
641
|
+
|
|
642
|
+
|