Denis Tosic | e7f4f9d | 2018-10-09 15:18:46 +0200 | [diff] [blame^] | 1 | """Utility class for sending requests to BonD, adding and controlling bots.""" |
| 2 | |
| 3 | from __future__ import absolute_import |
| 4 | from __future__ import division |
| 5 | from __future__ import print_function |
| 6 | |
| 7 | import json |
| 8 | import requests |
| 9 | |
| 10 | from datetime import datetime |
| 11 | from oauth2client.client import GoogleCredentials |
| 12 | |
| 13 | |
| 14 | _BOND_API_URL = 'https://bond-pa.sandbox.googleapis.com' |
| 15 | _HANGOUTS_API_URL = 'https://www.googleapis.com/hangouts/v1_meetings/' |
| 16 | _MEETINGS_API_URL = 'https://meetings.googleapis.com' |
| 17 | |
| 18 | _TOKEN_TTL_SECONDS = 3500 |
| 19 | _SERVICE_CREDS_FILE = '/creds/service_accounts/bond_service_account.json' |
| 20 | |
| 21 | _BOT_CREDS_FILE = '/creds/service_accounts/bond_bot_credentials.json' |
| 22 | |
| 23 | |
| 24 | class BondHttpApi(object): |
| 25 | """Utility class for sending requests to BonD for bots.""" |
| 26 | |
| 27 | def __init__(self): |
| 28 | self._bot_creds = self._GetBotCredentials() |
| 29 | self._last_token_request_time = None |
| 30 | self._last_token = None |
| 31 | |
| 32 | def GetAvailableWorkers(self): |
| 33 | """Gets the number of available workers from a conference. |
| 34 | |
| 35 | Returns: |
| 36 | GetNumOfAvailableWorkersResponse denoting failure or success of |
| 37 | the request. |
| 38 | """ |
| 39 | token = self._GetAccessToken() |
| 40 | resp = requests.get( |
| 41 | '%s/v1/workers:count' % _BOND_API_URL, |
| 42 | headers={ |
| 43 | 'Content-Type': 'application/json', |
| 44 | 'Authorization': 'Bearer %s' % token |
| 45 | }) |
| 46 | return resp.text |
| 47 | |
| 48 | def ExecuteScript(self, script, meeting_code): |
| 49 | """Executes the specified script. |
| 50 | |
| 51 | Args: |
| 52 | script: Script to execute. |
| 53 | meeting_code: The meeting to execute the script for. |
| 54 | |
| 55 | Returns: |
| 56 | RunScriptRequest denoting failure or success of the request. |
| 57 | """ |
| 58 | token = self._GetAccessToken() |
| 59 | |
| 60 | request_data = """{ |
| 61 | "script": "%s", |
| 62 | "conference": { |
| 63 | "conference_code": "%s" |
| 64 | }}""" % (script, meeting_code) |
| 65 | |
| 66 | resp = requests.post( |
| 67 | '%s/v1/conference/%s/script' % (_BOND_API_URL, meeting_code), |
| 68 | headers={ |
| 69 | 'Content-Type': 'application/json', |
| 70 | 'Authorization': 'Bearer %s' % token, |
| 71 | }, |
| 72 | data=request_data) |
| 73 | return resp.text |
| 74 | |
| 75 | def AddBotsRequest(self, meeting_code, number_of_bots, ttl_secs): |
| 76 | """Adds a number of bots to a meeting for a specified period of time. |
| 77 | |
| 78 | Args: |
| 79 | meeting_code: The meeting to join. |
| 80 | number_of_bots: The number of bots to add to the meeting. |
| 81 | ttl_secs: The time in seconds that the bots will stay in the meeting after |
| 82 | joining. |
| 83 | |
| 84 | Returns: |
| 85 | AddBotsResponse denoting failure or success of the request. |
| 86 | """ |
| 87 | token = self._GetAccessToken() |
| 88 | |
| 89 | request_data = { |
| 90 | 'num_of_bots': number_of_bots, |
| 91 | 'ttl_secs': ttl_secs, |
| 92 | 'video_call_options': {}, |
| 93 | 'media_options': { |
| 94 | 'audio_file_path': "audio_32bit_48k_stereo.raw", |
| 95 | 'mute_audio': True, |
| 96 | 'video_file_path': "jamboard_two_far_video_hd.1280_720.yuv", |
| 97 | 'video_fps': 30, |
| 98 | 'mute_video': False |
| 99 | }, |
| 100 | 'backend_options': { |
| 101 | 'mesi_apiary_url': _HANGOUTS_API_URL, |
| 102 | 'mas_one_platform_url': _MEETINGS_API_URL |
| 103 | }, |
| 104 | 'conference': { |
| 105 | 'conference_code': meeting_code |
| 106 | }, |
| 107 | 'bot_type': "MEETINGS", |
| 108 | 'authentication_options': { |
| 109 | 'is_anonymous': False, |
| 110 | 'oauth_credentials': self._bot_creds |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | resp = requests.post( |
| 115 | '%s/v1/conference/%s/bots:add' % (_BOND_API_URL, meeting_code), |
| 116 | headers={ |
| 117 | 'Content-Type': 'application/json', |
| 118 | 'Authorization': 'Bearer %s' % token |
| 119 | }, |
| 120 | data=json.dumps(request_data)) |
| 121 | |
| 122 | return resp.text |
| 123 | |
| 124 | def _GetAccessToken(self): |
| 125 | if self._last_token is None or self._CheckTokenExpired(): |
| 126 | credentials = self._CreateApiCredentials() |
| 127 | scope = 'https://www.googleapis.com/auth/meetings' |
| 128 | credentials = credentials.create_scoped(scope) |
| 129 | self._last_token_request_time = datetime.now() |
| 130 | self._last_token = credentials.get_access_token().access_token |
| 131 | return self._last_token |
| 132 | |
| 133 | def _CreateApiCredentials(self): |
| 134 | return GoogleCredentials.from_stream(_SERVICE_CREDS_FILE) |
| 135 | |
| 136 | def _GetBotCredentials(self): |
| 137 | with open(_BOT_CREDS_FILE) as f: |
| 138 | return json.load(f) |
| 139 | |
| 140 | def _CheckTokenExpired(self): |
| 141 | elapsed = datetime.now() - self._last_token_request_time |
| 142 | return elapsed.total_seconds() > _TOKEN_TTL_SECONDS |