Ejemplo n.º 1
0
def update_host_key(key: str):
    """Update the host key of the speakers' corner user for the upcoming hour."""
    logging.info("Updated the host key.")
    zoom_request(
        requests.patch,
        common.ZOOM_API + "users/" + common.SPEAKERS_CORNER_USER_ID,
        data=json.dumps({"host_key": key})
    )
Ejemplo n.º 2
0
def update_host_key():
    """Update the host key of the speakers' corner user for the upcoming hour."""
    logging.info("Updated the host key.")
    zoom_request(requests.patch,
                 common.ZOOM_API + "users/" + common.SPEAKERS_CORNER_USER_ID,
                 data=json.dumps({
                     "host_key":
                     host_key(
                         datetime.datetime.now(tz=pytz.UTC) +
                         datetime.timedelta(hours=1))
                 }))
Ejemplo n.º 3
0
def register_speaker(meeting_id, talk):
    # The splitting is approximate, and is done merely to satisfy the Zoom
    # registration requirements
    first_name, last_name = talk["speaker_name"].split(maxsplit=1) 
    request_payload = {
        "email": talk["email"],
        "first_name": first_name,
        "last_name": last_name,
        "org": talk["speaker_affiliation"],
        "custom_questions": [
            {
                "title": (
                    "Please confirm you agree to follow the participant instructions: "
                    "http://virtualscienceforum.org/#/attendeeguide"
                ),
                "value": "Yes",
            }
        ]
    }

    # Send request
    response = common.zoom_request(
        requests.post,
        f"{common.ZOOM_API}meetings/{meeting_id}/registrants",
        data=json.dumps(request_payload)
    )

    return response
Ejemplo n.º 4
0
def email_video_link(talk):
    """Send the presenter a link to their video, asking to confirm."""
    meeting_recordings = common.zoom_request(
        requests.get,
        common.ZOOM_API + f"/meetings/{talk['zoom_meeting_id']}/recordings")
    if not len(meeting_recordings["recording_files"]):
        raise RuntimeError("No recordings found")

    message = RECORDING_AVAILABLE_TEMPLATE.render(
        share_url=meeting_recordings["share_url"],
        **talk,
    )

    response = common.api_query(
        requests.post,
        common.MAILGUN_DOMAIN + "messages",
        data={
            "from": "VSF team <*****@*****.**>",
            "to": f"{talk['speaker_name']} <{talk['email']}>",
            "subject": "Approve your Speakers' Corner recording",
            "text": common.markdown_to_plain(message),
            "html": common.markdown_to_email(message),
        })
    logging.info(
        f"Notified the speaker of {talk['zoom_meeting_id']} about recording.")
    return response
Ejemplo n.º 5
0
def schedule_zoom_talk(talk) -> Tuple[str, str]:
    # Form the talk registration body
    request_body = {
        "topic": "Speakers\' corner talk by %s"%(talk["speaker_name"]),
        "type": 2, # Scheduled meeting
        "start_time": talk["time"].strftime('%Y-%m-%dT%H:%M:%S'),
        "timezone": "UTC",
        "duration": 60,
        "schedule_for": common.SPEAKERS_CORNER_USER_ID,

        # Generate a password for the meeting. This is required since
        # otherwise the meeting room will be forced. Zoom limits the
        # password length to max 10 characters.
        "password": secrets.token_urlsafe(16)[:10],

        # Meeting settings
        "settings": {
            "host_video": True,
            "participant_video": False,
            "cn_meeting": False,  # Host the meeting in China?
            "in_meeting": False,  # Host the meeting in India?

            # This will be switched to True shortly before the meeting starts
            # by the VSF bot. It will also be switched back to False afterwards
            "join_before_host": False,
            "mute_upon_entry": True,
            "watermark": False,  # Don't add a watermark when screensharing
            "use_pmi": False, # Don't use Personal Meeting ID, but generate one
            "approval_type": 0, # Automatically approve
            "close_registration" : True, # Close registration after event date
            "waiting_room" : False,    # No waiting room
            "audio": "both",
            "auto_recording": "cloud",
            "enforce_login": False,
            "alternative_hosts": "",

            # Email notifications are turned off when created, so that we can
            # register the speaker without them receiving an invitation to
            # their own talk. They will receive a separate email with info.
            # This will be turned on with a PATCH once the speaker is registered.
            "registrants_email_notification": False,
            "contact_email": "*****@*****.**",
        }
    }

    # Create the meeting
    response = common.zoom_request(
        requests.post,
        f"{common.ZOOM_API}users/{common.SPEAKERS_CORNER_USER_ID}/meetings",
        data=json.dumps(request_body)
    )

    meeting_id = response["id"]

    patch_registration_questions(meeting_id)
    speaker_join_url = register_speaker(meeting_id, talk)["join_url"]
    patch_registration_notification(meeting_id)

    return meeting_id, response["registration_url"], speaker_join_url
Ejemplo n.º 6
0
def patch_registration_questions(meeting_id):
    response = common.zoom_request(
        requests.patch,
        f"{common.ZOOM_API}meetings/{meeting_id}/registrants/questions",
        data=json.dumps(REGISTRATION_QUESTIONS)
    )

    return response
Ejemplo n.º 7
0
def patch_registration_notification(meeting_id):

    # Form the talk registration body
    request_body = {
        "settings": {
            "registrants_email_notification": True,
        },
    }

    # Create the meeting
    response = common.zoom_request(
        requests.patch,
        f"{common.ZOOM_API}meetings/{meeting_id}",
        data=json.dumps(request_body)
    )

    return response
Ejemplo n.º 8
0
def rotate_meetings():
    """Update the Speakers' corner meeting settings and statuses.

    1. Stop a running meeting if it runs for too long.
    2. Disable joining before host on recent meetings to prevent restarting.
    3. If there is an upcoming meeting in less than an hour, allow joining
       before host.
    """
    now = datetime.datetime.now(tz=pytz.UTC)
    hour = datetime.timedelta(hours=1)
    sc_meetings = common.all_meetings(common.SPEAKERS_CORNER_USER_ID)
    for m in sc_meetings:
        m["start_time"] = parse(m["start_time"])

    for recent in (
        m for m in sc_meetings
        if now - hour > m["start_time"] > now - 2*hour
    ):
        recent_id = recent["id"]
        if recent.get("live"):
            common.zoom_request(
                requests.put,
                f"{common.ZOOM_API}meetings/{recent_id}/status",
                json={"action": "end"},
            )
            logging.info(f"Stopped {recent_id}.")

        common.zoom_request(
            requests.patch,
            f"{common.ZOOM_API}meetings/{recent_id}",
            json={"settings": {"join_before_host": False}},
        )
        logging.info(f"Disabled joining {recent_id}.")


    for upcoming in (
        m for m in sc_meetings
        if now + hour > m["start_time"] > now
    ):
        upcoming_id = upcoming['id']
        common.zoom_request(
            requests.patch,
            f"{common.ZOOM_API}meetings/{upcoming_id}",
            json={"settings": {"join_before_host": True}},
        )
        logging.info(f"Allowed joining {upcoming_id}.")

        update_host_key(host_key(upcoming_id))
Ejemplo n.º 9
0
def rotate_meetings():
    """Update the Speakers' corner meeting settings and statuses.

    1. If there is an upcoming meeting in less than an hour, allow joining
       before host.
    2. Stop the running meeting if there is an upcoming one or if it runs for too long.
    3. Disable joining before host on recent meetings to prevent restarting.
    """
    now = datetime.datetime.now(tz=pytz.UTC)
    sc_meetings = common.all_meetings(common.SPEAKERS_CORNER_USER_ID)
    for m in sc_meetings:
        m["start_time"] = parse(m["start_time"])

    live = [m for m in sc_meetings if m.get("live")]

    try:
        upcoming = min((m for m in sc_meetings if m["start_time"] > now),
                       key=(lambda meeting: meeting["start_time"]))
        upcoming_start = upcoming["start_time"]
    except ValueError:
        upcoming = None
        upcoming_start = now + datetime.timedelta(weeks=1)

    recent = [
        m for m in sc_meetings
        if (now > m["start_time"] > now -
            datetime.timedelta(hours=2)) and not m.get("live")
    ]

    starting_soon = upcoming_start - now < datetime.timedelta(hours=1)
    if starting_soon:
        common.zoom_request(
            requests.patch,
            f"{common.ZOOM_API}meetings/{upcoming['id']}",
            data=json.dumps({"settings": {
                "join_before_host": True
            }}),
        )
        logging.info(f"Allowed joining {upcoming['id']} before host.")

    running = bool(live)
    if (live and
        (starting_soon
         or live[0]["start_time"] < now - datetime.timedelta(minutes=90))):
        running = False
        for live_meeting in live:
            live_id = live_meeting["id"]
            common.zoom_request(
                requests.put,
                f"{common.ZOOM_API}meetings/{live_id}/status",
                data=json.dumps({"action": "end"}),
            )
            common.zoom_request(
                requests.patch,
                f"{common.ZOOM_API}meetings/{live_id}",
                data=json.dumps({"settings": {
                    "join_before_host": False
                }}),
            )
            logging.info(f"Stopped {live_id} and disabled joining.")

    for meeting in recent:
        common.zoom_request(
            requests.patch,
            f"{common.ZOOM_API}meetings/{meeting['id']}",
            data=json.dumps({"settings": {
                "join_before_host": False
            }}),
        )
        logging.info(f"Disabled joining {meeting['id']}")

    return running