import os import requests import urllib import subprocess import time from flask import Flask, jsonify, redirect, request from flask_cors import CORS from dotenv import load_dotenv load_dotenv(override=True) app = Flask(__name__) CORS(app) APPS = { "chatbot": "../starter-apps/chatbot.py", "patient-intake": "../starter-apps/patient-intake.py", "storybot": "../starter-apps/storybot.py", "translator": "../starter-apps/translator.py" } daily_api_key = os.getenv("DAILY_API_KEY") api_path = os.getenv("DAILY_API_PATH") or "https://api.daily.co/v1" def get_room_name(room_url): return urllib.parse.urlparse(room_url).path[1:] def create_room(room_properties, exp): room_props = { "exp": exp, "enable_chat": True, "enable_emoji_reactions": True, "eject_at_room_exp": True, "enable_prejoin_ui": False, "enable_recording": "cloud" } if room_properties: room_props |= room_properties res = requests.post( f"{api_path}/rooms", headers={"Authorization": f"Bearer {daily_api_key}"}, json={ "properties": room_props }, ) if res.status_code != 200: raise Exception(f"Unable to create room: {res.text}") room_url = res.json()["url"] room_name = res.json()["name"] return (room_url, room_name) def create_token(room_name, token_properties, exp): token_props = {"exp": exp, "is_owner": True} if token_properties: token_props |= token_properties # Force the token to be limited to the room token_props |= {"room_name": room_name} res = requests.post( f'{api_path}/meeting-tokens', headers={ 'Authorization': f'Bearer {daily_api_key}'}, json={ 'properties': token_props}) if res.status_code != 200: if res.status_code != 200: raise Exception(f"Unable to create meeting token: {res.text}") meeting_token = res.json()['token'] return meeting_token def start_bot(*, bot_path, room_url, token, bot_args, wait_for_bot): room_name = get_room_name(room_url) proc = subprocess.Popen( [f"python {bot_path} -u {room_url} -t {token} -k {daily_api_key} {bot_args}"], shell=True, bufsize=1, ) if wait_for_bot: # Don't return until the bot has joined the room, but wait for at most 5 # seconds. attempts = 0 while attempts < 50: time.sleep(0.1) attempts += 1 res = requests.get( f"{api_path}/rooms/{room_name}/get-session-data", headers={"Authorization": f"Bearer {daily_api_key}"}, ) if res.status_code == 200: print(f"Took {attempts} attempts to join room {room_name}") return True # If we don't break from the loop, that means we never found the bot in the room raise Exception("The bot was unable to join the room. Please try again.") return True @app.route("/start/", methods=["GET", "POST"]) def start(botname): try: if botname not in APPS: raise Exception(f"Bot '{botname}' is not in the allowlist.") bot_path = APPS[botname] props = { "room_url": None, "room_properties": None, "token_properties": None, "bot_args": None, "wait_for_bot": True, "duration": None, "redirect": True } props |= request.values.to_dict() # gets URL params as well as plaintext POST body try: props |= request.json except BaseException: pass if props['redirect'] == "false": props['redirect'] = False if props['wait_for_bot'] == "false": props['wait_for_bot'] = False duration = int(os.getenv("DAILY_BOT_DURATION") or 7200) if props['duration']: duration = props['duration'] exp = time.time() + duration if (props['room_url']): room_url = props['room_url'] try: room_name = get_room_name(room_url) except ValueError: raise Exception( "There was a problem detecting the room name. Please double-check the value of room_url.") else: room_url, room_name = create_room(props['room_properties'], exp) token = create_token(room_name, props['token_properties'], exp) bot = start_bot( room_url=room_url, bot_path=bot_path, token=token, bot_args=props['bot_args'], wait_for_bot=props['wait_for_bot']) if props['redirect'] and request.method == "GET": return redirect(room_url, 302) else: return jsonify({"room_url": room_url, "token": token}) except BaseException as e: return f"There was a problem starting the bot: {e}", 500 @app.route("/healthz") def health_check(): return "ok", 200