diff --git a/vc_zoom/indico_vc_zoom/plugin.py b/vc_zoom/indico_vc_zoom/plugin.py index 10152089..04745077 100644 --- a/vc_zoom/indico_vc_zoom/plugin.py +++ b/vc_zoom/indico_vc_zoom/plugin.py @@ -895,6 +895,16 @@ def _get_registrant_email(self, registration): return enterprise_email.lower() return registration.email + def _get_zoom_host_emails(self, vc_room): + # Zoom rejects registering the meeting host or any pre-assigned alt host (API error 3027). + identifiers = [vc_room.data['host'], *(vc_room.data.get('alternative_hosts') or [])] + emails = set() + for ident in identifiers: + principal = principal_from_identifier(ident, require_user_token=False) + if principal and (email := find_enterprise_email(principal)): + emails.add(email.lower()) + return emails + def _get_user_registration(self, event, user): return (Registration.query.with_parent(event) .join(Registration.registration_form) @@ -1003,7 +1013,8 @@ def _collect_room_ops(self, pending): else: room_ops[zoom_id]['remove'][email] = registration.id for ops in room_ops.values(): - ops['add'] = list(ops['add'].values()) + skip = self._get_zoom_host_emails(ops['vc_room']) + ops['add'] = [e for e in ops['add'].values() if e['data']['email'].lower() not in skip] return room_ops def _has_other_active_room_registration(self, vc_room, email, exclude_ids): diff --git a/vc_zoom/tests/registration_test.py b/vc_zoom/tests/registration_test.py index 8c8595f0..a87104d0 100644 --- a/vc_zoom/tests/registration_test.py +++ b/vc_zoom/tests/registration_test.py @@ -728,3 +728,29 @@ def test_registration_summary_hides_zoom_link_if_not_registered_in_zoom( html = zoom_plugin._render_registration_zoom_link(registration, from_management=False) assert html == '' + + +@pytest.mark.usefixtures('smtp') +def test_batch_excludes_host_and_alt_host(db, zoom_plugin, zoom_api_registrants, reg_form, zoom_user, create_user): + """When the queue mixes host, alt host, and regular participants, only the regular ones reach Zoom.""" + alt_host = create_user(2, email='alt.host@megacorp.xyz') + event = reg_form.event + _make_complete_registration(db, zoom_plugin, reg_form, zoom_user.email, 'Don', 'Orange') + _make_complete_registration(db, zoom_plugin, reg_form, alt_host.email, 'Alt', 'Host') + _make_complete_registration(db, zoom_plugin, reg_form, 'alice@example.com', 'Alice', 'Smith') + _make_complete_registration(db, zoom_plugin, reg_form, 'bob@example.com', 'Bob', 'Jones') + + zoom_plugin.settings.set('allow_auto_register', True) + zoom_api_registrants['add_meeting_registrant'].reset_mock() + zoom_api_registrants['batch_meeting_registrants'].reset_mock() + + vc_room, assoc = _create_vc_room_with_assoc(db, event, zoom_user) + vc_room.data['alternative_hosts'] = [alt_host.persistent_identifier] + signals.vc.vc_room_created.send(vc_room, event=event, assoc=assoc) + zoom_plugin._flush_pending_registrations(None) + + zoom_api_registrants['add_meeting_registrant'].assert_not_called() + assert zoom_api_registrants['batch_meeting_registrants'].call_count == 1 + batch_data = zoom_api_registrants['batch_meeting_registrants'].call_args[0][1] + batch_emails = {r['email'] for r in batch_data['registrants']} + assert batch_emails == {'alice@example.com', 'bob@example.com'}