Skip to content

Commit a85bd3b

Browse files
authored
Merge pull request #16 from virtualscienceforum/schedule-zoom-talks-bot
Scheduling zoom talks, sending keys and announcing at researchseminars.org
2 parents 4c87306 + fec3226 commit a85bd3b

File tree

5 files changed

+433
-5
lines changed

5 files changed

+433
-5
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: Schedule Zoom Talks
2+
on:
3+
repository_dispatch:
4+
types: [schedule-zoom-talk]
5+
6+
jobs:
7+
scheduleTalks:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- name: Checkout code
11+
uses: actions/checkout@v2
12+
13+
- name: Set up Python 3.8
14+
uses: actions/setup-python@v2
15+
with:
16+
python-version: 3.8
17+
18+
- name: Install dependencies
19+
run: |
20+
python -m pip install --upgrade pip
21+
pip install -r requirements.txt
22+
23+
- name: Schedule talks
24+
working-directory: ./bots
25+
run: python3 schedulezoomtalks.py
26+
env:
27+
VSF_BOT_TOKEN: ${{ secrets.VSF_BOT_TOKEN }}
28+
ZOOM_API_KEY: ${{ secrets.ZOOM_API_KEY }}
29+
ZOOM_API_SECRET: ${{ secrets.ZOOM_API_SECRET }}
30+
MAILGUN_API_KEY: ${{ secrets.MAILGUN_API_KEY }}

common.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
from functools import lru_cache
22
import os
33
from time import time
4+
import json
45

56
import jwt
67
import requests
78

89
ZOOM_API = "https://api.zoom.us/v2/"
910
SPEAKERS_CORNER_USER_ID = "D0n5UNEHQiajWtgdWLlNSA"
11+
TALKS_FILE = "speakers_corner_talks.yml"
12+
13+
MAILGUN_BASE_URL = "https://api.eu.mailgun.net/v3/"
14+
MAILGUN_DOMAIN = "mail.virtualscienceforum.org/"
1015

1116
@lru_cache()
1217
def zoom_headers(duration: int=100) -> dict:
@@ -31,7 +36,6 @@ def zoom_request(method: callable, *args, **kwargs):
3136
if response.content:
3237
return response.json()
3338

34-
3539
def speakers_corner_user_id() -> str:
3640
users = zoom_request(requests.get, ZOOM_API + "users")["users"]
3741
sc_user_id = next(
@@ -43,7 +47,7 @@ def speakers_corner_user_id() -> str:
4347

4448
def all_meetings(user_id) -> list:
4549
"""Return all meetings by a user.
46-
50+
4751
Handles pagination, and adds ``live: True`` to a meeting that is running (if any).
4852
"""
4953
meetings = []
@@ -58,7 +62,7 @@ def all_meetings(user_id) -> list:
5862
next_page_token = meetings_page["next_page_token"]
5963
if not next_page_token:
6064
break
61-
65+
6266
live_meetings = zoom_request(
6367
requests.get,
6468
f"{ZOOM_API}users/{user_id}/meetings",
@@ -69,5 +73,19 @@ def all_meetings(user_id) -> list:
6973
for meeting in meetings:
7074
if meeting["id"] == live_meetings[0]["id"]:
7175
meeting["live"] = True
72-
76+
7377
return meetings
78+
79+
80+
def decode(response):
81+
if response.status_code > 299: # Not OK
82+
raise RuntimeError(response.content.decode())
83+
return json.loads(response.content.decode())
84+
85+
86+
def api_query(method, endpoint, **params):
87+
return decode(method(
88+
MAILGUN_BASE_URL + endpoint,
89+
auth=("api", os.getenv("MAILGUN_API_KEY")),
90+
**params
91+
))

requirements.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
requests
22
pytz
33
python-dateutil
4-
PyJWT
4+
PyJWT
5+
PyGithub
6+
PyYAML

researchseminarsdotorg.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import os
2+
from requests import get, post
3+
4+
SPEAKERS_CORNER_SEMINAR_SERIES = {"series_id": "speakerscorner",
5+
"name": "Speakers\' Corner",
6+
"is_conference": False,
7+
"topics": [""], # TODO: Get a list of topics
8+
"language": "en",
9+
"institutions": [""],
10+
"timezone": "UTC",
11+
"homepage": "https://virtualscienceforum.org/speakerscorner.md",
12+
"visibility": 1, # 0=private, 1=unlisted, 2=public
13+
"access_control": 0, # 0=open, see schema for more
14+
"slots": [""],
15+
"organizers": [{"name": "Virtual Science Forum",
16+
"email": "vsf@virtualscienceforum.org",
17+
"homepage": "https://virtualscienceforum.org",
18+
"organizer": True,
19+
"order": 0,
20+
"display": True}]}
21+
22+
23+
def find_seminar_series(series_id):
24+
url = f"https://researchseminars.org/api/0/search/series?series_id={series_id}"
25+
r = get(url)
26+
if r.status_code == 200:
27+
J = r.json()
28+
results = J["properties"]["results"]
29+
return (len(results) != 0)
30+
31+
32+
def create_seminar_series(payload, authorization):
33+
url = "https://researchseminars.org/api/0/save/series/"
34+
r = post(url, json=payload, headers={"authorization":authorization})
35+
J = r.json()
36+
code = J.get("code")
37+
38+
if r.status_code == 200:
39+
if code == "warning":
40+
return True, J["warnings"]
41+
else:
42+
return True, ""
43+
else:
44+
return False, ""
45+
46+
47+
def edit_seminar_series(name, payload, authorization):
48+
url = "https://researchseminars.org/api/0/save/series/"
49+
r = post(url, json=payload, headers={"authorization":authorization})
50+
J = r.json()
51+
code = J.get("code")
52+
53+
if r.status_code == 200:
54+
if code == "warning":
55+
return True, J["warnings"]
56+
else:
57+
return True, ""
58+
else:
59+
return False, ""
60+
61+
62+
def add_talk_to_series(series_id, payload, authorization):
63+
url = "https://researchseminars.org/api/0/save/talk/"
64+
r = post(url, json=payload, headers={"authorization":authorization})
65+
J = r.json()
66+
code = J.get("code")
67+
if r.status_code == 200:
68+
if code == "warning":
69+
return J["series_ctr"], J["warnings"]
70+
else:
71+
return J["series_ctr"]
72+
else:
73+
return "", r.status_code
74+
75+
76+
def publish_to_researchseminars(talk):
77+
# talk should be provided in yaml format
78+
api_token = os.getenv("RESEARCHSEMINARS_API_TOKEN")
79+
authorization = "vsf@virtualscienceforum.org %s" % api_token
80+
81+
# Find speakers' corner series, and create it if it doesn't exist
82+
if not find_seminar_series("speakerscorner"):
83+
print("[ResearchSeminars.org]: The speakerscorner seminar series "\
84+
"does not yet exist. Creating it now")
85+
create_seminar_series(SPEAKERS_CORNER_SEMINAR_SERIES)
86+
87+
# TODO: Figure out if we need to edit the series;
88+
# Would be annoying since edits have to be approved
89+
90+
# Set up payload for talk creation
91+
talk_payload = {"title":talk.get('title'),
92+
"speaker":talk.get('author'), # TODO: will be 'speakerS'
93+
"online": True,
94+
"start_time":talk["time"],
95+
"timezone":"UTC"
96+
}
97+
98+
# Make request to remote API
99+
series_ctr, warnings = add_talk_to_series("speakerscorner", talk_payload, authorization)
100+
101+
if series_ctr != "":
102+
print("Talk with id {0} successfully added".format(series_ctr))
103+
if warnings != "":
104+
print("Warnings: {0}".format(warnings))
105+
return True
106+
else:
107+
print("-- ERROR -- ")
108+
print("Could not add talk to series, status code {0}".format(warnings))
109+
return False

0 commit comments

Comments
 (0)