wxcli 1.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (413) hide show
  1. wxcli/__init__.py +4 -0
  2. wxcli/_playbook/.claude/agents/migration-advisor.md +239 -0
  3. wxcli/_playbook/.claude/agents/wxc-calling-builder.md +946 -0
  4. wxcli/_playbook/.claude/rules/cleanup.md +29 -0
  5. wxcli/_playbook/.claude/rules/cucm-migration.md +58 -0
  6. wxcli/_playbook/.claude/rules/org-health.md +14 -0
  7. wxcli/_playbook/.claude/settings.json +8 -0
  8. wxcli/_playbook/.claude/skills/audit-compliance/SKILL.md +515 -0
  9. wxcli/_playbook/.claude/skills/call-control/SKILL.md +747 -0
  10. wxcli/_playbook/.claude/skills/configure-features/SKILL.md +623 -0
  11. wxcli/_playbook/.claude/skills/configure-routing/SKILL.md +627 -0
  12. wxcli/_playbook/.claude/skills/contact-center/SKILL.md +1181 -0
  13. wxcli/_playbook/.claude/skills/cucm-migrate/SKILL.md +820 -0
  14. wxcli/_playbook/.claude/skills/customer-assist/SKILL.md +443 -0
  15. wxcli/_playbook/.claude/skills/device-platform/SKILL.md +407 -0
  16. wxcli/_playbook/.claude/skills/manage-call-settings/SKILL.md +757 -0
  17. wxcli/_playbook/.claude/skills/manage-devices/SKILL.md +790 -0
  18. wxcli/_playbook/.claude/skills/manage-identity/SKILL.md +638 -0
  19. wxcli/_playbook/.claude/skills/manage-licensing/SKILL.md +528 -0
  20. wxcli/_playbook/.claude/skills/manage-meetings/SKILL.md +708 -0
  21. wxcli/_playbook/.claude/skills/messaging-bots/SKILL.md +365 -0
  22. wxcli/_playbook/.claude/skills/messaging-spaces/SKILL.md +520 -0
  23. wxcli/_playbook/.claude/skills/org-health/SKILL.md +159 -0
  24. wxcli/_playbook/.claude/skills/provision-calling/SKILL.md +421 -0
  25. wxcli/_playbook/.claude/skills/query-live/SKILL.md +183 -0
  26. wxcli/_playbook/.claude/skills/query-live/domains/call-flow-trace.md +223 -0
  27. wxcli/_playbook/.claude/skills/query-live/domains/call-history.md +44 -0
  28. wxcli/_playbook/.claude/skills/query-live/domains/features.md +169 -0
  29. wxcli/_playbook/.claude/skills/query-live/domains/numbers.md +93 -0
  30. wxcli/_playbook/.claude/skills/query-live/domains/people-and-settings.md +146 -0
  31. wxcli/_playbook/.claude/skills/query-live/domains/routing.md +124 -0
  32. wxcli/_playbook/.claude/skills/reporting/SKILL.md +501 -0
  33. wxcli/_playbook/.claude/skills/reporting/references/cdr-recipes.md +1628 -0
  34. wxcli/_playbook/.claude/skills/reporting-cc/SKILL.md +251 -0
  35. wxcli/_playbook/.claude/skills/reporting-meetings/SKILL.md +290 -0
  36. wxcli/_playbook/.claude/skills/teardown/SKILL.md +214 -0
  37. wxcli/_playbook/.claude/skills/video-mesh/SKILL.md +379 -0
  38. wxcli/_playbook/.claude/skills/wxc-calling-debug/SKILL.md +475 -0
  39. wxcli/_playbook/CLAUDE.md +373 -0
  40. wxcli/_playbook/docs/reference/CLAUDE.md +25 -0
  41. wxcli/_playbook/docs/reference/admin-apps-data.md +643 -0
  42. wxcli/_playbook/docs/reference/admin-audit-security.md +322 -0
  43. wxcli/_playbook/docs/reference/admin-hybrid.md +383 -0
  44. wxcli/_playbook/docs/reference/admin-identity-scim.md +1007 -0
  45. wxcli/_playbook/docs/reference/admin-licensing.md +414 -0
  46. wxcli/_playbook/docs/reference/admin-org-management.md +586 -0
  47. wxcli/_playbook/docs/reference/admin-partner.md +357 -0
  48. wxcli/_playbook/docs/reference/archive/wxc-sdk-patterns.md +1229 -0
  49. wxcli/_playbook/docs/reference/archive/wxcadm-advanced.md +835 -0
  50. wxcli/_playbook/docs/reference/archive/wxcadm-core.md +743 -0
  51. wxcli/_playbook/docs/reference/archive/wxcadm-devices-workspaces.md +1305 -0
  52. wxcli/_playbook/docs/reference/archive/wxcadm-features.md +849 -0
  53. wxcli/_playbook/docs/reference/archive/wxcadm-locations.md +938 -0
  54. wxcli/_playbook/docs/reference/archive/wxcadm-person.md +1255 -0
  55. wxcli/_playbook/docs/reference/archive/wxcadm-routing.md +1262 -0
  56. wxcli/_playbook/docs/reference/authentication.md +965 -0
  57. wxcli/_playbook/docs/reference/call-control.md +1205 -0
  58. wxcli/_playbook/docs/reference/call-features-additional.md +2541 -0
  59. wxcli/_playbook/docs/reference/call-features-major.md +1320 -0
  60. wxcli/_playbook/docs/reference/call-routing.md +2304 -0
  61. wxcli/_playbook/docs/reference/contact-center-analytics.md +1049 -0
  62. wxcli/_playbook/docs/reference/contact-center-core.md +1614 -0
  63. wxcli/_playbook/docs/reference/contact-center-journey.md +457 -0
  64. wxcli/_playbook/docs/reference/contact-center-routing.md +1138 -0
  65. wxcli/_playbook/docs/reference/devices-core.md +1869 -0
  66. wxcli/_playbook/docs/reference/devices-dect.md +1150 -0
  67. wxcli/_playbook/docs/reference/devices-platform.md +467 -0
  68. wxcli/_playbook/docs/reference/devices-workspaces.md +1426 -0
  69. wxcli/_playbook/docs/reference/emergency-services.md +1122 -0
  70. wxcli/_playbook/docs/reference/location-calling-core.md +2037 -0
  71. wxcli/_playbook/docs/reference/location-calling-media.md +1334 -0
  72. wxcli/_playbook/docs/reference/location-recording-advanced.md +1482 -0
  73. wxcli/_playbook/docs/reference/meetings-content.md +473 -0
  74. wxcli/_playbook/docs/reference/meetings-core.md +903 -0
  75. wxcli/_playbook/docs/reference/meetings-infrastructure.md +899 -0
  76. wxcli/_playbook/docs/reference/meetings-settings.md +850 -0
  77. wxcli/_playbook/docs/reference/messaging-bots.md +1091 -0
  78. wxcli/_playbook/docs/reference/messaging-spaces.md +695 -0
  79. wxcli/_playbook/docs/reference/person-call-settings-behavior.md +1639 -0
  80. wxcli/_playbook/docs/reference/person-call-settings-handling.md +1394 -0
  81. wxcli/_playbook/docs/reference/person-call-settings-media.md +1898 -0
  82. wxcli/_playbook/docs/reference/person-call-settings-permissions.md +1329 -0
  83. wxcli/_playbook/docs/reference/provisioning.md +1366 -0
  84. wxcli/_playbook/docs/reference/reporting-analytics.md +1318 -0
  85. wxcli/_playbook/docs/reference/self-service-call-settings.md +1020 -0
  86. wxcli/_playbook/docs/reference/virtual-lines.md +1132 -0
  87. wxcli/_playbook/docs/reference/webhooks-events.md +1444 -0
  88. wxcli/_playbook/docs/reference/wxcadm-xsi-realtime.md +953 -0
  89. wxcli/_version.py +24 -0
  90. wxcli/auth.py +137 -0
  91. wxcli/commands/__init__.py +0 -0
  92. wxcli/commands/_registry.py +181 -0
  93. wxcli/commands/activation_email.py +103 -0
  94. wxcli/commands/admin_recordings.py +585 -0
  95. wxcli/commands/analytics.py +105 -0
  96. wxcli/commands/announcement_playlists.py +219 -0
  97. wxcli/commands/announcements.py +519 -0
  98. wxcli/commands/archive_users.py +36 -0
  99. wxcli/commands/attachment_actions.py +60 -0
  100. wxcli/commands/audit_events.py +83 -0
  101. wxcli/commands/authorizations.py +113 -0
  102. wxcli/commands/auto_attendant.py +642 -0
  103. wxcli/commands/broadworks_billing_reports.py +126 -0
  104. wxcli/commands/broadworks_enterprises.py +160 -0
  105. wxcli/commands/broadworks_subscribers.py +319 -0
  106. wxcli/commands/broadworks_workspaces.py +108 -0
  107. wxcli/commands/call_controls.py +1304 -0
  108. wxcli/commands/call_park.py +494 -0
  109. wxcli/commands/call_pickup.py +229 -0
  110. wxcli/commands/call_queue.py +1878 -0
  111. wxcli/commands/call_recording.py +707 -0
  112. wxcli/commands/call_routing.py +1610 -0
  113. wxcli/commands/call_settings_for_me_phase_5.py +212 -0
  114. wxcli/commands/caller_reputation.py +169 -0
  115. wxcli/commands/calling_service.py +305 -0
  116. wxcli/commands/cc_address_book.py +770 -0
  117. wxcli/commands/cc_agent_greetings.py +242 -0
  118. wxcli/commands/cc_agent_summaries.py +39 -0
  119. wxcli/commands/cc_agent_wellbeing.py +216 -0
  120. wxcli/commands/cc_agents.py +425 -0
  121. wxcli/commands/cc_ai_assistant.py +55 -0
  122. wxcli/commands/cc_ai_feature.py +294 -0
  123. wxcli/commands/cc_audio_files.py +149 -0
  124. wxcli/commands/cc_auto_csat.py +311 -0
  125. wxcli/commands/cc_aux_code.py +458 -0
  126. wxcli/commands/cc_business_hour.py +353 -0
  127. wxcli/commands/cc_call_monitoring.py +228 -0
  128. wxcli/commands/cc_callbacks.py +194 -0
  129. wxcli/commands/cc_campaign.py +212 -0
  130. wxcli/commands/cc_captures.py +39 -0
  131. wxcli/commands/cc_contact_list.py +169 -0
  132. wxcli/commands/cc_contact_number.py +295 -0
  133. wxcli/commands/cc_data_sources.py +235 -0
  134. wxcli/commands/cc_desktop_layout.py +380 -0
  135. wxcli/commands/cc_desktop_profile.py +322 -0
  136. wxcli/commands/cc_dial_number.py +511 -0
  137. wxcli/commands/cc_dial_plan.py +347 -0
  138. wxcli/commands/cc_dnc.py +101 -0
  139. wxcli/commands/cc_entry_point.py +545 -0
  140. wxcli/commands/cc_ewt.py +51 -0
  141. wxcli/commands/cc_flow.py +477 -0
  142. wxcli/commands/cc_global_vars.py +430 -0
  143. wxcli/commands/cc_holiday_list.py +335 -0
  144. wxcli/commands/cc_journey.py +1261 -0
  145. wxcli/commands/cc_legacy_flows.py +49 -0
  146. wxcli/commands/cc_multimedia_profile.py +436 -0
  147. wxcli/commands/cc_notification.py +51 -0
  148. wxcli/commands/cc_outdial_ani.py +604 -0
  149. wxcli/commands/cc_overrides.py +341 -0
  150. wxcli/commands/cc_queue.py +1083 -0
  151. wxcli/commands/cc_queue_stats.py +55 -0
  152. wxcli/commands/cc_realtime.py +51 -0
  153. wxcli/commands/cc_resource_collection.py +312 -0
  154. wxcli/commands/cc_search.py +46 -0
  155. wxcli/commands/cc_site.py +400 -0
  156. wxcli/commands/cc_skill.py +350 -0
  157. wxcli/commands/cc_skill_profile.py +289 -0
  158. wxcli/commands/cc_subscriptions.py +426 -0
  159. wxcli/commands/cc_summaries.py +130 -0
  160. wxcli/commands/cc_tasks.py +844 -0
  161. wxcli/commands/cc_team.py +325 -0
  162. wxcli/commands/cc_user_profiles.py +349 -0
  163. wxcli/commands/cc_users.py +701 -0
  164. wxcli/commands/cc_work_types.py +363 -0
  165. wxcli/commands/cdr.py +89 -0
  166. wxcli/commands/classifications.py +38 -0
  167. wxcli/commands/cleanup.py +1427 -0
  168. wxcli/commands/client_settings.py +72 -0
  169. wxcli/commands/conference.py +328 -0
  170. wxcli/commands/configure.py +72 -0
  171. wxcli/commands/converged_recordings.py +347 -0
  172. wxcli/commands/converged_recordings_export.py +310 -0
  173. wxcli/commands/cq_playlists.py +42 -0
  174. wxcli/commands/cucm.py +2794 -0
  175. wxcli/commands/cucm_config.py +129 -0
  176. wxcli/commands/customer_assist.py +373 -0
  177. wxcli/commands/data_sources.py +227 -0
  178. wxcli/commands/dect_devices.py +787 -0
  179. wxcli/commands/device_configurations.py +77 -0
  180. wxcli/commands/device_dynamic_settings.py +320 -0
  181. wxcli/commands/device_settings.py +1489 -0
  182. wxcli/commands/devices.py +280 -0
  183. wxcli/commands/domains.py +142 -0
  184. wxcli/commands/ecm.py +178 -0
  185. wxcli/commands/emergency_services.py +827 -0
  186. wxcli/commands/events.py +83 -0
  187. wxcli/commands/external_voicemail.py +52 -0
  188. wxcli/commands/groups.py +206 -0
  189. wxcli/commands/guest_management.py +76 -0
  190. wxcli/commands/hds.py +340 -0
  191. wxcli/commands/hot_desk.py +67 -0
  192. wxcli/commands/hot_desking_members.py +118 -0
  193. wxcli/commands/hot_desking_portal.py +124 -0
  194. wxcli/commands/hunt_group.py +577 -0
  195. wxcli/commands/hybrid_clusters.py +71 -0
  196. wxcli/commands/hybrid_connectors.py +67 -0
  197. wxcli/commands/identity_org.py +159 -0
  198. wxcli/commands/init_playbook.py +155 -0
  199. wxcli/commands/licenses.py +106 -0
  200. wxcli/commands/live_monitoring.py +40 -0
  201. wxcli/commands/location_call_handling.py +577 -0
  202. wxcli/commands/location_schedules.py +341 -0
  203. wxcli/commands/location_settings.py +1297 -0
  204. wxcli/commands/location_voicemail.py +494 -0
  205. wxcli/commands/locations.py +336 -0
  206. wxcli/commands/meeting_captions.py +110 -0
  207. wxcli/commands/meeting_chats.py +63 -0
  208. wxcli/commands/meeting_invitees.py +234 -0
  209. wxcli/commands/meeting_messages.py +28 -0
  210. wxcli/commands/meeting_participants.py +278 -0
  211. wxcli/commands/meeting_polls.py +111 -0
  212. wxcli/commands/meeting_preferences.py +515 -0
  213. wxcli/commands/meeting_qa.py +78 -0
  214. wxcli/commands/meeting_qualities.py +43 -0
  215. wxcli/commands/meeting_reports.py +101 -0
  216. wxcli/commands/meeting_session_types.py +105 -0
  217. wxcli/commands/meeting_site.py +57 -0
  218. wxcli/commands/meeting_slido.py +44 -0
  219. wxcli/commands/meeting_summaries.py +92 -0
  220. wxcli/commands/meeting_tracking_codes.py +233 -0
  221. wxcli/commands/meeting_transcripts.py +230 -0
  222. wxcli/commands/meetings.py +1997 -0
  223. wxcli/commands/memberships.py +164 -0
  224. wxcli/commands/messages.py +213 -0
  225. wxcli/commands/mode_management.py +244 -0
  226. wxcli/commands/my_call_settings.py +3577 -0
  227. wxcli/commands/numbers.py +405 -0
  228. wxcli/commands/operating_modes.py +410 -0
  229. wxcli/commands/org_contacts.py +284 -0
  230. wxcli/commands/org_health_cli.py +55 -0
  231. wxcli/commands/org_settings.py +71 -0
  232. wxcli/commands/organizations.py +83 -0
  233. wxcli/commands/paging_group.py +257 -0
  234. wxcli/commands/partner_admins.py +141 -0
  235. wxcli/commands/partner_reports.py +188 -0
  236. wxcli/commands/partner_tags.py +216 -0
  237. wxcli/commands/people.py +279 -0
  238. wxcli/commands/person_call_settings.py +71 -0
  239. wxcli/commands/pstn.py +280 -0
  240. wxcli/commands/recording_report.py +154 -0
  241. wxcli/commands/report_templates.py +38 -0
  242. wxcli/commands/reports.py +144 -0
  243. wxcli/commands/resource_group_memberships.py +160 -0
  244. wxcli/commands/resource_groups.py +67 -0
  245. wxcli/commands/roles.py +63 -0
  246. wxcli/commands/room_tabs.py +160 -0
  247. wxcli/commands/rooms.py +225 -0
  248. wxcli/commands/scim_bulk.py +46 -0
  249. wxcli/commands/scim_groups.py +246 -0
  250. wxcli/commands/scim_schemas.py +82 -0
  251. wxcli/commands/scim_users.py +283 -0
  252. wxcli/commands/security_audit.py +54 -0
  253. wxcli/commands/service_apps.py +51 -0
  254. wxcli/commands/single_number_reach.py +209 -0
  255. wxcli/commands/team_memberships.py +155 -0
  256. wxcli/commands/teams.py +153 -0
  257. wxcli/commands/ucm_profile.py +42 -0
  258. wxcli/commands/update.py +219 -0
  259. wxcli/commands/user_settings.py +5116 -0
  260. wxcli/commands/video_mesh.py +1090 -0
  261. wxcli/commands/virtual_extensions.py +502 -0
  262. wxcli/commands/virtual_line_settings.py +2072 -0
  263. wxcli/commands/webhooks.py +176 -0
  264. wxcli/commands/wholesale_billing_reports.py +147 -0
  265. wxcli/commands/wholesale_provisioning.py +523 -0
  266. wxcli/commands/workspace_locations.py +339 -0
  267. wxcli/commands/workspace_metrics.py +103 -0
  268. wxcli/commands/workspace_personalization.py +70 -0
  269. wxcli/commands/workspace_settings.py +3347 -0
  270. wxcli/commands/workspaces.py +279 -0
  271. wxcli/commands/xapi.py +114 -0
  272. wxcli/config.py +139 -0
  273. wxcli/errors.py +77 -0
  274. wxcli/main.py +210 -0
  275. wxcli/migration/CLAUDE.md +106 -0
  276. wxcli/migration/__init__.py +1 -0
  277. wxcli/migration/advisory/CLAUDE.md +196 -0
  278. wxcli/migration/advisory/__init__.py +46 -0
  279. wxcli/migration/advisory/advisor.py +107 -0
  280. wxcli/migration/advisory/advisory_patterns.py +2552 -0
  281. wxcli/migration/advisory/recommendation_rules.py +835 -0
  282. wxcli/migration/cucm/CLAUDE.md +74 -0
  283. wxcli/migration/cucm/__init__.py +5 -0
  284. wxcli/migration/cucm/connection.py +265 -0
  285. wxcli/migration/cucm/discovery.py +225 -0
  286. wxcli/migration/cucm/extractors/__init__.py +1 -0
  287. wxcli/migration/cucm/extractors/announcements.py +92 -0
  288. wxcli/migration/cucm/extractors/base.py +90 -0
  289. wxcli/migration/cucm/extractors/device_profiles.py +98 -0
  290. wxcli/migration/cucm/extractors/devices.py +193 -0
  291. wxcli/migration/cucm/extractors/e911.py +103 -0
  292. wxcli/migration/cucm/extractors/features.py +309 -0
  293. wxcli/migration/cucm/extractors/helpers.py +73 -0
  294. wxcli/migration/cucm/extractors/informational.py +295 -0
  295. wxcli/migration/cucm/extractors/locations.py +221 -0
  296. wxcli/migration/cucm/extractors/moh.py +100 -0
  297. wxcli/migration/cucm/extractors/remote_destinations.py +96 -0
  298. wxcli/migration/cucm/extractors/routing.py +493 -0
  299. wxcli/migration/cucm/extractors/shared_lines.py +146 -0
  300. wxcli/migration/cucm/extractors/templates.py +229 -0
  301. wxcli/migration/cucm/extractors/tier4.py +173 -0
  302. wxcli/migration/cucm/extractors/users.py +240 -0
  303. wxcli/migration/cucm/extractors/voicemail.py +243 -0
  304. wxcli/migration/cucm/extractors/workspaces.py +88 -0
  305. wxcli/migration/cucm/unity_connection.py +304 -0
  306. wxcli/migration/execute/CLAUDE.md +372 -0
  307. wxcli/migration/execute/__init__.py +303 -0
  308. wxcli/migration/execute/batch.py +277 -0
  309. wxcli/migration/execute/dependency.py +483 -0
  310. wxcli/migration/execute/engine.py +964 -0
  311. wxcli/migration/execute/handlers.py +2232 -0
  312. wxcli/migration/execute/planner.py +1939 -0
  313. wxcli/migration/execute/runtime.py +571 -0
  314. wxcli/migration/export/CLAUDE.md +49 -0
  315. wxcli/migration/export/__init__.py +8 -0
  316. wxcli/migration/export/csv_export.py +126 -0
  317. wxcli/migration/export/deployment_plan.py +479 -0
  318. wxcli/migration/export/json_export.py +60 -0
  319. wxcli/migration/models.py +941 -0
  320. wxcli/migration/phone_models.py +50 -0
  321. wxcli/migration/preflight/CLAUDE.md +52 -0
  322. wxcli/migration/preflight/__init__.py +89 -0
  323. wxcli/migration/preflight/checks.py +911 -0
  324. wxcli/migration/preflight/runner.py +293 -0
  325. wxcli/migration/rate_limiter.py +144 -0
  326. wxcli/migration/report/CLAUDE.md +245 -0
  327. wxcli/migration/report/__init__.py +7 -0
  328. wxcli/migration/report/appendix.py +2437 -0
  329. wxcli/migration/report/assembler.py +183 -0
  330. wxcli/migration/report/charts.py +263 -0
  331. wxcli/migration/report/executive.py +776 -0
  332. wxcli/migration/report/explainer.py +704 -0
  333. wxcli/migration/report/helpers.py +74 -0
  334. wxcli/migration/report/ingest.py +152 -0
  335. wxcli/migration/report/notice_templates.py +192 -0
  336. wxcli/migration/report/score.py +375 -0
  337. wxcli/migration/report/styles.py +1052 -0
  338. wxcli/migration/report/user_diff.py +1052 -0
  339. wxcli/migration/report/user_notice.py +425 -0
  340. wxcli/migration/state.py +139 -0
  341. wxcli/migration/store.py +860 -0
  342. wxcli/migration/transform/CLAUDE.md +107 -0
  343. wxcli/migration/transform/__init__.py +12 -0
  344. wxcli/migration/transform/analysis_pipeline.py +453 -0
  345. wxcli/migration/transform/analyzers/__init__.py +142 -0
  346. wxcli/migration/transform/analyzers/css_permission.py +189 -0
  347. wxcli/migration/transform/analyzers/css_routing.py +335 -0
  348. wxcli/migration/transform/analyzers/device_compatibility.py +122 -0
  349. wxcli/migration/transform/analyzers/dn_ambiguity.py +131 -0
  350. wxcli/migration/transform/analyzers/duplicate_user.py +221 -0
  351. wxcli/migration/transform/analyzers/extension_conflict.py +173 -0
  352. wxcli/migration/transform/analyzers/feature_approximation.py +227 -0
  353. wxcli/migration/transform/analyzers/layout_overflow.py +174 -0
  354. wxcli/migration/transform/analyzers/location_ambiguity.py +184 -0
  355. wxcli/migration/transform/analyzers/missing_data.py +300 -0
  356. wxcli/migration/transform/analyzers/selective_call_handling.py +394 -0
  357. wxcli/migration/transform/analyzers/shared_line.py +170 -0
  358. wxcli/migration/transform/analyzers/voicemail_compatibility.py +267 -0
  359. wxcli/migration/transform/analyzers/workspace_license.py +212 -0
  360. wxcli/migration/transform/cross_reference.py +1136 -0
  361. wxcli/migration/transform/cucm_pattern.py +246 -0
  362. wxcli/migration/transform/decisions.py +301 -0
  363. wxcli/migration/transform/e164.py +145 -0
  364. wxcli/migration/transform/engine.py +256 -0
  365. wxcli/migration/transform/mappers/CLAUDE.md +123 -0
  366. wxcli/migration/transform/mappers/__init__.py +76 -0
  367. wxcli/migration/transform/mappers/announcement_mapper.py +122 -0
  368. wxcli/migration/transform/mappers/base.py +190 -0
  369. wxcli/migration/transform/mappers/button_template_mapper.py +186 -0
  370. wxcli/migration/transform/mappers/call_forwarding_mapper.py +228 -0
  371. wxcli/migration/transform/mappers/call_settings_mapper.py +144 -0
  372. wxcli/migration/transform/mappers/css_mapper.py +868 -0
  373. wxcli/migration/transform/mappers/dect_mapper.py +367 -0
  374. wxcli/migration/transform/mappers/device_layout_mapper.py +289 -0
  375. wxcli/migration/transform/mappers/device_mapper.py +227 -0
  376. wxcli/migration/transform/mappers/device_profile_mapper.py +190 -0
  377. wxcli/migration/transform/mappers/device_settings_mapper.py +464 -0
  378. wxcli/migration/transform/mappers/e911_mapper.py +129 -0
  379. wxcli/migration/transform/mappers/ecbn_mapper.py +263 -0
  380. wxcli/migration/transform/mappers/executive_assistant_mapper.py +156 -0
  381. wxcli/migration/transform/mappers/feature_mapper.py +1179 -0
  382. wxcli/migration/transform/mappers/line_mapper.py +310 -0
  383. wxcli/migration/transform/mappers/location_mapper.py +235 -0
  384. wxcli/migration/transform/mappers/moh_mapper.py +114 -0
  385. wxcli/migration/transform/mappers/monitoring_mapper.py +221 -0
  386. wxcli/migration/transform/mappers/receptionist_mapper.py +245 -0
  387. wxcli/migration/transform/mappers/routing_mapper.py +821 -0
  388. wxcli/migration/transform/mappers/snr_mapper.py +201 -0
  389. wxcli/migration/transform/mappers/softkey_mapper.py +205 -0
  390. wxcli/migration/transform/mappers/user_mapper.py +264 -0
  391. wxcli/migration/transform/mappers/voicemail_group_mapper.py +286 -0
  392. wxcli/migration/transform/mappers/voicemail_mapper.py +514 -0
  393. wxcli/migration/transform/mappers/workspace_mapper.py +464 -0
  394. wxcli/migration/transform/normalizers.py +2008 -0
  395. wxcli/migration/transform/pattern_converter.py +106 -0
  396. wxcli/migration/transform/pipeline.py +271 -0
  397. wxcli/migration/transform/rules.py +255 -0
  398. wxcli/org_health/CLAUDE.md +63 -0
  399. wxcli/org_health/__init__.py +7 -0
  400. wxcli/org_health/analyze.py +93 -0
  401. wxcli/org_health/checks.py +458 -0
  402. wxcli/org_health/collector.py +62 -0
  403. wxcli/org_health/models.py +78 -0
  404. wxcli/org_health/report.py +300 -0
  405. wxcli/output.py +101 -0
  406. wxcli-1.2.0.dist-info/METADATA +505 -0
  407. wxcli-1.2.0.dist-info/RECORD +413 -0
  408. wxcli-1.2.0.dist-info/WHEEL +5 -0
  409. wxcli-1.2.0.dist-info/entry_points.txt +2 -0
  410. wxcli-1.2.0.dist-info/licenses/LICENSE +191 -0
  411. wxcli-1.2.0.dist-info/scm_file_list.json +751 -0
  412. wxcli-1.2.0.dist-info/scm_version.json +8 -0
  413. wxcli-1.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1997 @@
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
+
7
+
8
+ app = typer.Typer(help="Manage Webex Meetings meetings.")
9
+
10
+
11
+ @app.command("show")
12
+ def show(
13
+ meeting_id: str = typer.Argument(help="meetingId"),
14
+ current: str = typer.Option(None, "--current", help="Whether or not to retrieve only the current scheduled meetin"),
15
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
16
+ debug: bool = typer.Option(False, "--debug"),
17
+ ):
18
+ """Get a Meeting By an Admin."""
19
+ api = get_api(debug=debug)
20
+ url = f"https://webexapis.com/v1/admin/meetings/{meeting_id}"
21
+ params = {}
22
+ if current is not None:
23
+ params["current"] = current
24
+ try:
25
+ result = api.session.rest_get(url, params=params)
26
+ except WebexError as e:
27
+ handle_rest_error(e)
28
+ if output == "json":
29
+ print_json(result)
30
+ else:
31
+ if isinstance(result, dict):
32
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
33
+ elif isinstance(result, list):
34
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
35
+ else:
36
+ print_json(result)
37
+
38
+
39
+
40
+ @app.command("list")
41
+ def cmd_list(
42
+ meeting_number: str = typer.Option(None, "--meeting-number", help="Meeting number for the meeting objects being requested. `mee"),
43
+ web_link: str = typer.Option(None, "--web-link", help="URL encoded link to information page for the meeting objects"),
44
+ current: str = typer.Option(None, "--current", help="Flag identifying to retrieve the current scheduled meeting o"),
45
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
46
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
47
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
48
+ debug: bool = typer.Option(False, "--debug"),
49
+ ):
50
+ """List Meetings By an Admin."""
51
+ api = get_api(debug=debug)
52
+ url = f"https://webexapis.com/v1/admin/meetings"
53
+ params = {}
54
+ if meeting_number is not None:
55
+ params["meetingNumber"] = meeting_number
56
+ if web_link is not None:
57
+ params["webLink"] = web_link
58
+ if current is not None:
59
+ params["current"] = current
60
+ if limit > 0:
61
+ params["max"] = limit
62
+ if offset > 0:
63
+ params["start"] = offset
64
+ try:
65
+ if limit > 0:
66
+ result = api.session.rest_get(url, params=params)
67
+ result = result or {}
68
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
69
+ else:
70
+ if "max" not in params:
71
+ params["max"] = 1000
72
+ items = list(api.session.follow_pagination(url=url, params=params, item_key="items"))
73
+ except WebexError as e:
74
+ handle_rest_error(e)
75
+ if output == "json":
76
+ print_json(items)
77
+ else:
78
+ print_table(items, columns=[('ID', 'id'), ('Title', 'title'), ('Start', 'start'), ('End', 'end'), ('State', 'state')], limit=limit)
79
+
80
+
81
+
82
+ @app.command("list-meetings")
83
+ def list_meetings(
84
+ meeting_number: str = typer.Option(None, "--meeting-number", help="Meeting number for the meeting objects being requested. `mee"),
85
+ web_link: str = typer.Option(None, "--web-link", help="URL encoded link to information page for the meeting objects"),
86
+ room_id: str = typer.Option(None, "--room-id", help="Associated Webex space ID for the meeting objects being requ"),
87
+ meeting_series_id: str = typer.Option(None, "--meeting-series-id", help="Unique identifier for the meeting series. The meeting ID of"),
88
+ from_param: str = typer.Option(None, "--from", help="Start date and time (inclusive) for the range for which meet"),
89
+ to: str = typer.Option(None, "--to", help="End date and time (exclusive) for the range for which meetin"),
90
+ meeting_type: str = typer.Option(None, "--meeting-type", help="Choices: meetingSeries, scheduledMeeting, meeting"),
91
+ state: str = typer.Option(None, "--state", help="Choices: active, scheduled, ready, lobby, inProgress, ended, missed, expired"),
92
+ scheduled_type: str = typer.Option(None, "--scheduled-type", help="Choices: meeting, webinar, personalRoomMeeting"),
93
+ is_modified: str = typer.Option(None, "--is-modified", help="Flag identifying whether a meeting has been modified. Only a"),
94
+ has_chat: str = typer.Option(None, "--has-chat", help="Flag identifying whether a meeting has a chat log. Only appl"),
95
+ has_recording: str = typer.Option(None, "--has-recording", help="Flag identifying meetings which have been recorded. Only app"),
96
+ has_transcription: str = typer.Option(None, "--has-transcription", help="Flag identifying meetings with transcripts. Only applies to"),
97
+ has_summary: str = typer.Option(None, "--has-summary", help="Flag identifying meetings with summaries. Only applies to en"),
98
+ has_closed_caption: str = typer.Option(None, "--has-closed-caption", help="Flag identifying meetings with closed captions. Only applies"),
99
+ has_polls: str = typer.Option(None, "--has-polls", help="Flag identifying meetings with polls. Only applies to ended"),
100
+ has_qa: str = typer.Option(None, "--has-qa", help="Flag identifying meetings with Q&A. Only applies to ended me"),
101
+ has_slido: str = typer.Option(None, "--has-slido", help="Flag identifying meetings with Slido interactions. Only appl"),
102
+ current: str = typer.Option(None, "--current", help="Flag identifying to retrieve the current scheduled meeting o"),
103
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This parameter is only u"),
104
+ site_url: str = typer.Option(None, "--site-url", help="URL of the Webex site which the API lists meetings from. If"),
105
+ integration_tag: str = typer.Option(None, "--integration-tag", help="External key created by an integration application. This par"),
106
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
107
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
108
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
109
+ debug: bool = typer.Option(False, "--debug"),
110
+ ):
111
+ """List Meetings."""
112
+ api = get_api(debug=debug)
113
+ url = f"https://webexapis.com/v1/meetings"
114
+ params = {}
115
+ if meeting_number is not None:
116
+ params["meetingNumber"] = meeting_number
117
+ if web_link is not None:
118
+ params["webLink"] = web_link
119
+ if room_id is not None:
120
+ params["roomId"] = room_id
121
+ if meeting_series_id is not None:
122
+ params["meetingSeriesId"] = meeting_series_id
123
+ if from_param is not None:
124
+ params["from"] = from_param
125
+ if to is not None:
126
+ params["to"] = to
127
+ if meeting_type is not None:
128
+ params["meetingType"] = meeting_type
129
+ if state is not None:
130
+ params["state"] = state
131
+ if scheduled_type is not None:
132
+ params["scheduledType"] = scheduled_type
133
+ if is_modified is not None:
134
+ params["isModified"] = is_modified
135
+ if has_chat is not None:
136
+ params["hasChat"] = has_chat
137
+ if has_recording is not None:
138
+ params["hasRecording"] = has_recording
139
+ if has_transcription is not None:
140
+ params["hasTranscription"] = has_transcription
141
+ if has_summary is not None:
142
+ params["hasSummary"] = has_summary
143
+ if has_closed_caption is not None:
144
+ params["hasClosedCaption"] = has_closed_caption
145
+ if has_polls is not None:
146
+ params["hasPolls"] = has_polls
147
+ if has_qa is not None:
148
+ params["hasQA"] = has_qa
149
+ if has_slido is not None:
150
+ params["hasSlido"] = has_slido
151
+ if current is not None:
152
+ params["current"] = current
153
+ if host_email is not None:
154
+ params["hostEmail"] = host_email
155
+ if site_url is not None:
156
+ params["siteUrl"] = site_url
157
+ if integration_tag is not None:
158
+ params["integrationTag"] = integration_tag
159
+ if limit > 0:
160
+ params["max"] = limit
161
+ if offset > 0:
162
+ params["start"] = offset
163
+ try:
164
+ if limit > 0:
165
+ result = api.session.rest_get(url, params=params)
166
+ result = result or {}
167
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
168
+ else:
169
+ items = list(api.session.follow_pagination(url=url, params=params, item_key="items"))
170
+ except WebexError as e:
171
+ handle_rest_error(e)
172
+ if output == "json":
173
+ print_json(items)
174
+ else:
175
+ print_table(items, columns=[('ID', 'id'), ('Title', 'title'), ('Start', 'start'), ('End', 'end'), ('State', 'state')], limit=limit)
176
+
177
+
178
+
179
+ @app.command("create")
180
+ def create(
181
+ adhoc: bool = typer.Option(None, "--adhoc/--no-adhoc", help="Whether or not to create an ad-hoc meeting for the room spec"),
182
+ room_id: str = typer.Option(None, "--room-id", help="Unique identifier for the Webex space which the meeting is t"),
183
+ template_id: str = typer.Option(None, "--template-id", help="Unique identifier for meeting template. Please note that `st"),
184
+ title: str = typer.Option(None, "--title", help="(required) Meeting title. The title can be a maximum of 128 characters"),
185
+ agenda: str = typer.Option(None, "--agenda", help="Meeting agenda. The agenda can be a maximum of 1300 characte"),
186
+ password: str = typer.Option(None, "--password", help="Meeting password. Must conform to the site's password comple"),
187
+ start: str = typer.Option(None, "--start", help="(required) Date and time for the start of meeting in any [ISO 8601](htt"),
188
+ end: str = typer.Option(None, "--end", help="(required) Date and time for the end of meeting in any [ISO 8601](https"),
189
+ timezone: str = typer.Option(None, "--timezone", help="[Time zone](https://en.wikipedia.org/wiki/List_of_tz_databas"),
190
+ recurrence: str = typer.Option(None, "--recurrence", help="Meeting series recurrence rule (conforming with [RFC 2445](h"),
191
+ enabled_auto_record_meeting: bool = typer.Option(None, "--enabled-auto-record-meeting/--no-enabled-auto-record-meeting", help="Whether or not meeting is recorded automatically."),
192
+ allow_any_user_to_be_co_host: bool = typer.Option(None, "--allow-any-user-to-be-co-host/--no-allow-any-user-to-be-co-host", help="Whether or not to allow any attendee with a host account on"),
193
+ enabled_join_before_host: bool = typer.Option(None, "--enabled-join-before-host/--no-enabled-join-before-host", help="Whether or not to allow any attendee to join the meeting bef"),
194
+ enable_connect_audio_before_host: bool = typer.Option(None, "--enable-connect-audio-before-host/--no-enable-connect-audio-before-host", help="Whether or not to allow any attendee to connect audio in the"),
195
+ join_before_host_minutes: str = typer.Option(None, "--join-before-host-minutes", help="Number of minutes an attendee can join the meeting before th"),
196
+ exclude_password: bool = typer.Option(None, "--exclude-password/--no-exclude-password", help="Whether or not to exclude the meeting password from the emai"),
197
+ public_meeting: bool = typer.Option(None, "--public-meeting/--no-public-meeting", help="Whether or not to allow the meeting to be listed on the publ"),
198
+ reminder_time: str = typer.Option(None, "--reminder-time", help="The number of minutes before the meeting begins, that an ema"),
199
+ unlocked_meeting_join_security: str = typer.Option(None, "--unlocked-meeting-join-security", help="Choices: allowJoin, allowJoinWithLobby, blockFromJoin"),
200
+ session_type_id: str = typer.Option(None, "--session-type-id", help="Unique identifier for a meeting session type for the user. T"),
201
+ scheduled_type: str = typer.Option(None, "--scheduled-type", help="Choices: meeting, webinar, personalRoomMeeting"),
202
+ enabled_webcast_view: bool = typer.Option(None, "--enabled-webcast-view/--no-enabled-webcast-view", help="Whether or not webcast view is enabled. This parameter is ig"),
203
+ panelist_password: str = typer.Option(None, "--panelist-password", help="Password for panelists of a webinar meeting. Must conform to"),
204
+ enable_automatic_lock: bool = typer.Option(None, "--enable-automatic-lock/--no-enable-automatic-lock", help="Whether or not to automatically lock the meeting after it st"),
205
+ automatic_lock_minutes: str = typer.Option(None, "--automatic-lock-minutes", help="The number of minutes after the meeting begins, for automati"),
206
+ allow_first_user_to_be_co_host: bool = typer.Option(None, "--allow-first-user-to-be-co-host/--no-allow-first-user-to-be-co-host", help="Whether or not to allow the first attendee of the meeting wi"),
207
+ allow_authenticated_devices: bool = typer.Option(None, "--allow-authenticated-devices/--no-allow-authenticated-devices", help="Whether or not to allow authenticated video devices in the m"),
208
+ send_email: bool = typer.Option(None, "--send-email/--no-send-email", help="Whether or not to send emails to host and invitees. It is an"),
209
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This attribute should on"),
210
+ site_url: str = typer.Option(None, "--site-url", help="URL of the Webex site which the meeting is created on. If no"),
211
+ enabled_breakout_sessions: bool = typer.Option(None, "--enabled-breakout-sessions/--no-enabled-breakout-sessions", help="Whether or not breakout sessions are enabled."),
212
+ enabled_audio_watermark: bool = typer.Option(None, "--enabled-audio-watermark/--no-enabled-audio-watermark", help="Whether or not the audio watermark is enabled. If it's `true"),
213
+ enabled_visual_watermark: bool = typer.Option(None, "--enabled-visual-watermark/--no-enabled-visual-watermark", help="Whether or not the visual watermark is enabled. If it's `tru"),
214
+ visual_watermark_opacity: str = typer.Option(None, "--visual-watermark-opacity", help="Opacity level for the visual watermark. The value must be be"),
215
+ require_attendee_login: bool = typer.Option(None, "--require-attendee-login/--no-require-attendee-login", help="Require attendees to sign in before joining the webinar. Thi"),
216
+ restrict_to_invitees: bool = typer.Option(None, "--restrict-to-invitees/--no-restrict-to-invitees", help="Restrict webinar to invited attendees only. This option work"),
217
+ enabled_live_stream: bool = typer.Option(None, "--enabled-live-stream/--no-enabled-live-stream", help="Whether or not live streaming is enabled. If it's enabled, t"),
218
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
219
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
220
+ debug: bool = typer.Option(False, "--debug"),
221
+ ):
222
+ """Create a Meeting\n\nExample --json-body:\n '{"title":"...","start":"...","end":"...","adhoc":true,"roomId":"...","templateId":"...","agenda":"...","password":"..."}'."""
223
+ api = get_api(debug=debug)
224
+ url = f"https://webexapis.com/v1/meetings"
225
+ if json_body:
226
+ body = json.loads(json_body)
227
+ else:
228
+ body = {}
229
+ if adhoc is not None:
230
+ body["adhoc"] = adhoc
231
+ if room_id is not None:
232
+ body["roomId"] = room_id
233
+ if template_id is not None:
234
+ body["templateId"] = template_id
235
+ if title is not None:
236
+ body["title"] = title
237
+ if agenda is not None:
238
+ body["agenda"] = agenda
239
+ if password is not None:
240
+ body["password"] = password
241
+ if start is not None:
242
+ body["start"] = start
243
+ if end is not None:
244
+ body["end"] = end
245
+ if timezone is not None:
246
+ body["timezone"] = timezone
247
+ if recurrence is not None:
248
+ body["recurrence"] = recurrence
249
+ if enabled_auto_record_meeting is not None:
250
+ body["enabledAutoRecordMeeting"] = enabled_auto_record_meeting
251
+ if allow_any_user_to_be_co_host is not None:
252
+ body["allowAnyUserToBeCoHost"] = allow_any_user_to_be_co_host
253
+ if enabled_join_before_host is not None:
254
+ body["enabledJoinBeforeHost"] = enabled_join_before_host
255
+ if enable_connect_audio_before_host is not None:
256
+ body["enableConnectAudioBeforeHost"] = enable_connect_audio_before_host
257
+ if join_before_host_minutes is not None:
258
+ body["joinBeforeHostMinutes"] = join_before_host_minutes
259
+ if exclude_password is not None:
260
+ body["excludePassword"] = exclude_password
261
+ if public_meeting is not None:
262
+ body["publicMeeting"] = public_meeting
263
+ if reminder_time is not None:
264
+ body["reminderTime"] = reminder_time
265
+ if unlocked_meeting_join_security is not None:
266
+ body["unlockedMeetingJoinSecurity"] = unlocked_meeting_join_security
267
+ if session_type_id is not None:
268
+ body["sessionTypeId"] = session_type_id
269
+ if scheduled_type is not None:
270
+ body["scheduledType"] = scheduled_type
271
+ if enabled_webcast_view is not None:
272
+ body["enabledWebcastView"] = enabled_webcast_view
273
+ if panelist_password is not None:
274
+ body["panelistPassword"] = panelist_password
275
+ if enable_automatic_lock is not None:
276
+ body["enableAutomaticLock"] = enable_automatic_lock
277
+ if automatic_lock_minutes is not None:
278
+ body["automaticLockMinutes"] = automatic_lock_minutes
279
+ if allow_first_user_to_be_co_host is not None:
280
+ body["allowFirstUserToBeCoHost"] = allow_first_user_to_be_co_host
281
+ if allow_authenticated_devices is not None:
282
+ body["allowAuthenticatedDevices"] = allow_authenticated_devices
283
+ if send_email is not None:
284
+ body["sendEmail"] = send_email
285
+ if host_email is not None:
286
+ body["hostEmail"] = host_email
287
+ if site_url is not None:
288
+ body["siteUrl"] = site_url
289
+ if enabled_breakout_sessions is not None:
290
+ body["enabledBreakoutSessions"] = enabled_breakout_sessions
291
+ if enabled_audio_watermark is not None:
292
+ body["enabledAudioWatermark"] = enabled_audio_watermark
293
+ if enabled_visual_watermark is not None:
294
+ body["enabledVisualWatermark"] = enabled_visual_watermark
295
+ if visual_watermark_opacity is not None:
296
+ body["visualWatermarkOpacity"] = visual_watermark_opacity
297
+ if require_attendee_login is not None:
298
+ body["requireAttendeeLogin"] = require_attendee_login
299
+ if restrict_to_invitees is not None:
300
+ body["restrictToInvitees"] = restrict_to_invitees
301
+ if enabled_live_stream is not None:
302
+ body["enabledLiveStream"] = enabled_live_stream
303
+ _missing = [f for f in ['title', 'start', 'end'] if f not in body or body[f] is None]
304
+ if _missing:
305
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
306
+ raise typer.Exit(1)
307
+ try:
308
+ result = api.session.rest_post(url, json=body)
309
+ except WebexError as e:
310
+ handle_rest_error(e)
311
+ if output == "json":
312
+ print_json(result)
313
+ elif isinstance(result, dict) and "id" in result:
314
+ typer.echo(f"Created: {result['id']}")
315
+ elif not result or result == {}:
316
+ typer.echo("Created.")
317
+ else:
318
+ print_json(result)
319
+
320
+
321
+
322
+ @app.command("show-meetings")
323
+ def show_meetings(
324
+ meeting_id: str = typer.Argument(help="meetingId"),
325
+ current: str = typer.Option(None, "--current", help="Whether or not to retrieve only the current scheduled meetin"),
326
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This parameter is only u"),
327
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
328
+ debug: bool = typer.Option(False, "--debug"),
329
+ ):
330
+ """Get a Meeting."""
331
+ api = get_api(debug=debug)
332
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}"
333
+ params = {}
334
+ if current is not None:
335
+ params["current"] = current
336
+ if host_email is not None:
337
+ params["hostEmail"] = host_email
338
+ try:
339
+ result = api.session.rest_get(url, params=params)
340
+ except WebexError as e:
341
+ handle_rest_error(e)
342
+ if output == "json":
343
+ print_json(result)
344
+ else:
345
+ if isinstance(result, dict):
346
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
347
+ elif isinstance(result, list):
348
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
349
+ else:
350
+ print_json(result)
351
+
352
+
353
+
354
+ @app.command("update")
355
+ def update(
356
+ meeting_id: str = typer.Argument(help="meetingId"),
357
+ title: str = typer.Option(None, "--title", help="Meeting title. The title can be a maximum of 128 characters"),
358
+ agenda: str = typer.Option(None, "--agenda", help="Meeting agenda. The agenda can be a maximum of 1300 characte"),
359
+ password: str = typer.Option(None, "--password", help="Meeting password. Must conform to the site's password comple"),
360
+ start: str = typer.Option(None, "--start", help="Date and time for the start of meeting in any [ISO 8601](htt"),
361
+ end: str = typer.Option(None, "--end", help="Date and time for the end of meeting in any [ISO 8601](https"),
362
+ timezone: str = typer.Option(None, "--timezone", help="[Time zone](https://en.wikipedia.org/wiki/List_of_tz_databas"),
363
+ recurrence: str = typer.Option(None, "--recurrence", help="Meeting series recurrence rule (conforming with [RFC 2445](h"),
364
+ enabled_auto_record_meeting: bool = typer.Option(None, "--enabled-auto-record-meeting/--no-enabled-auto-record-meeting", help="Whether or not meeting is recorded automatically."),
365
+ allow_any_user_to_be_co_host: bool = typer.Option(None, "--allow-any-user-to-be-co-host/--no-allow-any-user-to-be-co-host", help="Whether or not to allow any attendee with a host account on"),
366
+ enabled_join_before_host: bool = typer.Option(None, "--enabled-join-before-host/--no-enabled-join-before-host", help="Whether or not to allow any attendee to join the meeting bef"),
367
+ enable_connect_audio_before_host: bool = typer.Option(None, "--enable-connect-audio-before-host/--no-enable-connect-audio-before-host", help="Whether or not to allow any attendee to connect audio in the"),
368
+ join_before_host_minutes: str = typer.Option(None, "--join-before-host-minutes", help="Number of minutes an attendee can join the meeting before th"),
369
+ exclude_password: bool = typer.Option(None, "--exclude-password/--no-exclude-password", help="Whether or not to exclude the meeting password from the emai"),
370
+ public_meeting: bool = typer.Option(None, "--public-meeting/--no-public-meeting", help="Whether or not to allow the meeting to be listed on the publ"),
371
+ reminder_time: str = typer.Option(None, "--reminder-time", help="The number of minutes before the meeting begins, that an ema"),
372
+ unlocked_meeting_join_security: str = typer.Option(None, "--unlocked-meeting-join-security", help="Choices: allowJoin, allowJoinWithLobby, blockFromJoin"),
373
+ session_type_id: str = typer.Option(None, "--session-type-id", help="Unique identifier for a meeting session type for the user. T"),
374
+ enabled_webcast_view: bool = typer.Option(None, "--enabled-webcast-view/--no-enabled-webcast-view", help="Whether or not webcast view is enabled."),
375
+ panelist_password: str = typer.Option(None, "--panelist-password", help="Password for panelists of a webinar meeting. Must conform to"),
376
+ enable_automatic_lock: bool = typer.Option(None, "--enable-automatic-lock/--no-enable-automatic-lock", help="Whether or not to automatically lock the meeting after it st"),
377
+ automatic_lock_minutes: str = typer.Option(None, "--automatic-lock-minutes", help="The number of minutes after the meeting begins, for automati"),
378
+ allow_first_user_to_be_co_host: bool = typer.Option(None, "--allow-first-user-to-be-co-host/--no-allow-first-user-to-be-co-host", help="Whether or not to allow the first attendee of the meeting wi"),
379
+ allow_authenticated_devices: bool = typer.Option(None, "--allow-authenticated-devices/--no-allow-authenticated-devices", help="Whether or not to allow authenticated video devices in the m"),
380
+ send_email: bool = typer.Option(None, "--send-email/--no-send-email", help="Whether or not to send emails to host and invitees. It is an"),
381
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This attribute should on"),
382
+ enabled_breakout_sessions: bool = typer.Option(None, "--enabled-breakout-sessions/--no-enabled-breakout-sessions", help="Whether or not breakout sessions are enabled. If the value o"),
383
+ enabled_audio_watermark: bool = typer.Option(None, "--enabled-audio-watermark/--no-enabled-audio-watermark", help="Whether or not the audio watermark is enabled. If it's `true"),
384
+ enabled_visual_watermark: bool = typer.Option(None, "--enabled-visual-watermark/--no-enabled-visual-watermark", help="Whether or not the visual watermark is enabled. If it's `tru"),
385
+ visual_watermark_opacity: str = typer.Option(None, "--visual-watermark-opacity", help="Opacity level for the visual watermark. The value must be be"),
386
+ require_attendee_login: bool = typer.Option(None, "--require-attendee-login/--no-require-attendee-login", help="Require attendees to sign in before joining the webinar. Thi"),
387
+ restrict_to_invitees: bool = typer.Option(None, "--restrict-to-invitees/--no-restrict-to-invitees", help="Restrict webinar to invited attendees only. This option work"),
388
+ enabled_live_stream: bool = typer.Option(None, "--enabled-live-stream/--no-enabled-live-stream", help="Whether or not live streaming is enabled."),
389
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
390
+ debug: bool = typer.Option(False, "--debug"),
391
+ ):
392
+ """Update a Meeting\n\nExample --json-body:\n '{"title":"...","agenda":"...","password":"...","start":"...","end":"...","timezone":"...","recurrence":"...","enabledAutoRecordMeeting":true}'."""
393
+ api = get_api(debug=debug)
394
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}"
395
+ if json_body:
396
+ body = json.loads(json_body)
397
+ else:
398
+ body = {}
399
+ if title is not None:
400
+ body["title"] = title
401
+ if agenda is not None:
402
+ body["agenda"] = agenda
403
+ if password is not None:
404
+ body["password"] = password
405
+ if start is not None:
406
+ body["start"] = start
407
+ if end is not None:
408
+ body["end"] = end
409
+ if timezone is not None:
410
+ body["timezone"] = timezone
411
+ if recurrence is not None:
412
+ body["recurrence"] = recurrence
413
+ if enabled_auto_record_meeting is not None:
414
+ body["enabledAutoRecordMeeting"] = enabled_auto_record_meeting
415
+ if allow_any_user_to_be_co_host is not None:
416
+ body["allowAnyUserToBeCoHost"] = allow_any_user_to_be_co_host
417
+ if enabled_join_before_host is not None:
418
+ body["enabledJoinBeforeHost"] = enabled_join_before_host
419
+ if enable_connect_audio_before_host is not None:
420
+ body["enableConnectAudioBeforeHost"] = enable_connect_audio_before_host
421
+ if join_before_host_minutes is not None:
422
+ body["joinBeforeHostMinutes"] = join_before_host_minutes
423
+ if exclude_password is not None:
424
+ body["excludePassword"] = exclude_password
425
+ if public_meeting is not None:
426
+ body["publicMeeting"] = public_meeting
427
+ if reminder_time is not None:
428
+ body["reminderTime"] = reminder_time
429
+ if unlocked_meeting_join_security is not None:
430
+ body["unlockedMeetingJoinSecurity"] = unlocked_meeting_join_security
431
+ if session_type_id is not None:
432
+ body["sessionTypeId"] = session_type_id
433
+ if enabled_webcast_view is not None:
434
+ body["enabledWebcastView"] = enabled_webcast_view
435
+ if panelist_password is not None:
436
+ body["panelistPassword"] = panelist_password
437
+ if enable_automatic_lock is not None:
438
+ body["enableAutomaticLock"] = enable_automatic_lock
439
+ if automatic_lock_minutes is not None:
440
+ body["automaticLockMinutes"] = automatic_lock_minutes
441
+ if allow_first_user_to_be_co_host is not None:
442
+ body["allowFirstUserToBeCoHost"] = allow_first_user_to_be_co_host
443
+ if allow_authenticated_devices is not None:
444
+ body["allowAuthenticatedDevices"] = allow_authenticated_devices
445
+ if send_email is not None:
446
+ body["sendEmail"] = send_email
447
+ if host_email is not None:
448
+ body["hostEmail"] = host_email
449
+ if enabled_breakout_sessions is not None:
450
+ body["enabledBreakoutSessions"] = enabled_breakout_sessions
451
+ if enabled_audio_watermark is not None:
452
+ body["enabledAudioWatermark"] = enabled_audio_watermark
453
+ if enabled_visual_watermark is not None:
454
+ body["enabledVisualWatermark"] = enabled_visual_watermark
455
+ if visual_watermark_opacity is not None:
456
+ body["visualWatermarkOpacity"] = visual_watermark_opacity
457
+ if require_attendee_login is not None:
458
+ body["requireAttendeeLogin"] = require_attendee_login
459
+ if restrict_to_invitees is not None:
460
+ body["restrictToInvitees"] = restrict_to_invitees
461
+ if enabled_live_stream is not None:
462
+ body["enabledLiveStream"] = enabled_live_stream
463
+ try:
464
+ result = api.session.rest_put(url, json=body)
465
+ except WebexError as e:
466
+ handle_rest_error(e)
467
+ typer.echo(f"Updated.")
468
+
469
+
470
+
471
+ @app.command("update-meetings")
472
+ def update_meetings(
473
+ meeting_id: str = typer.Argument(help="meetingId"),
474
+ title: str = typer.Option(None, "--title", help="Meeting title. The title can be a maximum of 128 characters"),
475
+ agenda: str = typer.Option(None, "--agenda", help="Meeting agenda. The agenda can be a maximum of 1300 characte"),
476
+ password: str = typer.Option(None, "--password", help="Meeting password. Must conform to the site's password comple"),
477
+ start: str = typer.Option(None, "--start", help="Date and time for the start of meeting in any [ISO 8601](htt"),
478
+ end: str = typer.Option(None, "--end", help="Date and time for the end of meeting in any [ISO 8601](https"),
479
+ timezone: str = typer.Option(None, "--timezone", help="[Time zone](https://en.wikipedia.org/wiki/List_of_tz_databas"),
480
+ recurrence: str = typer.Option(None, "--recurrence", help="Meeting series recurrence rule (conforming with [RFC 2445](h"),
481
+ enabled_auto_record_meeting: str = typer.Option(None, "--enabled-auto-record-meeting", help="Whether or not meeting is recorded automatically."),
482
+ allow_any_user_to_be_co_host: str = typer.Option(None, "--allow-any-user-to-be-co-host", help="Whether or not to allow any attendee with a host account on"),
483
+ enabled_join_before_host: str = typer.Option(None, "--enabled-join-before-host", help="Whether or not to allow any attendee to join the meeting bef"),
484
+ enable_connect_audio_before_host: str = typer.Option(None, "--enable-connect-audio-before-host", help="Whether or not to allow any attendee to connect audio in the"),
485
+ join_before_host_minutes: str = typer.Option(None, "--join-before-host-minutes", help="Number of minutes an attendee can join the meeting before th"),
486
+ exclude_password: str = typer.Option(None, "--exclude-password", help="Whether or not to exclude the meeting password from the emai"),
487
+ public_meeting: str = typer.Option(None, "--public-meeting", help="Whether or not to allow the meeting to be listed on the publ"),
488
+ reminder_time: str = typer.Option(None, "--reminder-time", help="The number of minutes before the meeting begins, that an ema"),
489
+ unlocked_meeting_join_security: str = typer.Option(None, "--unlocked-meeting-join-security", help="Choices: allowJoin, allowJoinWithLobby, blockFromJoin"),
490
+ session_type_id: str = typer.Option(None, "--session-type-id", help="Unique identifier for a meeting session type for the user. T"),
491
+ enabled_webcast_view: str = typer.Option(None, "--enabled-webcast-view", help="Whether or not webcast view is enabled."),
492
+ panelist_password: str = typer.Option(None, "--panelist-password", help="Password for panelists of a webinar meeting. Must conform to"),
493
+ enable_automatic_lock: str = typer.Option(None, "--enable-automatic-lock", help="Whether or not to automatically lock the meeting after it st"),
494
+ automatic_lock_minutes: str = typer.Option(None, "--automatic-lock-minutes", help="The number of minutes after the meeting begins, for automati"),
495
+ allow_first_user_to_be_co_host: str = typer.Option(None, "--allow-first-user-to-be-co-host", help="Whether or not to allow the first attendee of the meeting wi"),
496
+ allow_authenticated_devices: str = typer.Option(None, "--allow-authenticated-devices", help="Whether or not to allow authenticated video devices in the m"),
497
+ send_email: str = typer.Option(None, "--send-email", help="Whether or not to send emails to host and invitees. It is an"),
498
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This attribute should on"),
499
+ enabled_breakout_sessions: str = typer.Option(None, "--enabled-breakout-sessions", help="Whether or not breakout sessions are enabled. If the value o"),
500
+ enabled_audio_watermark: str = typer.Option(None, "--enabled-audio-watermark", help="Whether or not the audio watermark is enabled. If it's `true"),
501
+ enabled_visual_watermark: str = typer.Option(None, "--enabled-visual-watermark", help="Whether or not the visual watermark is enabled. If it's `tru"),
502
+ visual_watermark_opacity: str = typer.Option(None, "--visual-watermark-opacity", help="Opacity level for the visual watermark. The value must be be"),
503
+ require_attendee_login: str = typer.Option(None, "--require-attendee-login", help="Require attendees to sign in before joining the webinar. Thi"),
504
+ restrict_to_invitees: str = typer.Option(None, "--restrict-to-invitees", help="Restrict webinar to invited attendees only. This option work"),
505
+ enabled_live_stream: str = typer.Option(None, "--enabled-live-stream", help="Whether or not live streaming is enabled."),
506
+ value: str = typer.Option(None, "--value", help="Value for replace op (JSON-parsed: string, number, bool, or array)"),
507
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
508
+ debug: bool = typer.Option(False, "--debug"),
509
+ ):
510
+ """Patch a Meeting\n\nExample --json-body:\n '{"title":"...","agenda":"...","password":"...","start":"...","end":"...","timezone":"...","recurrence":"...","enabledAutoRecordMeeting":true}'."""
511
+ api = get_api(debug=debug)
512
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}"
513
+ if json_body:
514
+ body = json.loads(json_body)
515
+ else:
516
+ patch_op = {}
517
+ if title is not None:
518
+ patch_op["title"] = title
519
+ if agenda is not None:
520
+ patch_op["agenda"] = agenda
521
+ if password is not None:
522
+ patch_op["password"] = password
523
+ if start is not None:
524
+ patch_op["start"] = start
525
+ if end is not None:
526
+ patch_op["end"] = end
527
+ if timezone is not None:
528
+ patch_op["timezone"] = timezone
529
+ if recurrence is not None:
530
+ patch_op["recurrence"] = recurrence
531
+ if enabled_auto_record_meeting is not None:
532
+ patch_op["enabledAutoRecordMeeting"] = enabled_auto_record_meeting
533
+ if allow_any_user_to_be_co_host is not None:
534
+ patch_op["allowAnyUserToBeCoHost"] = allow_any_user_to_be_co_host
535
+ if enabled_join_before_host is not None:
536
+ patch_op["enabledJoinBeforeHost"] = enabled_join_before_host
537
+ if enable_connect_audio_before_host is not None:
538
+ patch_op["enableConnectAudioBeforeHost"] = enable_connect_audio_before_host
539
+ if join_before_host_minutes is not None:
540
+ patch_op["joinBeforeHostMinutes"] = join_before_host_minutes
541
+ if exclude_password is not None:
542
+ patch_op["excludePassword"] = exclude_password
543
+ if public_meeting is not None:
544
+ patch_op["publicMeeting"] = public_meeting
545
+ if reminder_time is not None:
546
+ patch_op["reminderTime"] = reminder_time
547
+ if unlocked_meeting_join_security is not None:
548
+ patch_op["unlockedMeetingJoinSecurity"] = unlocked_meeting_join_security
549
+ if session_type_id is not None:
550
+ patch_op["sessionTypeId"] = session_type_id
551
+ if enabled_webcast_view is not None:
552
+ patch_op["enabledWebcastView"] = enabled_webcast_view
553
+ if panelist_password is not None:
554
+ patch_op["panelistPassword"] = panelist_password
555
+ if enable_automatic_lock is not None:
556
+ patch_op["enableAutomaticLock"] = enable_automatic_lock
557
+ if automatic_lock_minutes is not None:
558
+ patch_op["automaticLockMinutes"] = automatic_lock_minutes
559
+ if allow_first_user_to_be_co_host is not None:
560
+ patch_op["allowFirstUserToBeCoHost"] = allow_first_user_to_be_co_host
561
+ if allow_authenticated_devices is not None:
562
+ patch_op["allowAuthenticatedDevices"] = allow_authenticated_devices
563
+ if send_email is not None:
564
+ patch_op["sendEmail"] = send_email
565
+ if host_email is not None:
566
+ patch_op["hostEmail"] = host_email
567
+ if enabled_breakout_sessions is not None:
568
+ patch_op["enabledBreakoutSessions"] = enabled_breakout_sessions
569
+ if enabled_audio_watermark is not None:
570
+ patch_op["enabledAudioWatermark"] = enabled_audio_watermark
571
+ if enabled_visual_watermark is not None:
572
+ patch_op["enabledVisualWatermark"] = enabled_visual_watermark
573
+ if visual_watermark_opacity is not None:
574
+ patch_op["visualWatermarkOpacity"] = visual_watermark_opacity
575
+ if require_attendee_login is not None:
576
+ patch_op["requireAttendeeLogin"] = require_attendee_login
577
+ if restrict_to_invitees is not None:
578
+ patch_op["restrictToInvitees"] = restrict_to_invitees
579
+ if enabled_live_stream is not None:
580
+ patch_op["enabledLiveStream"] = enabled_live_stream
581
+ if value is not None:
582
+ try:
583
+ patch_op["value"] = json.loads(value)
584
+ except json.JSONDecodeError:
585
+ patch_op["value"] = value
586
+ body = [patch_op]
587
+ try:
588
+ result = api.session.rest_patch(url, json=body, content_type="application/json-patch+json")
589
+ except WebexError as e:
590
+ handle_rest_error(e)
591
+ typer.echo(f"Updated.")
592
+
593
+
594
+
595
+ @app.command("delete")
596
+ def delete(
597
+ meeting_id: str = typer.Argument(help="meetingId"),
598
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This parameter is only u"),
599
+ send_email: str = typer.Option(None, "--send-email", help="Whether or not to send emails to host and invitees. It is an"),
600
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
601
+ debug: bool = typer.Option(False, "--debug"),
602
+ ):
603
+ """Delete a Meeting."""
604
+ if not force:
605
+ typer.confirm(f"Delete {meeting_id}?", abort=True)
606
+ api = get_api(debug=debug)
607
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}"
608
+ params = {}
609
+ if host_email is not None:
610
+ params["hostEmail"] = host_email
611
+ if send_email is not None:
612
+ params["sendEmail"] = send_email
613
+ try:
614
+ api.session.rest_delete(url, params=params)
615
+ except WebexError as e:
616
+ handle_rest_error(e)
617
+ typer.echo(f"Deleted: {meeting_id}")
618
+
619
+
620
+
621
+ @app.command("create-join")
622
+ def create_join(
623
+ meeting_id: str = typer.Option(None, "--meeting-id", help="Unique identifier for the meeting. This parameter applies to"),
624
+ meeting_number: str = typer.Option(None, "--meeting-number", help="Meeting number. Applies to meeting series, scheduled meeting"),
625
+ web_link: str = typer.Option(None, "--web-link", help="Link to a meeting information page where the meeting client"),
626
+ join_directly: bool = typer.Option(None, "--join-directly/--no-join-directly", help="Whether or not to redirect to `joinLink`. It is an optional"),
627
+ email: str = typer.Option(None, "--email", help="Email address of meeting participant. If `email` is specifie"),
628
+ display_name: str = typer.Option(None, "--display-name", help="Display name of meeting participant. If `displayName` is spe"),
629
+ password: str = typer.Option(None, "--password", help="Required when the meeting is protected by a password and the"),
630
+ expiration_minutes: str = typer.Option(None, "--expiration-minutes", help="Expiration duration of `joinLink` in minutes. Must be betwee"),
631
+ registration_id: str = typer.Option(None, "--registration-id", help="Required when the meeting is webinar-enabled and enabled reg"),
632
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This attribute should be"),
633
+ create_join_link_as_web_link: bool = typer.Option(None, "--create-join-link-as-web-link/--no-create-join-link-as-web-link", help="When `createJoinLinkAsWebLink` is true, the returned `joinLi"),
634
+ create_start_link_as_web_link: bool = typer.Option(None, "--create-start-link-as-web-link/--no-create-start-link-as-web-link", help="When `createStartLinkAsWebLink` is true, the returned `start"),
635
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
636
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
637
+ debug: bool = typer.Option(False, "--debug"),
638
+ ):
639
+ """Join a Meeting."""
640
+ api = get_api(debug=debug)
641
+ url = f"https://webexapis.com/v1/meetings/join"
642
+ if json_body:
643
+ body = json.loads(json_body)
644
+ else:
645
+ body = {}
646
+ if meeting_id is not None:
647
+ body["meetingId"] = meeting_id
648
+ if meeting_number is not None:
649
+ body["meetingNumber"] = meeting_number
650
+ if web_link is not None:
651
+ body["webLink"] = web_link
652
+ if join_directly is not None:
653
+ body["joinDirectly"] = join_directly
654
+ if email is not None:
655
+ body["email"] = email
656
+ if display_name is not None:
657
+ body["displayName"] = display_name
658
+ if password is not None:
659
+ body["password"] = password
660
+ if expiration_minutes is not None:
661
+ body["expirationMinutes"] = expiration_minutes
662
+ if registration_id is not None:
663
+ body["registrationId"] = registration_id
664
+ if host_email is not None:
665
+ body["hostEmail"] = host_email
666
+ if create_join_link_as_web_link is not None:
667
+ body["createJoinLinkAsWebLink"] = create_join_link_as_web_link
668
+ if create_start_link_as_web_link is not None:
669
+ body["createStartLinkAsWebLink"] = create_start_link_as_web_link
670
+ try:
671
+ result = api.session.rest_post(url, json=body)
672
+ except WebexError as e:
673
+ handle_rest_error(e)
674
+ if output == "json":
675
+ print_json(result)
676
+ elif isinstance(result, dict) and "id" in result:
677
+ typer.echo(f"Created: {result['id']}")
678
+ elif not result or result == {}:
679
+ typer.echo("Created.")
680
+ else:
681
+ print_json(result)
682
+
683
+
684
+
685
+ @app.command("list-templates")
686
+ def list_templates(
687
+ template_type: str = typer.Option(None, "--template-type", help="Choices: meeting, webinar"),
688
+ locale: str = typer.Option(None, "--locale", help="Locale for the meeting template objects being requested. If"),
689
+ is_default: str = typer.Option(None, "--is-default", help="The value is `true` or `false`. If it's `true`, return the d"),
690
+ is_standard: str = typer.Option(None, "--is-standard", help="The value is `true` or `false`. If it's `true`, return the s"),
691
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This parameter is only u"),
692
+ site_url: str = typer.Option(None, "--site-url", help="URL of the Webex site which the API lists meeting templates"),
693
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
694
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
695
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
696
+ debug: bool = typer.Option(False, "--debug"),
697
+ ):
698
+ """List Meeting Templates."""
699
+ api = get_api(debug=debug)
700
+ url = f"https://webexapis.com/v1/meetings/templates"
701
+ params = {}
702
+ if template_type is not None:
703
+ params["templateType"] = template_type
704
+ if locale is not None:
705
+ params["locale"] = locale
706
+ if is_default is not None:
707
+ params["isDefault"] = is_default
708
+ if is_standard is not None:
709
+ params["isStandard"] = is_standard
710
+ if host_email is not None:
711
+ params["hostEmail"] = host_email
712
+ if site_url is not None:
713
+ params["siteUrl"] = site_url
714
+ if limit > 0:
715
+ params["max"] = limit
716
+ if offset > 0:
717
+ params["start"] = offset
718
+ result = None
719
+ try:
720
+ result = api.session.rest_get(url, params=params)
721
+ except WebexError as e:
722
+ handle_rest_error(e)
723
+ result = result or []
724
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
725
+ if output == "json":
726
+ print_json(items)
727
+ else:
728
+ print_table(items, columns=[('ID', 'id'), ('Title', 'title'), ('Start', 'start'), ('End', 'end'), ('State', 'state')], limit=limit)
729
+
730
+
731
+
732
+ @app.command("show-templates")
733
+ def show_templates(
734
+ template_id: str = typer.Argument(help="templateId"),
735
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This parameter is only u"),
736
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
737
+ debug: bool = typer.Option(False, "--debug"),
738
+ ):
739
+ """Get a Meeting Template."""
740
+ api = get_api(debug=debug)
741
+ url = f"https://webexapis.com/v1/meetings/templates/{template_id}"
742
+ params = {}
743
+ if host_email is not None:
744
+ params["hostEmail"] = host_email
745
+ try:
746
+ result = api.session.rest_get(url, params=params)
747
+ except WebexError as e:
748
+ handle_rest_error(e)
749
+ if output == "json":
750
+ print_json(result)
751
+ else:
752
+ if isinstance(result, dict):
753
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
754
+ elif isinstance(result, list):
755
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
756
+ else:
757
+ print_json(result)
758
+
759
+
760
+
761
+ @app.command("show-controls")
762
+ def show_controls(
763
+ meeting_id: str = typer.Option(..., "--meeting-id", help="Unique identifier for the meeting. Does not support meeting"),
764
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
765
+ debug: bool = typer.Option(False, "--debug"),
766
+ ):
767
+ """Get Meeting Control Status."""
768
+ api = get_api(debug=debug)
769
+ url = f"https://webexapis.com/v1/meetings/controls"
770
+ params = {}
771
+ if meeting_id is not None:
772
+ params["meetingId"] = meeting_id
773
+ try:
774
+ result = api.session.rest_get(url, params=params)
775
+ except WebexError as e:
776
+ handle_rest_error(e)
777
+ if output == "json":
778
+ print_json(result)
779
+ else:
780
+ if isinstance(result, dict):
781
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
782
+ elif isinstance(result, list):
783
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
784
+ else:
785
+ print_json(result)
786
+
787
+
788
+
789
+ @app.command("update-controls")
790
+ def update_controls(
791
+ meeting_id: str = typer.Option(..., "--meeting-id", help="Unique identifier for the meeting. Does not support meeting"),
792
+ recording_started: bool = typer.Option(None, "--recording-started/--no-recording-started", help="The value can be true or false. true means to start the reco"),
793
+ recording_paused: bool = typer.Option(None, "--recording-paused/--no-recording-paused", help="The value can be true or false, will be ignored if 'recordin"),
794
+ locked: bool = typer.Option(None, "--locked/--no-locked", help="The value is true or false."),
795
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
796
+ debug: bool = typer.Option(False, "--debug"),
797
+ ):
798
+ """Update Meeting Control Status."""
799
+ api = get_api(debug=debug)
800
+ url = f"https://webexapis.com/v1/meetings/controls"
801
+ params = {}
802
+ if meeting_id is not None:
803
+ params["meetingId"] = meeting_id
804
+ if json_body:
805
+ body = json.loads(json_body)
806
+ else:
807
+ body = {}
808
+ if recording_started is not None:
809
+ body["recordingStarted"] = recording_started
810
+ if recording_paused is not None:
811
+ body["recordingPaused"] = recording_paused
812
+ if locked is not None:
813
+ body["locked"] = locked
814
+ try:
815
+ result = api.session.rest_put(url, json=body, params=params)
816
+ except WebexError as e:
817
+ handle_rest_error(e)
818
+ typer.echo(f"Updated.")
819
+
820
+
821
+
822
+ @app.command("list-session-types")
823
+ def list_session_types(
824
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the user. This parameter is only used if t"),
825
+ site_url: str = typer.Option(None, "--site-url", help="Webex site URL to query. If `siteUrl` is not specified, the"),
826
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
827
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
828
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
829
+ debug: bool = typer.Option(False, "--debug"),
830
+ ):
831
+ """List Meeting Session Types."""
832
+ api = get_api(debug=debug)
833
+ url = f"https://webexapis.com/v1/meetings/sessionTypes"
834
+ params = {}
835
+ if host_email is not None:
836
+ params["hostEmail"] = host_email
837
+ if site_url is not None:
838
+ params["siteUrl"] = site_url
839
+ if limit > 0:
840
+ params["max"] = limit
841
+ if offset > 0:
842
+ params["start"] = offset
843
+ result = None
844
+ try:
845
+ result = api.session.rest_get(url, params=params)
846
+ except WebexError as e:
847
+ handle_rest_error(e)
848
+ result = result or []
849
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
850
+ if output == "json":
851
+ print_json(items)
852
+ else:
853
+ print_table(items, columns=[('ID', 'id'), ('Title', 'title'), ('Start', 'start'), ('End', 'end'), ('State', 'state')], limit=limit)
854
+
855
+
856
+
857
+ @app.command("show-session-types")
858
+ def show_session_types(
859
+ session_type_id: str = typer.Argument(help="sessionTypeId"),
860
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the user. This parameter is only used if t"),
861
+ site_url: str = typer.Option(None, "--site-url", help="Webex site URL to query. If `siteUrl` is not specified, the"),
862
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
863
+ debug: bool = typer.Option(False, "--debug"),
864
+ ):
865
+ """Get a Meeting Session Type."""
866
+ api = get_api(debug=debug)
867
+ url = f"https://webexapis.com/v1/meetings/sessionTypes/{session_type_id}"
868
+ params = {}
869
+ if host_email is not None:
870
+ params["hostEmail"] = host_email
871
+ if site_url is not None:
872
+ params["siteUrl"] = site_url
873
+ try:
874
+ result = api.session.rest_get(url, params=params)
875
+ except WebexError as e:
876
+ handle_rest_error(e)
877
+ if output == "json":
878
+ print_json(result)
879
+ else:
880
+ if isinstance(result, dict):
881
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
882
+ elif isinstance(result, list):
883
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
884
+ else:
885
+ print_json(result)
886
+
887
+
888
+
889
+ @app.command("list-registration")
890
+ def list_registration(
891
+ meeting_id: str = typer.Argument(help="meetingId"),
892
+ current: str = typer.Option(None, "--current", help="Whether or not to retrieve only the current scheduled meetin"),
893
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This parameter is only u"),
894
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
895
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
896
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
897
+ debug: bool = typer.Option(False, "--debug"),
898
+ ):
899
+ """Get registration form for a meeting."""
900
+ api = get_api(debug=debug)
901
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}/registration"
902
+ params = {}
903
+ if current is not None:
904
+ params["current"] = current
905
+ if host_email is not None:
906
+ params["hostEmail"] = host_email
907
+ if limit > 0:
908
+ params["max"] = limit
909
+ if offset > 0:
910
+ params["start"] = offset
911
+ result = None
912
+ try:
913
+ result = api.session.rest_get(url, params=params)
914
+ except WebexError as e:
915
+ handle_rest_error(e)
916
+ result = result or []
917
+ items = result.get("customizedQuestions", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
918
+ if output == "json":
919
+ print_json(items)
920
+ else:
921
+ print_table(items, columns=[('ID', 'id'), ('Title', 'title'), ('Start', 'start'), ('End', 'end'), ('State', 'state')], limit=limit)
922
+
923
+
924
+
925
+ @app.command("update-registration")
926
+ def update_registration(
927
+ meeting_id: str = typer.Argument(help="meetingId"),
928
+ host_email: str = typer.Option(None, "--host-email", help="- Email address for the meeting host. This parameter is only"),
929
+ auto_accept_request: bool = typer.Option(None, "--auto-accept-request/--no-auto-accept-request", help="Whether or not meeting registration requests are accepted au"),
930
+ require_first_name: bool = typer.Option(None, "--require-first-name/--no-require-first-name", help="Whether or not a registrant's first name is required for mee"),
931
+ require_last_name: bool = typer.Option(None, "--require-last-name/--no-require-last-name", help="Whether or not a registrant's last name is required for meet"),
932
+ require_email: bool = typer.Option(None, "--require-email/--no-require-email", help="Whether or not a registrant's email is required for meeting"),
933
+ require_job_title: bool = typer.Option(None, "--require-job-title/--no-require-job-title", help="Whether or not a registrant's job title is shown or required"),
934
+ require_company_name: bool = typer.Option(None, "--require-company-name/--no-require-company-name", help="Whether or not a registrant's company name is shown or requi"),
935
+ require_address1: bool = typer.Option(None, "--require-address1/--no-require-address1", help="Whether or not a registrant's first address field is shown o"),
936
+ require_address2: bool = typer.Option(None, "--require-address2/--no-require-address2", help="Whether or not a registrant's second address field is shown"),
937
+ require_city: bool = typer.Option(None, "--require-city/--no-require-city", help="Whether or not a registrant's city is shown or required for"),
938
+ require_state: bool = typer.Option(None, "--require-state/--no-require-state", help="Whether or not a registrant's state is shown or required for"),
939
+ require_zip_code: bool = typer.Option(None, "--require-zip-code/--no-require-zip-code", help="Whether or not a registrant's postal code is shown or requir"),
940
+ require_country_region: bool = typer.Option(None, "--require-country-region/--no-require-country-region", help="Whether or not a registrant's country or region is shown or"),
941
+ require_work_phone: bool = typer.Option(None, "--require-work-phone/--no-require-work-phone", help="Whether or not a registrant's work phone number is shown or"),
942
+ require_fax: bool = typer.Option(None, "--require-fax/--no-require-fax", help="Whether or not a registrant's fax number is shown or require"),
943
+ max_register_num: str = typer.Option(None, "--max-register-num", help="Maximum number of meeting registrations. This only applies t"),
944
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
945
+ debug: bool = typer.Option(False, "--debug"),
946
+ ):
947
+ """Update Meeting Registration Form\n\nExample --json-body:\n '{"hostEmail":"...","autoAcceptRequest":true,"requireFirstName":true,"requireLastName":true,"requireEmail":true,"requireJobTitle":true,"requireCompanyName":true,"requireAddress1":true}'."""
948
+ api = get_api(debug=debug)
949
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}/registration"
950
+ if json_body:
951
+ body = json.loads(json_body)
952
+ else:
953
+ body = {}
954
+ if host_email is not None:
955
+ body["hostEmail"] = host_email
956
+ if auto_accept_request is not None:
957
+ body["autoAcceptRequest"] = auto_accept_request
958
+ if require_first_name is not None:
959
+ body["requireFirstName"] = require_first_name
960
+ if require_last_name is not None:
961
+ body["requireLastName"] = require_last_name
962
+ if require_email is not None:
963
+ body["requireEmail"] = require_email
964
+ if require_job_title is not None:
965
+ body["requireJobTitle"] = require_job_title
966
+ if require_company_name is not None:
967
+ body["requireCompanyName"] = require_company_name
968
+ if require_address1 is not None:
969
+ body["requireAddress1"] = require_address1
970
+ if require_address2 is not None:
971
+ body["requireAddress2"] = require_address2
972
+ if require_city is not None:
973
+ body["requireCity"] = require_city
974
+ if require_state is not None:
975
+ body["requireState"] = require_state
976
+ if require_zip_code is not None:
977
+ body["requireZipCode"] = require_zip_code
978
+ if require_country_region is not None:
979
+ body["requireCountryRegion"] = require_country_region
980
+ if require_work_phone is not None:
981
+ body["requireWorkPhone"] = require_work_phone
982
+ if require_fax is not None:
983
+ body["requireFax"] = require_fax
984
+ if max_register_num is not None:
985
+ body["maxRegisterNum"] = max_register_num
986
+ try:
987
+ result = api.session.rest_put(url, json=body)
988
+ except WebexError as e:
989
+ handle_rest_error(e)
990
+ typer.echo(f"Updated.")
991
+
992
+
993
+
994
+ @app.command("delete-registration")
995
+ def delete_registration(
996
+ meeting_id: str = typer.Argument(help="meetingId"),
997
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
998
+ debug: bool = typer.Option(False, "--debug"),
999
+ ):
1000
+ """Delete Meeting Registration Form."""
1001
+ if not force:
1002
+ typer.confirm(f"Delete {meeting_id}?", abort=True)
1003
+ api = get_api(debug=debug)
1004
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}/registration"
1005
+ try:
1006
+ api.session.rest_delete(url)
1007
+ except WebexError as e:
1008
+ handle_rest_error(e)
1009
+ typer.echo(f"Deleted: {meeting_id}")
1010
+
1011
+
1012
+
1013
+ @app.command("list-registrants")
1014
+ def list_registrants(
1015
+ meeting_id: str = typer.Argument(help="meetingId"),
1016
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This parameter is only u"),
1017
+ current: str = typer.Option(None, "--current", help="Whether or not to retrieve only the current scheduled meetin"),
1018
+ email: str = typer.Option(None, "--email", help="Registrant's email to filter registrants."),
1019
+ registration_time_from: str = typer.Option(None, "--registration-time-from", help="The time registrants register a meeting starts from the spec"),
1020
+ registration_time_to: str = typer.Option(None, "--registration-time-to", help="The time registrants register a meeting before the specified"),
1021
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1022
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1023
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1024
+ debug: bool = typer.Option(False, "--debug"),
1025
+ ):
1026
+ """List Meeting Registrants."""
1027
+ api = get_api(debug=debug)
1028
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}/registrants"
1029
+ params = {}
1030
+ if host_email is not None:
1031
+ params["hostEmail"] = host_email
1032
+ if current is not None:
1033
+ params["current"] = current
1034
+ if email is not None:
1035
+ params["email"] = email
1036
+ if registration_time_from is not None:
1037
+ params["registrationTimeFrom"] = registration_time_from
1038
+ if registration_time_to is not None:
1039
+ params["registrationTimeTo"] = registration_time_to
1040
+ if limit > 0:
1041
+ params["max"] = limit
1042
+ if offset > 0:
1043
+ params["start"] = offset
1044
+ result = None
1045
+ try:
1046
+ result = api.session.rest_get(url, params=params)
1047
+ except WebexError as e:
1048
+ handle_rest_error(e)
1049
+ result = result or []
1050
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1051
+ if output == "json":
1052
+ print_json(items)
1053
+ else:
1054
+ print_table(items, columns=[('ID', 'id'), ('Title', 'title'), ('Start', 'start'), ('End', 'end'), ('State', 'state')], limit=limit)
1055
+
1056
+
1057
+
1058
+ @app.command("create-registrants")
1059
+ def create_registrants(
1060
+ meeting_id: str = typer.Argument(help="meetingId"),
1061
+ current: str = typer.Option(None, "--current", help="Whether or not to retrieve only the current scheduled meetin"),
1062
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This parameter is only u"),
1063
+ first_name: str = typer.Option(None, "--first-name", help="(required) The registrant's first name."),
1064
+ last_name: str = typer.Option(None, "--last-name", help="(required) The registrant's last name. (Required)"),
1065
+ email: str = typer.Option(None, "--email", help="(required) The registrant's email."),
1066
+ send_email: bool = typer.Option(None, "--send-email/--no-send-email", help="If `true` send email to the registrant. Default: `true`."),
1067
+ job_title: str = typer.Option(None, "--job-title", help="The registrant's job title. Registration options define whet"),
1068
+ company_name: str = typer.Option(None, "--company-name", help="The registrant's company. Registration options define whethe"),
1069
+ address1: str = typer.Option(None, "--address1", help="The registrant's first address line. Registration options de"),
1070
+ address2: str = typer.Option(None, "--address2", help="The registrant's second address line. Registration options d"),
1071
+ city: str = typer.Option(None, "--city", help="The registrant's city name. Registration options define whet"),
1072
+ state: str = typer.Option(None, "--state", help="The registrant's state. Registration options define whether"),
1073
+ zip_code: str = typer.Option(None, "--zip-code", help="The registrant's postal code. Registration options define wh"),
1074
+ country_region: str = typer.Option(None, "--country-region", help="The America is not a country or a specific region. Registrat"),
1075
+ work_phone: str = typer.Option(None, "--work-phone", help="The registrant's work phone number. Registration options def"),
1076
+ fax: str = typer.Option(None, "--fax", help="The registrant's FAX number. Registration options define whe"),
1077
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1078
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
1079
+ debug: bool = typer.Option(False, "--debug"),
1080
+ ):
1081
+ """Register a Meeting Registrant\n\nExample --json-body:\n '{"firstName":"...","lastName":"...","email":"...","sendEmail":true,"jobTitle":"...","companyName":"...","address1":"...","address2":"..."}'."""
1082
+ api = get_api(debug=debug)
1083
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}/registrants"
1084
+ params = {}
1085
+ if current is not None:
1086
+ params["current"] = current
1087
+ if host_email is not None:
1088
+ params["hostEmail"] = host_email
1089
+ if json_body:
1090
+ body = json.loads(json_body)
1091
+ else:
1092
+ body = {}
1093
+ if first_name is not None:
1094
+ body["firstName"] = first_name
1095
+ if last_name is not None:
1096
+ body["lastName"] = last_name
1097
+ if email is not None:
1098
+ body["email"] = email
1099
+ if send_email is not None:
1100
+ body["sendEmail"] = send_email
1101
+ if job_title is not None:
1102
+ body["jobTitle"] = job_title
1103
+ if company_name is not None:
1104
+ body["companyName"] = company_name
1105
+ if address1 is not None:
1106
+ body["address1"] = address1
1107
+ if address2 is not None:
1108
+ body["address2"] = address2
1109
+ if city is not None:
1110
+ body["city"] = city
1111
+ if state is not None:
1112
+ body["state"] = state
1113
+ if zip_code is not None:
1114
+ body["zipCode"] = zip_code
1115
+ if country_region is not None:
1116
+ body["countryRegion"] = country_region
1117
+ if work_phone is not None:
1118
+ body["workPhone"] = work_phone
1119
+ if fax is not None:
1120
+ body["fax"] = fax
1121
+ _missing = [f for f in ['firstName', 'lastName', 'email'] if f not in body or body[f] is None]
1122
+ if _missing:
1123
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
1124
+ raise typer.Exit(1)
1125
+ try:
1126
+ result = api.session.rest_post(url, json=body, params=params)
1127
+ except WebexError as e:
1128
+ handle_rest_error(e)
1129
+ if output == "json":
1130
+ print_json(result)
1131
+ elif isinstance(result, dict) and "id" in result:
1132
+ typer.echo(f"Created: {result['id']}")
1133
+ elif not result or result == {}:
1134
+ typer.echo("Created.")
1135
+ else:
1136
+ print_json(result)
1137
+
1138
+
1139
+
1140
+ @app.command("create-bulk-insert")
1141
+ def create_bulk_insert(
1142
+ meeting_id: str = typer.Argument(help="meetingId"),
1143
+ current: str = typer.Option(None, "--current", help="Whether or not to retrieve only the current scheduled meetin"),
1144
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This parameter is only u"),
1145
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1146
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
1147
+ debug: bool = typer.Option(False, "--debug"),
1148
+ ):
1149
+ """Batch register Meeting Registrants\n\nExample --json-body:\n '{"items":[{"firstName":"...","lastName":"...","email":"...","sendEmail":"...","jobTitle":"...","companyName":"...","address1":"...","address2":"..."}]}'."""
1150
+ api = get_api(debug=debug)
1151
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}/registrants/bulkInsert"
1152
+ params = {}
1153
+ if current is not None:
1154
+ params["current"] = current
1155
+ if host_email is not None:
1156
+ params["hostEmail"] = host_email
1157
+ if json_body:
1158
+ body = json.loads(json_body)
1159
+ else:
1160
+ body = {}
1161
+ try:
1162
+ result = api.session.rest_post(url, json=body, params=params)
1163
+ except WebexError as e:
1164
+ handle_rest_error(e)
1165
+ if output == "json":
1166
+ print_json(result)
1167
+ elif isinstance(result, dict) and "id" in result:
1168
+ typer.echo(f"Created: {result['id']}")
1169
+ elif not result or result == {}:
1170
+ typer.echo("Created.")
1171
+ else:
1172
+ print_json(result)
1173
+
1174
+
1175
+
1176
+ @app.command("show-registrants")
1177
+ def show_registrants(
1178
+ meeting_id: str = typer.Argument(help="meetingId"),
1179
+ registrant_id: str = typer.Argument(help="registrantId"),
1180
+ current: str = typer.Option(None, "--current", help="Whether or not to retrieve only the current scheduled meetin"),
1181
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This parameter is only u"),
1182
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1183
+ debug: bool = typer.Option(False, "--debug"),
1184
+ ):
1185
+ """Get Detailed Information for a Meeting Registrant."""
1186
+ api = get_api(debug=debug)
1187
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}/registrants/{registrant_id}"
1188
+ params = {}
1189
+ if current is not None:
1190
+ params["current"] = current
1191
+ if host_email is not None:
1192
+ params["hostEmail"] = host_email
1193
+ try:
1194
+ result = api.session.rest_get(url, params=params)
1195
+ except WebexError as e:
1196
+ handle_rest_error(e)
1197
+ if output == "json":
1198
+ print_json(result)
1199
+ else:
1200
+ if isinstance(result, dict):
1201
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1202
+ elif isinstance(result, list):
1203
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1204
+ else:
1205
+ print_json(result)
1206
+
1207
+
1208
+
1209
+ @app.command("delete-registrants")
1210
+ def delete_registrants(
1211
+ meeting_id: str = typer.Argument(help="meetingId"),
1212
+ registrant_id: str = typer.Argument(help="registrantId"),
1213
+ current: str = typer.Option(None, "--current", help="Whether or not to retrieve only the current scheduled meetin"),
1214
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This parameter is only u"),
1215
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
1216
+ debug: bool = typer.Option(False, "--debug"),
1217
+ ):
1218
+ """Delete a Meeting Registrant."""
1219
+ if not force:
1220
+ typer.confirm(f"Delete {registrant_id}?", abort=True)
1221
+ api = get_api(debug=debug)
1222
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}/registrants/{registrant_id}"
1223
+ params = {}
1224
+ if current is not None:
1225
+ params["current"] = current
1226
+ if host_email is not None:
1227
+ params["hostEmail"] = host_email
1228
+ try:
1229
+ api.session.rest_delete(url, params=params)
1230
+ except WebexError as e:
1231
+ handle_rest_error(e)
1232
+ typer.echo(f"Deleted: {registrant_id}")
1233
+
1234
+
1235
+
1236
+ @app.command("create-query")
1237
+ def create_query(
1238
+ meeting_id: str = typer.Argument(help="meetingId"),
1239
+ max: str = typer.Option(None, "--max", help="Limit the maximum number of registrants in the response, up"),
1240
+ current: str = typer.Option(None, "--current", help="Whether or not to retrieve only the current scheduled meetin"),
1241
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This parameter is only u"),
1242
+ status: str = typer.Option(None, "--status", help="Choices: approved, pending, rejected"),
1243
+ order_type: str = typer.Option(None, "--order-type", help="Choices: DESC, ASC"),
1244
+ order_by: str = typer.Option(None, "--order-by", help="Choices: firstName, lastName, status, email"),
1245
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1246
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
1247
+ debug: bool = typer.Option(False, "--debug"),
1248
+ ):
1249
+ """Query Meeting Registrants\n\nExample --json-body:\n '{"emails":["..."],"status":"approved","orderType":"DESC","orderBy":"firstName"}'."""
1250
+ api = get_api(debug=debug)
1251
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}/registrants/query"
1252
+ params = {}
1253
+ if max is not None:
1254
+ params["max"] = max
1255
+ if current is not None:
1256
+ params["current"] = current
1257
+ if host_email is not None:
1258
+ params["hostEmail"] = host_email
1259
+ if json_body:
1260
+ body = json.loads(json_body)
1261
+ else:
1262
+ body = {}
1263
+ if status is not None:
1264
+ body["status"] = status
1265
+ if order_type is not None:
1266
+ body["orderType"] = order_type
1267
+ if order_by is not None:
1268
+ body["orderBy"] = order_by
1269
+ try:
1270
+ result = api.session.rest_post(url, json=body, params=params)
1271
+ except WebexError as e:
1272
+ handle_rest_error(e)
1273
+ if output == "json":
1274
+ print_json(result)
1275
+ elif isinstance(result, dict) and "id" in result:
1276
+ typer.echo(f"Created: {result['id']}")
1277
+ elif not result or result == {}:
1278
+ typer.echo("Created.")
1279
+ else:
1280
+ print_json(result)
1281
+
1282
+
1283
+
1284
+ @app.command("create-approve")
1285
+ def create_approve(
1286
+ meeting_id: str = typer.Argument(help="meetingId"),
1287
+ current: str = typer.Option(None, "--current", help="Whether or not to retrieve only the current scheduled meetin"),
1288
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This parameter is only u"),
1289
+ send_email: bool = typer.Option(None, "--send-email/--no-send-email", help="If `true` send email to registrants. Default: `true`."),
1290
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1291
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
1292
+ debug: bool = typer.Option(False, "--debug"),
1293
+ ):
1294
+ """Batch Approve Meeting Registrants\n\nExample --json-body:\n '{"sendEmail":true,"registrants":[{"id":"..."}]}'."""
1295
+ api = get_api(debug=debug)
1296
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}/registrants/approve"
1297
+ params = {}
1298
+ if current is not None:
1299
+ params["current"] = current
1300
+ if host_email is not None:
1301
+ params["hostEmail"] = host_email
1302
+ if json_body:
1303
+ body = json.loads(json_body)
1304
+ else:
1305
+ body = {}
1306
+ if send_email is not None:
1307
+ body["sendEmail"] = send_email
1308
+ try:
1309
+ result = api.session.rest_post(url, json=body, params=params)
1310
+ except WebexError as e:
1311
+ handle_rest_error(e)
1312
+ if output == "json":
1313
+ print_json(result)
1314
+ elif isinstance(result, dict) and "id" in result:
1315
+ typer.echo(f"Created: {result['id']}")
1316
+ elif not result or result == {}:
1317
+ typer.echo("Created.")
1318
+ else:
1319
+ print_json(result)
1320
+
1321
+
1322
+
1323
+ @app.command("create-reject")
1324
+ def create_reject(
1325
+ meeting_id: str = typer.Argument(help="meetingId"),
1326
+ current: str = typer.Option(None, "--current", help="Whether or not to retrieve only the current scheduled meetin"),
1327
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This parameter is only u"),
1328
+ send_email: bool = typer.Option(None, "--send-email/--no-send-email", help="If `true` send email to registrants. Default: `true`."),
1329
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1330
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
1331
+ debug: bool = typer.Option(False, "--debug"),
1332
+ ):
1333
+ """Batch Reject Meeting Registrants\n\nExample --json-body:\n '{"sendEmail":true,"registrants":[{"id":"..."}]}'."""
1334
+ api = get_api(debug=debug)
1335
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}/registrants/reject"
1336
+ params = {}
1337
+ if current is not None:
1338
+ params["current"] = current
1339
+ if host_email is not None:
1340
+ params["hostEmail"] = host_email
1341
+ if json_body:
1342
+ body = json.loads(json_body)
1343
+ else:
1344
+ body = {}
1345
+ if send_email is not None:
1346
+ body["sendEmail"] = send_email
1347
+ try:
1348
+ result = api.session.rest_post(url, json=body, params=params)
1349
+ except WebexError as e:
1350
+ handle_rest_error(e)
1351
+ if output == "json":
1352
+ print_json(result)
1353
+ elif isinstance(result, dict) and "id" in result:
1354
+ typer.echo(f"Created: {result['id']}")
1355
+ elif not result or result == {}:
1356
+ typer.echo("Created.")
1357
+ else:
1358
+ print_json(result)
1359
+
1360
+
1361
+
1362
+ @app.command("create-cancel")
1363
+ def create_cancel(
1364
+ meeting_id: str = typer.Argument(help="meetingId"),
1365
+ current: str = typer.Option(None, "--current", help="Whether or not to retrieve only the current scheduled meetin"),
1366
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This parameter is only u"),
1367
+ send_email: bool = typer.Option(None, "--send-email/--no-send-email", help="If `true` send email to registrants. Default: `true`."),
1368
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1369
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
1370
+ debug: bool = typer.Option(False, "--debug"),
1371
+ ):
1372
+ """Batch Cancel Meeting Registrants\n\nExample --json-body:\n '{"sendEmail":true,"registrants":[{"id":"..."}]}'."""
1373
+ api = get_api(debug=debug)
1374
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}/registrants/cancel"
1375
+ params = {}
1376
+ if current is not None:
1377
+ params["current"] = current
1378
+ if host_email is not None:
1379
+ params["hostEmail"] = host_email
1380
+ if json_body:
1381
+ body = json.loads(json_body)
1382
+ else:
1383
+ body = {}
1384
+ if send_email is not None:
1385
+ body["sendEmail"] = send_email
1386
+ try:
1387
+ result = api.session.rest_post(url, json=body, params=params)
1388
+ except WebexError as e:
1389
+ handle_rest_error(e)
1390
+ if output == "json":
1391
+ print_json(result)
1392
+ elif isinstance(result, dict) and "id" in result:
1393
+ typer.echo(f"Created: {result['id']}")
1394
+ elif not result or result == {}:
1395
+ typer.echo("Created.")
1396
+ else:
1397
+ print_json(result)
1398
+
1399
+
1400
+
1401
+ @app.command("create-bulk-delete")
1402
+ def create_bulk_delete(
1403
+ meeting_id: str = typer.Argument(help="meetingId"),
1404
+ current: str = typer.Option(None, "--current", help="Whether or not to retrieve only the current scheduled meetin"),
1405
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This parameter is only u"),
1406
+ send_email: bool = typer.Option(None, "--send-email/--no-send-email", help="If `true` send email to registrants. Default: `true`."),
1407
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1408
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
1409
+ debug: bool = typer.Option(False, "--debug"),
1410
+ ):
1411
+ """Batch Delete Meeting Registrants\n\nExample --json-body:\n '{"sendEmail":true,"registrants":[{"id":"..."}]}'."""
1412
+ api = get_api(debug=debug)
1413
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}/registrants/bulkDelete"
1414
+ params = {}
1415
+ if current is not None:
1416
+ params["current"] = current
1417
+ if host_email is not None:
1418
+ params["hostEmail"] = host_email
1419
+ if json_body:
1420
+ body = json.loads(json_body)
1421
+ else:
1422
+ body = {}
1423
+ if send_email is not None:
1424
+ body["sendEmail"] = send_email
1425
+ try:
1426
+ result = api.session.rest_post(url, json=body, params=params)
1427
+ except WebexError as e:
1428
+ handle_rest_error(e)
1429
+ if output == "json":
1430
+ print_json(result)
1431
+ elif isinstance(result, dict) and "id" in result:
1432
+ typer.echo(f"Created: {result['id']}")
1433
+ elif not result or result == {}:
1434
+ typer.echo("Created.")
1435
+ else:
1436
+ print_json(result)
1437
+
1438
+
1439
+
1440
+ @app.command("update-simultaneous-interpretation")
1441
+ def update_simultaneous_interpretation(
1442
+ meeting_id: str = typer.Argument(help="meetingId"),
1443
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="Whether or not simultaneous interpretation is enabled."),
1444
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1445
+ debug: bool = typer.Option(False, "--debug"),
1446
+ ):
1447
+ """Update Meeting Simultaneous interpretation\n\nExample --json-body:\n '{"enabled":true,"interpreters":[{"languageCode1":"...","languageCode2":"...","email":"...","displayName":"..."}]}'."""
1448
+ api = get_api(debug=debug)
1449
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}/simultaneousInterpretation"
1450
+ if json_body:
1451
+ body = json.loads(json_body)
1452
+ else:
1453
+ body = {}
1454
+ if enabled is not None:
1455
+ body["enabled"] = enabled
1456
+ try:
1457
+ result = api.session.rest_put(url, json=body)
1458
+ except WebexError as e:
1459
+ handle_rest_error(e)
1460
+ typer.echo(f"Updated.")
1461
+
1462
+
1463
+
1464
+ @app.command("list-interpreters")
1465
+ def list_interpreters(
1466
+ meeting_id: str = typer.Argument(help="meetingId"),
1467
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This parameter is only u"),
1468
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1469
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1470
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1471
+ debug: bool = typer.Option(False, "--debug"),
1472
+ ):
1473
+ """List Meeting Interpreters."""
1474
+ api = get_api(debug=debug)
1475
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}/interpreters"
1476
+ params = {}
1477
+ if host_email is not None:
1478
+ params["hostEmail"] = host_email
1479
+ if limit > 0:
1480
+ params["max"] = limit
1481
+ if offset > 0:
1482
+ params["start"] = offset
1483
+ result = None
1484
+ try:
1485
+ result = api.session.rest_get(url, params=params)
1486
+ except WebexError as e:
1487
+ handle_rest_error(e)
1488
+ result = result or []
1489
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1490
+ if output == "json":
1491
+ print_json(items)
1492
+ else:
1493
+ print_table(items, columns=[('ID', 'id'), ('Title', 'title'), ('Start', 'start'), ('End', 'end'), ('State', 'state')], limit=limit)
1494
+
1495
+
1496
+
1497
+ @app.command("create-interpreters")
1498
+ def create_interpreters(
1499
+ meeting_id: str = typer.Argument(help="meetingId"),
1500
+ language_code1: str = typer.Option(None, "--language-code1", help="(required) The pair of `languageCode1` and `languageCode2` form a bi-di"),
1501
+ language_code2: str = typer.Option(None, "--language-code2", help="(required) The pair of `languageCode1` and `languageCode2` form a bi-di"),
1502
+ email: str = typer.Option(None, "--email", help="Email address of meeting interpreter. If not specified, an e"),
1503
+ display_name: str = typer.Option(None, "--display-name", help="Display name of meeting interpreter. If the interpreter is a"),
1504
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This attribute should on"),
1505
+ send_email: bool = typer.Option(None, "--send-email/--no-send-email", help="If `true`, send email to the interpreter."),
1506
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1507
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
1508
+ debug: bool = typer.Option(False, "--debug"),
1509
+ ):
1510
+ """Create a Meeting Interpreter."""
1511
+ api = get_api(debug=debug)
1512
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}/interpreters"
1513
+ if json_body:
1514
+ body = json.loads(json_body)
1515
+ else:
1516
+ body = {}
1517
+ if language_code1 is not None:
1518
+ body["languageCode1"] = language_code1
1519
+ if language_code2 is not None:
1520
+ body["languageCode2"] = language_code2
1521
+ if email is not None:
1522
+ body["email"] = email
1523
+ if display_name is not None:
1524
+ body["displayName"] = display_name
1525
+ if host_email is not None:
1526
+ body["hostEmail"] = host_email
1527
+ if send_email is not None:
1528
+ body["sendEmail"] = send_email
1529
+ _missing = [f for f in ['languageCode1', 'languageCode2'] if f not in body or body[f] is None]
1530
+ if _missing:
1531
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
1532
+ raise typer.Exit(1)
1533
+ try:
1534
+ result = api.session.rest_post(url, json=body)
1535
+ except WebexError as e:
1536
+ handle_rest_error(e)
1537
+ if output == "json":
1538
+ print_json(result)
1539
+ elif isinstance(result, dict) and "id" in result:
1540
+ typer.echo(f"Created: {result['id']}")
1541
+ elif not result or result == {}:
1542
+ typer.echo("Created.")
1543
+ else:
1544
+ print_json(result)
1545
+
1546
+
1547
+
1548
+ @app.command("show-interpreters")
1549
+ def show_interpreters(
1550
+ meeting_id: str = typer.Argument(help="meetingId"),
1551
+ interpreter_id: str = typer.Argument(help="interpreterId"),
1552
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This parameter is only u"),
1553
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1554
+ debug: bool = typer.Option(False, "--debug"),
1555
+ ):
1556
+ """Get a Meeting Interpreter."""
1557
+ api = get_api(debug=debug)
1558
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}/interpreters/{interpreter_id}"
1559
+ params = {}
1560
+ if host_email is not None:
1561
+ params["hostEmail"] = host_email
1562
+ try:
1563
+ result = api.session.rest_get(url, params=params)
1564
+ except WebexError as e:
1565
+ handle_rest_error(e)
1566
+ if output == "json":
1567
+ print_json(result)
1568
+ else:
1569
+ if isinstance(result, dict):
1570
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1571
+ elif isinstance(result, list):
1572
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1573
+ else:
1574
+ print_json(result)
1575
+
1576
+
1577
+
1578
+ @app.command("update-interpreters")
1579
+ def update_interpreters(
1580
+ meeting_id: str = typer.Argument(help="meetingId"),
1581
+ interpreter_id: str = typer.Argument(help="interpreterId"),
1582
+ language_code1: str = typer.Option(None, "--language-code1", help="The pair of `languageCode1` and `languageCode2` form a bi-di"),
1583
+ language_code2: str = typer.Option(None, "--language-code2", help="The pair of `languageCode1` and `languageCode2` form a bi-di"),
1584
+ email: str = typer.Option(None, "--email", help="Email address of meeting interpreter. If not specified, it'l"),
1585
+ display_name: str = typer.Option(None, "--display-name", help="Display name of meeting interpreter. If the interpreter is a"),
1586
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This attribute should on"),
1587
+ send_email: bool = typer.Option(None, "--send-email/--no-send-email", help="If `true`, send email to the interpreter."),
1588
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1589
+ debug: bool = typer.Option(False, "--debug"),
1590
+ ):
1591
+ """Update a Meeting Interpreter."""
1592
+ api = get_api(debug=debug)
1593
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}/interpreters/{interpreter_id}"
1594
+ if json_body:
1595
+ body = json.loads(json_body)
1596
+ else:
1597
+ body = {}
1598
+ if language_code1 is not None:
1599
+ body["languageCode1"] = language_code1
1600
+ if language_code2 is not None:
1601
+ body["languageCode2"] = language_code2
1602
+ if email is not None:
1603
+ body["email"] = email
1604
+ if display_name is not None:
1605
+ body["displayName"] = display_name
1606
+ if host_email is not None:
1607
+ body["hostEmail"] = host_email
1608
+ if send_email is not None:
1609
+ body["sendEmail"] = send_email
1610
+ try:
1611
+ result = api.session.rest_put(url, json=body)
1612
+ except WebexError as e:
1613
+ handle_rest_error(e)
1614
+ typer.echo(f"Updated.")
1615
+
1616
+
1617
+
1618
+ @app.command("delete-interpreters")
1619
+ def delete_interpreters(
1620
+ meeting_id: str = typer.Argument(help="meetingId"),
1621
+ interpreter_id: str = typer.Argument(help="interpreterId"),
1622
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This parameter is only u"),
1623
+ send_email: str = typer.Option(None, "--send-email", help="If `true`, send email to the interpreter."),
1624
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
1625
+ debug: bool = typer.Option(False, "--debug"),
1626
+ ):
1627
+ """Delete a Meeting Interpreter."""
1628
+ if not force:
1629
+ typer.confirm(f"Delete {interpreter_id}?", abort=True)
1630
+ api = get_api(debug=debug)
1631
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}/interpreters/{interpreter_id}"
1632
+ params = {}
1633
+ if host_email is not None:
1634
+ params["hostEmail"] = host_email
1635
+ if send_email is not None:
1636
+ params["sendEmail"] = send_email
1637
+ try:
1638
+ api.session.rest_delete(url, params=params)
1639
+ except WebexError as e:
1640
+ handle_rest_error(e)
1641
+ typer.echo(f"Deleted: {interpreter_id}")
1642
+
1643
+
1644
+
1645
+ @app.command("list-breakout-sessions")
1646
+ def list_breakout_sessions(
1647
+ meeting_id: str = typer.Argument(help="meetingId"),
1648
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1649
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1650
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1651
+ debug: bool = typer.Option(False, "--debug"),
1652
+ ):
1653
+ """List Meeting Breakout Sessions."""
1654
+ api = get_api(debug=debug)
1655
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}/breakoutSessions"
1656
+ params = {}
1657
+ if limit > 0:
1658
+ params["max"] = limit
1659
+ if offset > 0:
1660
+ params["start"] = offset
1661
+ result = None
1662
+ try:
1663
+ result = api.session.rest_get(url, params=params)
1664
+ except WebexError as e:
1665
+ handle_rest_error(e)
1666
+ result = result or []
1667
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1668
+ if output == "json":
1669
+ print_json(items)
1670
+ else:
1671
+ print_table(items, columns=[('ID', 'id'), ('Title', 'title'), ('Start', 'start'), ('End', 'end'), ('State', 'state')], limit=limit)
1672
+
1673
+
1674
+
1675
+ @app.command("update-breakout-sessions")
1676
+ def update_breakout_sessions(
1677
+ meeting_id: str = typer.Argument(help="meetingId"),
1678
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This parameter is only u"),
1679
+ send_email: bool = typer.Option(None, "--send-email/--no-send-email", help="Whether or not to send emails to host and invitees. It is an"),
1680
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1681
+ debug: bool = typer.Option(False, "--debug"),
1682
+ ):
1683
+ """Update Meeting Breakout Sessions\n\nExample --json-body:\n '{"hostEmail":"...","sendEmail":true,"items":[{"name":"...","invitees":"..."}]}'."""
1684
+ api = get_api(debug=debug)
1685
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}/breakoutSessions"
1686
+ if json_body:
1687
+ body = json.loads(json_body)
1688
+ else:
1689
+ body = {}
1690
+ if host_email is not None:
1691
+ body["hostEmail"] = host_email
1692
+ if send_email is not None:
1693
+ body["sendEmail"] = send_email
1694
+ try:
1695
+ result = api.session.rest_put(url, json=body)
1696
+ except WebexError as e:
1697
+ handle_rest_error(e)
1698
+ typer.echo(f"Updated.")
1699
+
1700
+
1701
+
1702
+ @app.command("delete-breakout-sessions")
1703
+ def delete_breakout_sessions(
1704
+ meeting_id: str = typer.Argument(help="meetingId"),
1705
+ send_email: str = typer.Option(None, "--send-email", help="Whether or not to send emails to host and invitees. It is an"),
1706
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
1707
+ debug: bool = typer.Option(False, "--debug"),
1708
+ ):
1709
+ """Delete Meeting Breakout Sessions."""
1710
+ if not force:
1711
+ typer.confirm(f"Delete {meeting_id}?", abort=True)
1712
+ api = get_api(debug=debug)
1713
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}/breakoutSessions"
1714
+ params = {}
1715
+ if send_email is not None:
1716
+ params["sendEmail"] = send_email
1717
+ try:
1718
+ api.session.rest_delete(url, params=params)
1719
+ except WebexError as e:
1720
+ handle_rest_error(e)
1721
+ typer.echo(f"Deleted: {meeting_id}")
1722
+
1723
+
1724
+
1725
+ @app.command("list-survey")
1726
+ def list_survey(
1727
+ meeting_id: str = typer.Argument(help="meetingId"),
1728
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1729
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1730
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1731
+ debug: bool = typer.Option(False, "--debug"),
1732
+ ):
1733
+ """Get a Meeting Survey."""
1734
+ api = get_api(debug=debug)
1735
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}/survey"
1736
+ params = {}
1737
+ if limit > 0:
1738
+ params["max"] = limit
1739
+ if offset > 0:
1740
+ params["start"] = offset
1741
+ result = None
1742
+ try:
1743
+ result = api.session.rest_get(url, params=params)
1744
+ except WebexError as e:
1745
+ handle_rest_error(e)
1746
+ result = result or []
1747
+ items = result.get("questions", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1748
+ if output == "json":
1749
+ print_json(items)
1750
+ else:
1751
+ print_table(items, columns=[('ID', 'id'), ('Title', 'title'), ('Start', 'start'), ('End', 'end'), ('State', 'state')], limit=limit)
1752
+
1753
+
1754
+
1755
+ @app.command("list-survey-results")
1756
+ def list_survey_results(
1757
+ meeting_id: str = typer.Argument(help="meetingId"),
1758
+ meeting_start_time_from: str = typer.Option(None, "--meeting-start-time-from", help="Start date and time (inclusive) in any [ISO 8601](https://en"),
1759
+ meeting_start_time_to: str = typer.Option(None, "--meeting-start-time-to", help="End date and time (exclusive) in any [ISO 8601](https://en.w"),
1760
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1761
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1762
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1763
+ debug: bool = typer.Option(False, "--debug"),
1764
+ ):
1765
+ """List Meeting Survey Results."""
1766
+ api = get_api(debug=debug)
1767
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}/surveyResults"
1768
+ params = {}
1769
+ if meeting_start_time_from is not None:
1770
+ params["meetingStartTimeFrom"] = meeting_start_time_from
1771
+ if meeting_start_time_to is not None:
1772
+ params["meetingStartTimeTo"] = meeting_start_time_to
1773
+ if limit > 0:
1774
+ params["max"] = limit
1775
+ if offset > 0:
1776
+ params["start"] = offset
1777
+ result = None
1778
+ try:
1779
+ result = api.session.rest_get(url, params=params)
1780
+ except WebexError as e:
1781
+ handle_rest_error(e)
1782
+ result = result or []
1783
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1784
+ if output == "json":
1785
+ print_json(items)
1786
+ else:
1787
+ print_table(items, columns=[('ID', 'id'), ('Title', 'title'), ('Start', 'start'), ('End', 'end'), ('State', 'state')], limit=limit)
1788
+
1789
+
1790
+
1791
+ @app.command("create-survey-links")
1792
+ def create_survey_links(
1793
+ meeting_id: str = typer.Argument(help="meetingId"),
1794
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This parameter is only u"),
1795
+ meeting_start_time_from: str = typer.Option(None, "--meeting-start-time-from", help="Start date and time (inclusive) in any [ISO 8601](https://en"),
1796
+ meeting_start_time_to: str = typer.Option(None, "--meeting-start-time-to", help="End date and time (exclusive) in any [ISO 8601](https://en.w"),
1797
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1798
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
1799
+ debug: bool = typer.Option(False, "--debug"),
1800
+ ):
1801
+ """Get Meeting Survey Links\n\nExample --json-body:\n '{"hostEmail":"...","meetingStartTimeFrom":"...","meetingStartTimeTo":"...","emails":["..."]}'."""
1802
+ api = get_api(debug=debug)
1803
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}/surveyLinks"
1804
+ if json_body:
1805
+ body = json.loads(json_body)
1806
+ else:
1807
+ body = {}
1808
+ if host_email is not None:
1809
+ body["hostEmail"] = host_email
1810
+ if meeting_start_time_from is not None:
1811
+ body["meetingStartTimeFrom"] = meeting_start_time_from
1812
+ if meeting_start_time_to is not None:
1813
+ body["meetingStartTimeTo"] = meeting_start_time_to
1814
+ try:
1815
+ result = api.session.rest_post(url, json=body)
1816
+ except WebexError as e:
1817
+ handle_rest_error(e)
1818
+ if output == "json":
1819
+ print_json(result)
1820
+ elif isinstance(result, dict) and "id" in result:
1821
+ typer.echo(f"Created: {result['id']}")
1822
+ elif not result or result == {}:
1823
+ typer.echo("Created.")
1824
+ else:
1825
+ print_json(result)
1826
+
1827
+
1828
+
1829
+ @app.command("list-invitation-sources")
1830
+ def list_invitation_sources(
1831
+ meeting_id: str = typer.Argument(help="meetingId"),
1832
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1833
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1834
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1835
+ debug: bool = typer.Option(False, "--debug"),
1836
+ ):
1837
+ """List Invitation Sources."""
1838
+ api = get_api(debug=debug)
1839
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}/invitationSources"
1840
+ params = {}
1841
+ if limit > 0:
1842
+ params["max"] = limit
1843
+ if offset > 0:
1844
+ params["start"] = offset
1845
+ result = None
1846
+ try:
1847
+ result = api.session.rest_get(url, params=params)
1848
+ except WebexError as e:
1849
+ handle_rest_error(e)
1850
+ result = result or []
1851
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1852
+ if output == "json":
1853
+ print_json(items)
1854
+ else:
1855
+ print_table(items, columns=[('ID', 'id'), ('Title', 'title'), ('Start', 'start'), ('End', 'end'), ('State', 'state')], limit=limit)
1856
+
1857
+
1858
+
1859
+ @app.command("create-invitation-sources")
1860
+ def create_invitation_sources(
1861
+ meeting_id: str = typer.Argument(help="meetingId"),
1862
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This parameter is only u"),
1863
+ person_id: str = typer.Option(None, "--person-id", help="Unique identifier for the meeting host. Should only be set i"),
1864
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1865
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
1866
+ debug: bool = typer.Option(False, "--debug"),
1867
+ ):
1868
+ """Create Invitation Sources\n\nExample --json-body:\n '{"hostEmail":"...","personId":"...","items":[{"sourceId":"...","sourceEmail":"..."}]}'."""
1869
+ api = get_api(debug=debug)
1870
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}/invitationSources"
1871
+ if json_body:
1872
+ body = json.loads(json_body)
1873
+ else:
1874
+ body = {}
1875
+ if host_email is not None:
1876
+ body["hostEmail"] = host_email
1877
+ if person_id is not None:
1878
+ body["personId"] = person_id
1879
+ try:
1880
+ result = api.session.rest_post(url, json=body)
1881
+ except WebexError as e:
1882
+ handle_rest_error(e)
1883
+ if output == "json":
1884
+ print_json(result)
1885
+ elif isinstance(result, dict) and "id" in result:
1886
+ typer.echo(f"Created: {result['id']}")
1887
+ elif not result or result == {}:
1888
+ typer.echo("Created.")
1889
+ else:
1890
+ print_json(result)
1891
+
1892
+
1893
+
1894
+ @app.command("list-tracking-codes")
1895
+ def list_tracking_codes(
1896
+ site_url: str = typer.Option(None, "--site-url", help="URL of the Webex site which the API retrieves the tracking c"),
1897
+ service: str = typer.Option(..., "--service", help="Service for schedule or sign-up pages."),
1898
+ host_email: str = typer.Option(None, "--host-email", help="Email address for the meeting host. This parameter is only u"),
1899
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1900
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1901
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1902
+ debug: bool = typer.Option(False, "--debug"),
1903
+ ):
1904
+ """List Meeting Tracking Codes."""
1905
+ api = get_api(debug=debug)
1906
+ url = f"https://webexapis.com/v1/meetings/trackingCodes"
1907
+ params = {}
1908
+ if site_url is not None:
1909
+ params["siteUrl"] = site_url
1910
+ if service is not None:
1911
+ params["service"] = service
1912
+ if host_email is not None:
1913
+ params["hostEmail"] = host_email
1914
+ if limit > 0:
1915
+ params["max"] = limit
1916
+ if offset > 0:
1917
+ params["start"] = offset
1918
+ result = None
1919
+ try:
1920
+ result = api.session.rest_get(url, params=params)
1921
+ except WebexError as e:
1922
+ handle_rest_error(e)
1923
+ result = result or []
1924
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1925
+ if output == "json":
1926
+ print_json(items)
1927
+ else:
1928
+ print_table(items, columns=[('ID', 'id'), ('Title', 'title'), ('Start', 'start'), ('End', 'end'), ('State', 'state')], limit=limit)
1929
+
1930
+
1931
+
1932
+ @app.command("create-reassign-host")
1933
+ def create_reassign_host(
1934
+ host_email: str = typer.Option(None, "--host-email", help="(required) Email address of the new meeting host."),
1935
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1936
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
1937
+ debug: bool = typer.Option(False, "--debug"),
1938
+ ):
1939
+ """Reassign Meetings to a New Host\n\nExample --json-body:\n '{"hostEmail":"...","meetingIds":["..."]}'."""
1940
+ api = get_api(debug=debug)
1941
+ url = f"https://webexapis.com/v1/meetings/reassignHost"
1942
+ if json_body:
1943
+ body = json.loads(json_body)
1944
+ else:
1945
+ body = {}
1946
+ if host_email is not None:
1947
+ body["hostEmail"] = host_email
1948
+ _missing = [f for f in ['hostEmail'] if f not in body or body[f] is None]
1949
+ if _missing:
1950
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
1951
+ raise typer.Exit(1)
1952
+ try:
1953
+ result = api.session.rest_post(url, json=body)
1954
+ except WebexError as e:
1955
+ handle_rest_error(e)
1956
+ if output == "json":
1957
+ print_json(result)
1958
+ elif isinstance(result, dict) and "id" in result:
1959
+ typer.echo(f"Created: {result['id']}")
1960
+ elif not result or result == {}:
1961
+ typer.echo("Created.")
1962
+ else:
1963
+ print_json(result)
1964
+
1965
+
1966
+
1967
+ @app.command("create-end")
1968
+ def create_end(
1969
+ meeting_id: str = typer.Argument(help="meetingId"),
1970
+ reason: str = typer.Option(None, "--reason", help="The reason for ending the meeting. This field is optional."),
1971
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1972
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
1973
+ debug: bool = typer.Option(False, "--debug"),
1974
+ ):
1975
+ """End a Meeting."""
1976
+ api = get_api(debug=debug)
1977
+ url = f"https://webexapis.com/v1/meetings/{meeting_id}/end"
1978
+ if json_body:
1979
+ body = json.loads(json_body)
1980
+ else:
1981
+ body = {}
1982
+ if reason is not None:
1983
+ body["reason"] = reason
1984
+ try:
1985
+ result = api.session.rest_post(url, json=body)
1986
+ except WebexError as e:
1987
+ handle_rest_error(e)
1988
+ if output == "json":
1989
+ print_json(result)
1990
+ elif isinstance(result, dict) and "id" in result:
1991
+ typer.echo(f"Created: {result['id']}")
1992
+ elif not result or result == {}:
1993
+ typer.echo("Created.")
1994
+ else:
1995
+ print_json(result)
1996
+
1997
+