• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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