1+ #!/usr/bin/env python
2+
3+ import os
4+ import hashlib
5+ import datetime
6+ import json
7+
8+ import requests
9+ import pytz
10+ from dateutil .parser import parse
11+
12+ import common
13+ from common import zoom_request
14+
15+
16+ def host_key (timeslot : datetime .datetime ) -> int :
17+ """Generate a host key for a specified time."""
18+ key_salt = os .getenv ("HOST_KEY_SALT" ).encode ()
19+ timestamp = timeslot .replace (second = 0 , microsecond = 0 , minute = 0 ).timestamp ()
20+ hashed = hashlib .sha512 (int (timestamp .to_bytes (5 , "big" )) + key_salt )
21+ return f"{ int (hashed .hexdigest (), 16 ) % int (1e7 ):06} "
22+
23+
24+ def update_host_key ():
25+ """Update the host key of the speakers' corner user for the upcoming hour."""
26+ zoom_request (
27+ requests .patch ,
28+ common .ZOOM_API + "users/" + common .SPEAKERS_CORNER_USER_ID ,
29+ data = json .dumps ({
30+ "host_key" : host_key (datetime .datetime .now () + datetime .timedelta (hours = 1 ))
31+ })
32+ )
33+
34+
35+ def rotate_meetings ():
36+ """Update the Speakers' corner meeting settings and statuses.
37+
38+ 1. If there is an upcoming meeting in less than an hour, allow joining
39+ before host.
40+ 2. Stop the running meeting if there is an upcoming one or if it runs for too long.
41+ 3. Disable joining before host on recent meetings to prevent restarting.
42+ """
43+ now = datetime .datetime .now (tz = pytz .UTC )
44+ sc_meetings = common .all_meetings (common .SPEAKERS_CORNER_USER_ID )
45+ for m in sc_meetings :
46+ m ["start_time" ] = parse (m ["start_time" ])
47+
48+ live = [m for m in sc_meetings if m ["live" ]]
49+
50+ try :
51+ upcoming = min (
52+ (m for m in sc_meetings if m ["start_time" ] > now ),
53+ key = (lambda meeting : meeting ["start_time" ])
54+ )
55+ upcoming_start = upcoming ["start_time" ]
56+ except ValueError :
57+ upcoming = None
58+ upcoming_start = now + datetime .timedelta (weeks = 1 )
59+
60+ recent = [
61+ m for m in sc_meetings
62+ if (now > m ["start_time" ] > now - datetime .timedelta (hours = 2 ))
63+ and not m ["live" ]
64+ ]
65+
66+ starting_soon = upcoming_start - now < datetime .timedelta (hours = 1 )
67+ if starting_soon :
68+ common .zoom_request (
69+ requests .patch ,
70+ f"{ common .ZOOM_API } meetings/{ upcoming ['id' ]} " ,
71+ data = json .dumps ({"settings" : {"join_before_host" : True }}),
72+ )
73+
74+ if (
75+ live
76+ and (
77+ starting_soon
78+ or live [0 ]["start_time" ] < now - datetime .timedelta (minutes = 90 )
79+ )
80+ ):
81+ live_id = live [0 ]["id" ]
82+ common .zoom_request (
83+ requests .put ,
84+ f"{ common .ZOOM_API } meetings/{ live_id } /status" ,
85+ data = json .dumps ({"action" : "end" }),
86+ )
87+ common .zoom_request (
88+ requests .patch ,
89+ f"{ common .ZOOM_API } meetings/{ live_id } " ,
90+ data = json .dumps ({"settings" : {"join_before_host" : False }}),
91+ )
92+
93+ for meeting in recent :
94+ common .zoom_request (
95+ requests .patch ,
96+ f"{ common .ZOOM_API } meetings/{ meeting ['id' ]} " ,
97+ data = json .dumps ({"settings" : {"join_before_host" : False }}),
98+ )
99+
100+ if __name__ == "__main__" :
101+ update_host_key ()
102+ rotate_meetings ()
0 commit comments