1"""Utility class for sending requests to BonD, adding and controlling bots.""" 2 3from __future__ import absolute_import 4from __future__ import division 5from __future__ import print_function 6 7import json 8import logging 9import requests 10 11from datetime import datetime 12from oauth2client.client import GoogleCredentials 13 14 15_BOND_API_URL = 'https://bond-pa.sandbox.googleapis.com' 16_HANGOUTS_API_URL = 'https://www.googleapis.com/hangouts/v1_meetings_preprod/' 17_MEETINGS_API_URL = 'https://preprod-meetings.sandbox.googleapis.com' 18 19_TOKEN_TTL_SECONDS = 3500 20 21# See https://crbug.com/874835 for details on the credentials files. 22_SERVICE_CREDS_FILE = '/creds/service_accounts/bond_service_account.json' 23 24 25class BondHttpApi(object): 26 """Utility class for sending requests to BonD for bots.""" 27 28 def __init__(self): 29 self._last_token_request_time = None 30 self._last_token = None 31 32 def GetAvailableWorkers(self): 33 """Gets the number of available workers for a conference.""" 34 token = self._GetAccessToken() 35 resp = requests.get( 36 '%s/v1/workers:count' % _BOND_API_URL, 37 headers={ 38 'Content-Type': 'application/json', 39 'Authorization': 'Bearer %s' % token 40 }) 41 return json.loads(resp.text)["numOfAvailableWorkers"] 42 43 def CreateConference(self): 44 """Creates a conference. 45 46 Returns: 47 The meeting code of the created conference. 48 """ 49 token = self._GetAccessToken() 50 51 request_data = { 52 'conference_type': 'THOR', 53 'backend_options': { 54 'mesi_apiary_url': _HANGOUTS_API_URL, 55 'mas_one_platform_url': _MEETINGS_API_URL 56 }, 57 } 58 59 resp = requests.post( 60 '%s/v1/conferences:create' % _BOND_API_URL, 61 headers={ 62 'Content-Type': 'application/json', 63 'Authorization': 'Bearer %s' % token, 64 }, 65 data=json.dumps(request_data)) 66 json_response = json.loads(resp.text) 67 logging.info("CreateConference response: %s", json_response) 68 return json_response["conference"]["conferenceCode"] 69 70 def ExecuteScript(self, script, meeting_code): 71 """Executes the specified script. 72 73 Args: 74 script: Script to execute. 75 meeting_code: The meeting to execute the script for. 76 77 Returns: 78 RunScriptRequest denoting failure or success of the request. 79 """ 80 token = self._GetAccessToken() 81 82 request_data = { 83 'script': script, 84 'conference': { 85 'conference_code': meeting_code 86 } 87 } 88 89 resp = requests.post( 90 '%s/v1/conference/%s/script' % (_BOND_API_URL, meeting_code), 91 headers={ 92 'Content-Type': 'application/json', 93 'Authorization': 'Bearer %s' % token, 94 }, 95 data=json.dumps(request_data)) 96 97 json_response = json.loads(resp.text) 98 logging.info("ExecuteScript response: %s", json_response) 99 return json_response['success'] 100 101 102 def AddBotsRequest(self, meeting_code, number_of_bots, ttl_secs): 103 """Adds a number of bots to a meeting for a specified period of time. 104 105 Args: 106 meeting_code: The meeting to join. 107 number_of_bots: The number of bots to add to the meeting. 108 ttl_secs: The time in seconds that the bots will stay in the meeting after 109 joining. 110 111 Returns: 112 List of IDs of the started bots. 113 """ 114 token = self._GetAccessToken() 115 116 request_data = { 117 'num_of_bots': number_of_bots, 118 'ttl_secs': ttl_secs, 119 'video_call_options': {}, 120 'media_options': { 121 'audio_file_path': "audio_32bit_48k_stereo.raw", 122 'mute_audio': True, 123 'video_file_path': "jamboard_two_far_video_hd.1280_720.yuv", 124 'video_fps': 30, 125 'mute_video': False 126 }, 127 'backend_options': { 128 'mesi_apiary_url': _HANGOUTS_API_URL, 129 'mas_one_platform_url': _MEETINGS_API_URL 130 }, 131 'conference': { 132 'conference_code': meeting_code 133 }, 134 'bot_type': "MEETINGS" 135 } 136 137 resp = requests.post( 138 '%s/v1/conference/%s/bots:add' % (_BOND_API_URL, meeting_code), 139 headers={ 140 'Content-Type': 'application/json', 141 'Authorization': 'Bearer %s' % token 142 }, 143 data=json.dumps(request_data)) 144 145 json_response = json.loads(resp.text) 146 logging.info("AddBotsRequest response: %s", json_response) 147 return json_response["botIds"] 148 149 def _GetAccessToken(self): 150 if self._last_token is None or self._CheckTokenExpired(): 151 credentials = self._CreateApiCredentials() 152 scope = 'https://www.googleapis.com/auth/meetings' 153 credentials = credentials.create_scoped(scope) 154 self._last_token_request_time = datetime.now() 155 self._last_token = credentials.get_access_token().access_token 156 return self._last_token 157 158 def _CreateApiCredentials(self): 159 return GoogleCredentials.from_stream(_SERVICE_CREDS_FILE) 160 161 def _CheckTokenExpired(self): 162 elapsed = datetime.now() - self._last_token_request_time 163 return elapsed.total_seconds() > _TOKEN_TTL_SECONDS 164