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,577 @@
|
|
|
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 hunt-group.")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@app.command("list")
|
|
13
|
+
def cmd_list(
|
|
14
|
+
location_id: str = typer.Option(None, "--location-id", help="Only return hunt groups with matching location ID."),
|
|
15
|
+
name: str = typer.Option(None, "--name", help="Only return hunt groups with the matching name."),
|
|
16
|
+
phone_number: str = typer.Option(None, "--phone-number", help="Only return hunt groups with the matching primary phone numb"),
|
|
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 Hunt Groups."""
|
|
23
|
+
api = get_api(debug=debug)
|
|
24
|
+
url = f"https://webexapis.com/v1/telephony/config/huntGroups"
|
|
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("huntGroups", 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="huntGroups"))
|
|
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'), ('Enabled', 'enabled')], limit=limit)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@app.command("create")
|
|
56
|
+
def create(
|
|
57
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
58
|
+
name: str = typer.Option(None, "--name", help="(required) Unique name for the hunt group."),
|
|
59
|
+
phone_number: str = typer.Option(None, "--phone-number", help="Primary phone number of the hunt group. Either phone number"),
|
|
60
|
+
extension: str = typer.Option(None, "--extension", help="Primary phone extension of the hunt group. Either phone numb"),
|
|
61
|
+
language_code: str = typer.Option(None, "--language-code", help="Language code."),
|
|
62
|
+
first_name: str = typer.Option(None, "--first-name", help="First name to be shown when calls are forwarded out of this"),
|
|
63
|
+
last_name: str = typer.Option(None, "--last-name", help="Last name to be shown when calls are forwarded out of this h"),
|
|
64
|
+
time_zone: str = typer.Option(None, "--time-zone", help="Time zone for the hunt group."),
|
|
65
|
+
enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="(required) Whether or not the hunt group is enabled."),
|
|
66
|
+
hunt_group_caller_id_for_outgoing_calls_enabled: bool = typer.Option(None, "--hunt-group-caller-id-for-outgoing-calls-enabled/--no-hunt-group-caller-id-for-outgoing-calls-enabled", help="Enable the hunt group to be used as the caller ID when the a"),
|
|
67
|
+
dial_by_name: str = typer.Option(None, "--dial-by-name", help="The name to be used for dial by name functions. Characters"),
|
|
68
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
69
|
+
output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
|
|
70
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
71
|
+
):
|
|
72
|
+
"""Create a Hunt Group\n\nExample --json-body:\n '{"name":"...","callPolicies":{"policy":"CIRCULAR","noAnswer":{"nextAgentEnabled":"...","nextAgentRings":"...","forwardEnabled":"...","numberOfRings":"...","destinationVoicemailEnabled":"...","destination":"..."},"waitingEnabled":true,"groupBusyEnabled":true,"allowMembersToControlGroupBusyEnabled":true,"busyRedirect":{"enabled":"...","destination":"...","destinationVoicemailEnabled":"..."},"businessContinuityRedirect":{"enabled":"...","destination":"...","destinationVoicemailEnabled":"..."}},"agents":[{"id":"...","weight":"..."}],"enabled":true,"phoneNumber":"...","extension":"...","languageCode":"...","firstName":"..."}'."""
|
|
73
|
+
api = get_api(debug=debug)
|
|
74
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/huntGroups"
|
|
75
|
+
params = {}
|
|
76
|
+
org_id = get_org_id()
|
|
77
|
+
if org_id is not None:
|
|
78
|
+
params["orgId"] = org_id
|
|
79
|
+
if json_body:
|
|
80
|
+
body = json.loads(json_body)
|
|
81
|
+
else:
|
|
82
|
+
body = {}
|
|
83
|
+
if name is not None:
|
|
84
|
+
body["name"] = name
|
|
85
|
+
if phone_number is not None:
|
|
86
|
+
body["phoneNumber"] = phone_number
|
|
87
|
+
if extension is not None:
|
|
88
|
+
body["extension"] = extension
|
|
89
|
+
if language_code is not None:
|
|
90
|
+
body["languageCode"] = language_code
|
|
91
|
+
if first_name is not None:
|
|
92
|
+
body["firstName"] = first_name
|
|
93
|
+
if last_name is not None:
|
|
94
|
+
body["lastName"] = last_name
|
|
95
|
+
if time_zone is not None:
|
|
96
|
+
body["timeZone"] = time_zone
|
|
97
|
+
if enabled is not None:
|
|
98
|
+
body["enabled"] = enabled
|
|
99
|
+
if hunt_group_caller_id_for_outgoing_calls_enabled is not None:
|
|
100
|
+
body["huntGroupCallerIdForOutgoingCallsEnabled"] = hunt_group_caller_id_for_outgoing_calls_enabled
|
|
101
|
+
if dial_by_name is not None:
|
|
102
|
+
body["dialByName"] = dial_by_name
|
|
103
|
+
_missing = [f for f in ['name', 'enabled'] if f not in body or body[f] is None]
|
|
104
|
+
if _missing:
|
|
105
|
+
typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
|
|
106
|
+
raise typer.Exit(1)
|
|
107
|
+
body.setdefault('callPolicies', {'policy': 'CIRCULAR', 'noAnswer': {'nextAgentEnabled': True, 'nextAgentRings': 3, 'forwardEnabled': False, 'numberOfRings': 15, 'destinationVoicemailEnabled': False}})
|
|
108
|
+
body.setdefault('agents', [])
|
|
109
|
+
body.setdefault('enabled', True)
|
|
110
|
+
try:
|
|
111
|
+
result = api.session.rest_post(url, json=body, params=params)
|
|
112
|
+
except WebexError as e:
|
|
113
|
+
handle_rest_error(e)
|
|
114
|
+
if output == "json":
|
|
115
|
+
print_json(result)
|
|
116
|
+
elif isinstance(result, dict) and "id" in result:
|
|
117
|
+
typer.echo(f"Created: {result['id']}")
|
|
118
|
+
elif not result or result == {}:
|
|
119
|
+
typer.echo("Created.")
|
|
120
|
+
else:
|
|
121
|
+
print_json(result)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@app.command("show")
|
|
126
|
+
def show(
|
|
127
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
128
|
+
hunt_group_id: str = typer.Argument(help="huntGroupId"),
|
|
129
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
130
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
131
|
+
):
|
|
132
|
+
"""Get Details for a Hunt Group."""
|
|
133
|
+
api = get_api(debug=debug)
|
|
134
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/huntGroups/{hunt_group_id}"
|
|
135
|
+
params = {}
|
|
136
|
+
org_id = get_org_id()
|
|
137
|
+
if org_id is not None:
|
|
138
|
+
params["orgId"] = org_id
|
|
139
|
+
try:
|
|
140
|
+
result = api.session.rest_get(url, params=params)
|
|
141
|
+
except WebexError as e:
|
|
142
|
+
handle_rest_error(e)
|
|
143
|
+
if output == "json":
|
|
144
|
+
print_json(result)
|
|
145
|
+
else:
|
|
146
|
+
if isinstance(result, dict):
|
|
147
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
148
|
+
elif isinstance(result, list):
|
|
149
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
150
|
+
else:
|
|
151
|
+
print_json(result)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@app.command("update")
|
|
156
|
+
def update(
|
|
157
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
158
|
+
hunt_group_id: str = typer.Argument(help="huntGroupId"),
|
|
159
|
+
enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="Whether or not the hunt group is enabled."),
|
|
160
|
+
name: str = typer.Option(None, "--name", help="Unique name for the hunt group."),
|
|
161
|
+
phone_number: str = typer.Option(None, "--phone-number", help="Primary phone number of the hunt group."),
|
|
162
|
+
extension: str = typer.Option(None, "--extension", help="Primary phone extension of the hunt group."),
|
|
163
|
+
distinctive_ring: bool = typer.Option(None, "--distinctive-ring/--no-distinctive-ring", help="Whether or not the hunt group has the distinctive ring optio"),
|
|
164
|
+
language_code: str = typer.Option(None, "--language-code", help="Language code."),
|
|
165
|
+
first_name: str = typer.Option(None, "--first-name", help="First name to be shown when calls are forwarded out of this"),
|
|
166
|
+
last_name: str = typer.Option(None, "--last-name", help="Last name to be shown when calls are forwarded out of this h"),
|
|
167
|
+
time_zone: str = typer.Option(None, "--time-zone", help="Time zone for the hunt group."),
|
|
168
|
+
hunt_group_caller_id_for_outgoing_calls_enabled: bool = typer.Option(None, "--hunt-group-caller-id-for-outgoing-calls-enabled/--no-hunt-group-caller-id-for-outgoing-calls-enabled", help="Enable the hunt group to be used as the caller ID when the a"),
|
|
169
|
+
dial_by_name: str = typer.Option(None, "--dial-by-name", help="Sets or clears the name to be used for dial by name function"),
|
|
170
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
171
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
172
|
+
):
|
|
173
|
+
"""Update a Hunt Group\n\nExample --json-body:\n '{"enabled":true,"name":"...","phoneNumber":"...","extension":"...","distinctiveRing":true,"alternateNumbers":[{"phoneNumber":"...","ringPattern":"..."}],"languageCode":"...","firstName":"..."}'."""
|
|
174
|
+
api = get_api(debug=debug)
|
|
175
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/huntGroups/{hunt_group_id}"
|
|
176
|
+
params = {}
|
|
177
|
+
org_id = get_org_id()
|
|
178
|
+
if org_id is not None:
|
|
179
|
+
params["orgId"] = org_id
|
|
180
|
+
if json_body:
|
|
181
|
+
body = json.loads(json_body)
|
|
182
|
+
else:
|
|
183
|
+
body = {}
|
|
184
|
+
if enabled is not None:
|
|
185
|
+
body["enabled"] = enabled
|
|
186
|
+
if name is not None:
|
|
187
|
+
body["name"] = name
|
|
188
|
+
if phone_number is not None:
|
|
189
|
+
body["phoneNumber"] = phone_number
|
|
190
|
+
if extension is not None:
|
|
191
|
+
body["extension"] = extension
|
|
192
|
+
if distinctive_ring is not None:
|
|
193
|
+
body["distinctiveRing"] = distinctive_ring
|
|
194
|
+
if language_code is not None:
|
|
195
|
+
body["languageCode"] = language_code
|
|
196
|
+
if first_name is not None:
|
|
197
|
+
body["firstName"] = first_name
|
|
198
|
+
if last_name is not None:
|
|
199
|
+
body["lastName"] = last_name
|
|
200
|
+
if time_zone is not None:
|
|
201
|
+
body["timeZone"] = time_zone
|
|
202
|
+
if hunt_group_caller_id_for_outgoing_calls_enabled is not None:
|
|
203
|
+
body["huntGroupCallerIdForOutgoingCallsEnabled"] = hunt_group_caller_id_for_outgoing_calls_enabled
|
|
204
|
+
if dial_by_name is not None:
|
|
205
|
+
body["dialByName"] = dial_by_name
|
|
206
|
+
try:
|
|
207
|
+
result = api.session.rest_put(url, json=body, params=params)
|
|
208
|
+
except WebexError as e:
|
|
209
|
+
handle_rest_error(e)
|
|
210
|
+
typer.echo(f"Updated.")
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
@app.command("delete")
|
|
215
|
+
def delete(
|
|
216
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
217
|
+
hunt_group_id: str = typer.Argument(help="huntGroupId"),
|
|
218
|
+
force: bool = typer.Option(False, "--force", help="Skip confirmation"),
|
|
219
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
220
|
+
):
|
|
221
|
+
"""Delete a Hunt Group."""
|
|
222
|
+
if not force:
|
|
223
|
+
typer.confirm(f"Delete {hunt_group_id}?", abort=True)
|
|
224
|
+
api = get_api(debug=debug)
|
|
225
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/huntGroups/{hunt_group_id}"
|
|
226
|
+
params = {}
|
|
227
|
+
org_id = get_org_id()
|
|
228
|
+
if org_id is not None:
|
|
229
|
+
params["orgId"] = org_id
|
|
230
|
+
try:
|
|
231
|
+
api.session.rest_delete(url, params=params)
|
|
232
|
+
except WebexError as e:
|
|
233
|
+
handle_rest_error(e)
|
|
234
|
+
typer.echo(f"Deleted: {hunt_group_id}")
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
@app.command("show-call-forwarding")
|
|
239
|
+
def show_call_forwarding(
|
|
240
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
241
|
+
hunt_group_id: str = typer.Argument(help="huntGroupId"),
|
|
242
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
243
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
244
|
+
):
|
|
245
|
+
"""Get Call Forwarding Settings for a Hunt Group."""
|
|
246
|
+
api = get_api(debug=debug)
|
|
247
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/huntGroups/{hunt_group_id}/callForwarding"
|
|
248
|
+
params = {}
|
|
249
|
+
org_id = get_org_id()
|
|
250
|
+
if org_id is not None:
|
|
251
|
+
params["orgId"] = org_id
|
|
252
|
+
try:
|
|
253
|
+
result = api.session.rest_get(url, params=params)
|
|
254
|
+
except WebexError as e:
|
|
255
|
+
handle_rest_error(e)
|
|
256
|
+
if output == "json":
|
|
257
|
+
print_json(result)
|
|
258
|
+
else:
|
|
259
|
+
if isinstance(result, dict):
|
|
260
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
261
|
+
elif isinstance(result, list):
|
|
262
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
263
|
+
else:
|
|
264
|
+
print_json(result)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
@app.command("update-call-forwarding")
|
|
269
|
+
def update_call_forwarding(
|
|
270
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
271
|
+
hunt_group_id: str = typer.Argument(help="huntGroupId"),
|
|
272
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
273
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
274
|
+
):
|
|
275
|
+
"""Update Call Forwarding Settings for a Hunt Group\n\nExample --json-body:\n '{"callForwarding":{"always":{"enabled":"...","destination":"...","ringReminderEnabled":"...","destinationVoicemailEnabled":"..."},"selective":{"enabled":"...","destination":"...","ringReminderEnabled":"...","destinationVoicemailEnabled":"..."},"rules":["..."],"operatingModes":{"enabled":"...","modes":"..."}}}'."""
|
|
276
|
+
api = get_api(debug=debug)
|
|
277
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/huntGroups/{hunt_group_id}/callForwarding"
|
|
278
|
+
params = {}
|
|
279
|
+
org_id = get_org_id()
|
|
280
|
+
if org_id is not None:
|
|
281
|
+
params["orgId"] = org_id
|
|
282
|
+
if json_body:
|
|
283
|
+
body = json.loads(json_body)
|
|
284
|
+
else:
|
|
285
|
+
body = {}
|
|
286
|
+
try:
|
|
287
|
+
result = api.session.rest_put(url, json=body, params=params)
|
|
288
|
+
except WebexError as e:
|
|
289
|
+
handle_rest_error(e)
|
|
290
|
+
typer.echo(f"Updated.")
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
@app.command("create-selective-rules")
|
|
295
|
+
def create_selective_rules(
|
|
296
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
297
|
+
hunt_group_id: str = typer.Argument(help="huntGroupId"),
|
|
298
|
+
name: str = typer.Option(None, "--name", help="(required) Unique name for the selective rule in the hunt group."),
|
|
299
|
+
enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="Reflects if rule is enabled."),
|
|
300
|
+
holiday_schedule: str = typer.Option(None, "--holiday-schedule", help="Name of the location's holiday schedule which determines whe"),
|
|
301
|
+
business_schedule: str = typer.Option(None, "--business-schedule", help="Name of the location's business schedule which determines wh"),
|
|
302
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
303
|
+
output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
|
|
304
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
305
|
+
):
|
|
306
|
+
"""Create a Selective Call Forwarding Rule for a Hunt Group\n\nExample --json-body:\n '{"name":"...","callsFrom":{"selection":"ANY","customNumbers":{"privateNumberEnabled":"...","unavailableNumberEnabled":"...","numbers":"..."}},"callsTo":{"numbers":["..."]},"enabled":true,"holidaySchedule":"...","businessSchedule":"...","forwardTo":{"selection":"FORWARD_TO_DEFAULT_NUMBER","phoneNumber":"..."}}'."""
|
|
307
|
+
api = get_api(debug=debug)
|
|
308
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/huntGroups/{hunt_group_id}/callForwarding/selectiveRules"
|
|
309
|
+
params = {}
|
|
310
|
+
org_id = get_org_id()
|
|
311
|
+
if org_id is not None:
|
|
312
|
+
params["orgId"] = org_id
|
|
313
|
+
if json_body:
|
|
314
|
+
body = json.loads(json_body)
|
|
315
|
+
else:
|
|
316
|
+
body = {}
|
|
317
|
+
if name is not None:
|
|
318
|
+
body["name"] = name
|
|
319
|
+
if enabled is not None:
|
|
320
|
+
body["enabled"] = enabled
|
|
321
|
+
if holiday_schedule is not None:
|
|
322
|
+
body["holidaySchedule"] = holiday_schedule
|
|
323
|
+
if business_schedule is not None:
|
|
324
|
+
body["businessSchedule"] = business_schedule
|
|
325
|
+
_missing = [f for f in ['name'] if f not in body or body[f] is None]
|
|
326
|
+
if _missing:
|
|
327
|
+
typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
|
|
328
|
+
raise typer.Exit(1)
|
|
329
|
+
try:
|
|
330
|
+
result = api.session.rest_post(url, json=body, params=params)
|
|
331
|
+
except WebexError as e:
|
|
332
|
+
handle_rest_error(e)
|
|
333
|
+
if output == "json":
|
|
334
|
+
print_json(result)
|
|
335
|
+
elif isinstance(result, dict) and "id" in result:
|
|
336
|
+
typer.echo(f"Created: {result['id']}")
|
|
337
|
+
elif not result or result == {}:
|
|
338
|
+
typer.echo("Created.")
|
|
339
|
+
else:
|
|
340
|
+
print_json(result)
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
@app.command("show-selective-rules")
|
|
345
|
+
def show_selective_rules(
|
|
346
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
347
|
+
hunt_group_id: str = typer.Argument(help="huntGroupId"),
|
|
348
|
+
rule_id: str = typer.Argument(help="ruleId"),
|
|
349
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
350
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
351
|
+
):
|
|
352
|
+
"""Get Selective Call Forwarding Rule for a Hunt Group."""
|
|
353
|
+
api = get_api(debug=debug)
|
|
354
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/huntGroups/{hunt_group_id}/callForwarding/selectiveRules/{rule_id}"
|
|
355
|
+
params = {}
|
|
356
|
+
org_id = get_org_id()
|
|
357
|
+
if org_id is not None:
|
|
358
|
+
params["orgId"] = org_id
|
|
359
|
+
try:
|
|
360
|
+
result = api.session.rest_get(url, params=params)
|
|
361
|
+
except WebexError as e:
|
|
362
|
+
handle_rest_error(e)
|
|
363
|
+
if output == "json":
|
|
364
|
+
print_json(result)
|
|
365
|
+
else:
|
|
366
|
+
if isinstance(result, dict):
|
|
367
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
368
|
+
elif isinstance(result, list):
|
|
369
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
370
|
+
else:
|
|
371
|
+
print_json(result)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
@app.command("update-selective-rules")
|
|
376
|
+
def update_selective_rules(
|
|
377
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
378
|
+
hunt_group_id: str = typer.Argument(help="huntGroupId"),
|
|
379
|
+
rule_id: str = typer.Argument(help="ruleId"),
|
|
380
|
+
name: str = typer.Option(None, "--name", help="Unique name for the selective rule in the hunt group."),
|
|
381
|
+
enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="Reflects if rule is enabled."),
|
|
382
|
+
holiday_schedule: str = typer.Option(None, "--holiday-schedule", help="Name of the location's holiday schedule which determines whe"),
|
|
383
|
+
business_schedule: str = typer.Option(None, "--business-schedule", help="Name of the location's business schedule which determines wh"),
|
|
384
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
385
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
386
|
+
):
|
|
387
|
+
"""Update a Selective Call Forwarding Rule for a Hunt Group\n\nExample --json-body:\n '{"name":"...","enabled":true,"holidaySchedule":"...","businessSchedule":"...","forwardTo":{"selection":"FORWARD_TO_DEFAULT_NUMBER","phoneNumber":"..."},"callsFrom":{"selection":"ANY","customNumbers":{"privateNumberEnabled":"...","unavailableNumberEnabled":"...","numbers":"..."}},"callsTo":{"numbers":["..."]}}'."""
|
|
388
|
+
api = get_api(debug=debug)
|
|
389
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/huntGroups/{hunt_group_id}/callForwarding/selectiveRules/{rule_id}"
|
|
390
|
+
params = {}
|
|
391
|
+
org_id = get_org_id()
|
|
392
|
+
if org_id is not None:
|
|
393
|
+
params["orgId"] = org_id
|
|
394
|
+
if json_body:
|
|
395
|
+
body = json.loads(json_body)
|
|
396
|
+
else:
|
|
397
|
+
body = {}
|
|
398
|
+
if name is not None:
|
|
399
|
+
body["name"] = name
|
|
400
|
+
if enabled is not None:
|
|
401
|
+
body["enabled"] = enabled
|
|
402
|
+
if holiday_schedule is not None:
|
|
403
|
+
body["holidaySchedule"] = holiday_schedule
|
|
404
|
+
if business_schedule is not None:
|
|
405
|
+
body["businessSchedule"] = business_schedule
|
|
406
|
+
try:
|
|
407
|
+
result = api.session.rest_put(url, json=body, params=params)
|
|
408
|
+
except WebexError as e:
|
|
409
|
+
handle_rest_error(e)
|
|
410
|
+
typer.echo(f"Updated.")
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
@app.command("delete-selective-rules")
|
|
415
|
+
def delete_selective_rules(
|
|
416
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
417
|
+
hunt_group_id: str = typer.Argument(help="huntGroupId"),
|
|
418
|
+
rule_id: str = typer.Argument(help="ruleId"),
|
|
419
|
+
force: bool = typer.Option(False, "--force", help="Skip confirmation"),
|
|
420
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
421
|
+
):
|
|
422
|
+
"""Delete a Selective Call Forwarding Rule for a Hunt Group."""
|
|
423
|
+
if not force:
|
|
424
|
+
typer.confirm(f"Delete {rule_id}?", abort=True)
|
|
425
|
+
api = get_api(debug=debug)
|
|
426
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/huntGroups/{hunt_group_id}/callForwarding/selectiveRules/{rule_id}"
|
|
427
|
+
params = {}
|
|
428
|
+
org_id = get_org_id()
|
|
429
|
+
if org_id is not None:
|
|
430
|
+
params["orgId"] = org_id
|
|
431
|
+
try:
|
|
432
|
+
api.session.rest_delete(url, params=params)
|
|
433
|
+
except WebexError as e:
|
|
434
|
+
handle_rest_error(e)
|
|
435
|
+
typer.echo(f"Deleted: {rule_id}")
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
@app.command("list-available-numbers-hunt-groups")
|
|
440
|
+
def list_available_numbers_hunt_groups(
|
|
441
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
442
|
+
phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
|
|
443
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
444
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
445
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
446
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
447
|
+
):
|
|
448
|
+
"""Get Hunt Group Primary Available Phone Numbers."""
|
|
449
|
+
api = get_api(debug=debug)
|
|
450
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/huntGroups/availableNumbers"
|
|
451
|
+
params = {}
|
|
452
|
+
if phone_number is not None:
|
|
453
|
+
params["phoneNumber"] = phone_number
|
|
454
|
+
if limit > 0:
|
|
455
|
+
params["max"] = limit
|
|
456
|
+
if offset > 0:
|
|
457
|
+
params["start"] = offset
|
|
458
|
+
org_id = get_org_id()
|
|
459
|
+
if org_id is not None:
|
|
460
|
+
params["orgId"] = org_id
|
|
461
|
+
result = None
|
|
462
|
+
try:
|
|
463
|
+
result = api.session.rest_get(url, params=params)
|
|
464
|
+
except WebexError as e:
|
|
465
|
+
handle_rest_error(e)
|
|
466
|
+
result = result or []
|
|
467
|
+
items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
468
|
+
if output == "json":
|
|
469
|
+
print_json(items)
|
|
470
|
+
else:
|
|
471
|
+
print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Extension', 'extension'), ('Enabled', 'enabled')], limit=limit)
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
@app.command("list-available-numbers-alternate")
|
|
476
|
+
def list_available_numbers_alternate(
|
|
477
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
478
|
+
phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
|
|
479
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
480
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
481
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
482
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
483
|
+
):
|
|
484
|
+
"""Get Hunt Group Alternate Available Phone Numbers."""
|
|
485
|
+
api = get_api(debug=debug)
|
|
486
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/huntGroups/alternate/availableNumbers"
|
|
487
|
+
params = {}
|
|
488
|
+
if phone_number is not None:
|
|
489
|
+
params["phoneNumber"] = phone_number
|
|
490
|
+
if limit > 0:
|
|
491
|
+
params["max"] = limit
|
|
492
|
+
if offset > 0:
|
|
493
|
+
params["start"] = offset
|
|
494
|
+
org_id = get_org_id()
|
|
495
|
+
if org_id is not None:
|
|
496
|
+
params["orgId"] = org_id
|
|
497
|
+
result = None
|
|
498
|
+
try:
|
|
499
|
+
result = api.session.rest_get(url, params=params)
|
|
500
|
+
except WebexError as e:
|
|
501
|
+
handle_rest_error(e)
|
|
502
|
+
result = result or []
|
|
503
|
+
items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
504
|
+
if output == "json":
|
|
505
|
+
print_json(items)
|
|
506
|
+
else:
|
|
507
|
+
print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Extension', 'extension'), ('Enabled', 'enabled')], limit=limit)
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
@app.command("list-available-numbers-call-forwarding")
|
|
512
|
+
def list_available_numbers_call_forwarding(
|
|
513
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
514
|
+
phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
|
|
515
|
+
owner_name: str = typer.Option(None, "--owner-name", help="Return the list of phone numbers that are owned by the given"),
|
|
516
|
+
extension: str = typer.Option(None, "--extension", help="Returns the list of PSTN phone numbers with the given `exten"),
|
|
517
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
518
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
519
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
520
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
521
|
+
):
|
|
522
|
+
"""Get Hunt Group Call Forward Available Phone Numbers."""
|
|
523
|
+
api = get_api(debug=debug)
|
|
524
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/huntGroups/callForwarding/availableNumbers"
|
|
525
|
+
params = {}
|
|
526
|
+
if phone_number is not None:
|
|
527
|
+
params["phoneNumber"] = phone_number
|
|
528
|
+
if owner_name is not None:
|
|
529
|
+
params["ownerName"] = owner_name
|
|
530
|
+
if extension is not None:
|
|
531
|
+
params["extension"] = extension
|
|
532
|
+
if limit > 0:
|
|
533
|
+
params["max"] = limit
|
|
534
|
+
if offset > 0:
|
|
535
|
+
params["start"] = offset
|
|
536
|
+
org_id = get_org_id()
|
|
537
|
+
if org_id is not None:
|
|
538
|
+
params["orgId"] = org_id
|
|
539
|
+
result = None
|
|
540
|
+
try:
|
|
541
|
+
result = api.session.rest_get(url, params=params)
|
|
542
|
+
except WebexError as e:
|
|
543
|
+
handle_rest_error(e)
|
|
544
|
+
result = result or []
|
|
545
|
+
items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
546
|
+
if output == "json":
|
|
547
|
+
print_json(items)
|
|
548
|
+
else:
|
|
549
|
+
print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Extension', 'extension'), ('Enabled', 'enabled')], limit=limit)
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
@app.command("switch-mode-for")
|
|
554
|
+
def switch_mode_for(
|
|
555
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
556
|
+
hunt_group_id: str = typer.Argument(help="huntGroupId"),
|
|
557
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body"),
|
|
558
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
559
|
+
):
|
|
560
|
+
"""Switch Mode for Call Forwarding Settings for a Hunt Group."""
|
|
561
|
+
api = get_api(debug=debug)
|
|
562
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/huntGroups/{hunt_group_id}/callForwarding/actions/switchMode/invoke"
|
|
563
|
+
params = {}
|
|
564
|
+
org_id = get_org_id()
|
|
565
|
+
if org_id is not None:
|
|
566
|
+
params["orgId"] = org_id
|
|
567
|
+
if json_body:
|
|
568
|
+
body = json.loads(json_body)
|
|
569
|
+
else:
|
|
570
|
+
body = {}
|
|
571
|
+
try:
|
|
572
|
+
result = api.session.rest_post(url, json=body, params=params)
|
|
573
|
+
except WebexError as e:
|
|
574
|
+
handle_rest_error(e)
|
|
575
|
+
print_json(result)
|
|
576
|
+
|
|
577
|
+
|
|
@@ -0,0 +1,71 @@
|
|
|
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 hybrid-clusters.")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@app.command("list")
|
|
13
|
+
def cmd_list(
|
|
14
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
15
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
16
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
17
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
18
|
+
):
|
|
19
|
+
"""List Hybrid Clusters."""
|
|
20
|
+
api = get_api(debug=debug)
|
|
21
|
+
url = f"https://webexapis.com/v1/hybrid/clusters"
|
|
22
|
+
params = {}
|
|
23
|
+
if limit > 0:
|
|
24
|
+
params["max"] = limit
|
|
25
|
+
if offset > 0:
|
|
26
|
+
params["start"] = offset
|
|
27
|
+
org_id = get_org_id()
|
|
28
|
+
if org_id is not None:
|
|
29
|
+
params["orgId"] = org_id
|
|
30
|
+
result = None
|
|
31
|
+
try:
|
|
32
|
+
result = api.session.rest_get(url, params=params)
|
|
33
|
+
except WebexError as e:
|
|
34
|
+
handle_rest_error(e)
|
|
35
|
+
result = result or []
|
|
36
|
+
items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
37
|
+
if output == "json":
|
|
38
|
+
print_json(items)
|
|
39
|
+
else:
|
|
40
|
+
print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@app.command("show")
|
|
45
|
+
def show(
|
|
46
|
+
hybrid_cluster_id: str = typer.Argument(help="hybridClusterId"),
|
|
47
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
48
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
49
|
+
):
|
|
50
|
+
"""Get Hybrid Cluster Details."""
|
|
51
|
+
api = get_api(debug=debug)
|
|
52
|
+
url = f"https://webexapis.com/v1/hybrid/clusters/{hybrid_cluster_id}"
|
|
53
|
+
params = {}
|
|
54
|
+
org_id = get_org_id()
|
|
55
|
+
if org_id is not None:
|
|
56
|
+
params["orgId"] = org_id
|
|
57
|
+
try:
|
|
58
|
+
result = api.session.rest_get(url, params=params)
|
|
59
|
+
except WebexError as e:
|
|
60
|
+
handle_rest_error(e)
|
|
61
|
+
if output == "json":
|
|
62
|
+
print_json(result)
|
|
63
|
+
else:
|
|
64
|
+
if isinstance(result, dict):
|
|
65
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
66
|
+
elif isinstance(result, list):
|
|
67
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
68
|
+
else:
|
|
69
|
+
print_json(result)
|
|
70
|
+
|
|
71
|
+
|