async def leave_group(self, request, context): try: leave_member = request.leave_member leave_member_by = request.leave_member_by new_member_status = "removed" if leave_member.id == leave_member_by.id: new_member_status = "leaved" group = GroupService().get_group_info(request.group_id) group_clients = json.loads(group.group_clients) leave_member_in_group = False leave_member_by_in_group = False for e in group_clients: if e['id'] == leave_member.id: leave_member_in_group = True e['status'] = new_member_status if e['id'] == leave_member_by.id: leave_member_by_in_group = True if not leave_member_in_group: raise Exception(Message.LEAVED_MEMBER_NOT_IN_GROUP) if not leave_member_by_in_group and new_member_status == "removed": raise Exception(Message.REMOVER_MEMBER_NOT_IN_GROUP) # update field group_clients first logger.info("New group client: {}".format(group_clients)) group.group_clients = json.dumps(group_clients) group.updated_by = leave_member_by.id group.update() owner_workspace_domain = get_owner_workspace_domain() # wait for service to leave group, and return base response if no exception occured if (group.owner_workspace_domain and group.owner_workspace_domain != owner_workspace_domain): await self.service.leave_group_not_owner( leave_member, leave_member_by, group, ) else: await self.service.leave_group_owner( leave_member, leave_member_by, group, ) return group_messages.BaseResponse() except Exception as e: logger.error(e) if not e.args or e.args[0] not in Message.msg_dict: errors = [Message.get_error_object(Message.LEAVE_GROUP_FAILED)] else: errors = [Message.get_error_object(e.args[0])] context.set_details(json.dumps( errors, default=lambda x: x.__dict__)) context.set_code(grpc.StatusCode.INTERNAL)
async def GroupGetClientKey(self, request, context): try: group_id = request.groupId client_id = request.clientId # get group first group = GroupService().get_group_obj(group_id) owner_workspace_domain = get_owner_workspace_domain() if group.owner_workspace_domain and group.owner_workspace_domain != owner_workspace_domain: owner_workspace_group_id = group.owner_group_id obj_resp = self.service.group_by_owner_get_client_key(owner_workspace_group_id, client_id) if obj_resp is not None: response = signal_pb2.GroupGetClientKeyResponse( groupId=obj_resp.group_id, clientKey=signal_pb2.GroupClientKeyObject( clientId=obj_resp.client_id, deviceId=obj_resp.device_id, clientKeyDistribution=obj_resp.client_key ) ) return response else: obj_resp = ClientSignal(group.owner_workspace_domain).group_get_client_key(group.owner_group_id, client_id) return obj_resp else: obj_resp = self.service.group_get_client_key(group_id, client_id) if obj_resp is not None: if obj_resp.client_workspace_domain and obj_resp.client_workspace_domain != owner_workspace_domain: obj_resp = ClientSignal(obj_resp.client_workspace_domain).workspace_group_get_client_key(obj_resp.client_workspace_group_id, obj_resp.client_id) return obj_resp else: response = signal_pb2.GroupGetClientKeyResponse( groupId=obj_resp.group_id, clientKey=signal_pb2.GroupClientKeyObject( clientId=obj_resp.client_id, deviceId=obj_resp.device_id, clientKeyDistribution=obj_resp.client_key ) ) return response raise Exception(Message.CLIENT_SIGNAL_KEY_NOT_FOUND) except Exception as e: errors = [Message.get_error_object(Message.CLIENT_SIGNAL_KEY_NOT_FOUND)] context.set_details(json.dumps( errors, default=lambda x: x.__dict__)) context.set_code(grpc.StatusCode.NOT_FOUND)
async def workspace_get_messages_in_group(self, request, context): try: logger.info("workspace_get_messages_in_group") owner_workspace_domain = get_owner_workspace_domain() group = GroupService().get_group_info(request.group_id) if group.owner_workspace_domain is None or group.owner_workspace_domain == owner_workspace_domain: obj_res = self.service.get_message_in_group(request.client_id, request.group_id, request.off_set, request.last_message_at) else: raise Exception(Message.GET_MESSAGE_IN_GROUP_FAILED) return obj_res except Exception as e: logger.error(e) if not e.args or e.args[0] not in Message.msg_dict: errors = [Message.get_error_object(Message.GET_MESSAGE_IN_GROUP_FAILED)] else: errors = [Message.get_error_object(e.args[0])] context.set_details(json.dumps( errors, default=lambda x: x.__dict__)) context.set_code(grpc.StatusCode.INTERNAL)
async def get_messages_in_group(self, request, context): try: header_data = dict(context.invocation_metadata()) introspect_token = KeyCloakUtils.introspect_token(header_data['access_token']) client_id = introspect_token['sub'] group_id = request.group_id off_set = request.off_set last_message_at = request.last_message_at owner_workspace_domain = get_owner_workspace_domain() group = GroupService().get_group_info(group_id) if group and group.owner_workspace_domain and group.owner_workspace_domain != owner_workspace_domain: workspace_request = message_pb2.WorkspaceGetMessagesInGroupRequest( group_id = group.owner_group_id, client_id = client_id, off_set = request.off_set, last_message_at = request.last_message_at ) obj_res = ClientMessage(group.owner_workspace_domain).workspace_get_messages_in_group(workspace_request) if obj_res and obj_res.lst_message: for obj in obj_res.lst_message: obj.group_id = group_id obj.client_workspace_domain = owner_workspace_domain return obj_res else: raise else: obj_res = self.service.get_message_in_group(client_id, group_id, off_set, last_message_at) return obj_res except Exception as e: logger.error(e) if not e.args or e.args[0] not in Message.msg_dict: # TODO: change message when got error errors = [Message.get_error_object(Message.GET_MESSAGE_IN_GROUP_FAILED)] else: errors = [Message.get_error_object(e.args[0])] context.set_details(json.dumps( errors, default=lambda x: x.__dict__)) context.set_code(grpc.StatusCode.INTERNAL)
async def update_call_to_group_not_owner(self, request, group, from_client_id): logger.info("update_call_to_group_not_owner") client_id = request.client_id update_type = request.update_type from_client_username = "" from_client_avatar = "" owner_workspace_domain = get_owner_workspace_domain() # update call to owner server, response ọbject push notification lst_client = GroupService().get_clients_in_group_owner(group.owner_group_id) for client in lst_client: if client.User.id == from_client_id: from_client_username = client.User.display_name from_client_avatar = client.User.avatar else: # ret_val = NotifyInAppService().notify_client_update_call(update_type, client.GroupClientKey.client_id, from_client_id, client.GroupClientKey.group_id) # if not ret_val: push_payload = { 'notify_type': update_type, 'group_id': str(client.GroupClientKey.group_id), 'from_client_id': from_client_id, 'from_client_name': from_client_username, 'from_client_avatar': from_client_avatar, 'client_id': client_id } await NotifyPushService().push_voip_client(client.GroupClientKey.client_id, push_payload) request = video_call_pb2.WorkspaceUpdateCallRequest( from_client_id=from_client_id, from_client_name=from_client_username, from_client_avatar=from_client_avatar, from_client_workspace_domain=owner_workspace_domain, client_id=client_id, group_id=group.owner_group_id, update_type=update_type ) obj_res = ClientVideoCall(group.owner_workspace_domain).workspace_update_call(request) return video_call_pb2.BaseResponse()
async def update_call(self, request, context): try: header_data = dict(context.invocation_metadata()) introspect_token = KeyCloakUtils.introspect_token(header_data['access_token']) from_client_id = introspect_token['sub'] group_id = request.group_id owner_workspace_domain = get_owner_workspace_domain() group = GroupService().get_group_info(group_id) if group.owner_workspace_domain and group.owner_workspace_domain != owner_workspace_domain: return await self.update_call_to_group_not_owner(request, group, from_client_id) else: return await self.update_call_to_group_owner(request, from_client_id) except Exception as e: logger.error(e) if not e.args or e.args[0] not in Message.msg_dict: errors = [Message.get_error_object(Message.CLIENT_UPDATE_CALL_FAILED)] else: errors = [Message.get_error_object(e.args[0])] context.set_details(json.dumps( errors, default=lambda x: x.__dict__)) context.set_code(grpc.StatusCode.INTERNAL)
async def Publish(self, request, context): try: header_data = dict(context.invocation_metadata()) introspect_token = KeyCloakUtils.introspect_token(header_data['access_token']) user_id = introspect_token['sub'] owner_workspace_domain = get_owner_workspace_domain() group = GroupService().get_group_info(request.groupId) if group.owner_workspace_domain and group.owner_workspace_domain != owner_workspace_domain: res_obj = await self.publish_to_group_not_owner(request, user_id, group) return res_obj else: res_obj = await self.publish_to_group_owner(request, user_id, group) return res_obj except Exception as e: logger.error(e) if not e.args or e.args[0] not in Message.msg_dict: errors = [Message.get_error_object(Message.CLIENT_PUBLISH_MESSAGE_FAILED)] else: errors = [Message.get_error_object(e.args[0])] context.set_details(json.dumps( errors, default=lambda x: x.__dict__)) context.set_code(grpc.StatusCode.INTERNAL)
def __init__(self): self.service_group = GroupService() self.push_service = NotifyPushService()
async def add_member(self, request, context): try: group = GroupService().get_group_info(request.group_id) group_clients = json.loads(group.group_clients) added_member_info = request.added_member_info adding_member_info = request.adding_member_info #get workspace is active in group workspace_domains = list(set( [e['workspace_domain'] for e in group_clients if ('status' not in e or ('status' in e and e['status'] in ['active']))] )) logger.info(workspace_domains) #check added and adding member in group adding_member_in_group = False for e in group_clients: if 'status' not in e or e['status'] in ['active']: if e['id'] == added_member_info.id: raise Exception(Message.ADDED_USER_IS_ALREADY_MEMBER) if e['id'] == adding_member_info.id: adding_member_in_group = True if not adding_member_in_group: raise Exception(Message.ADDER_MEMBER_NOT_IN_GROUP) # new group clients new_group_clients = [] is_old_member = False for e in group_clients: if e['id'] == added_member_info.id: e['status'] = 'active' # turn into active member is_old_member = True new_group_clients.append(e) if not is_old_member: added_member_info.status = 'active' new_group_clients.append( MessageToDict( added_member_info, preserving_proto_field_name=True ) ) # update group members first group.group_clients = json.dumps(new_group_clients) group.updated_by = adding_member_info.id group.updated_at = datetime.datetime.now() group.update() owner_workspace_domain = get_owner_workspace_domain() # wait for service to add member to group, and return base response if no exception occured if (group.owner_workspace_domain and group.owner_workspace_domain != owner_workspace_domain): await self.service.add_member_to_group_not_owner( added_member_info, adding_member_info, group, ) else: await self.service.add_member_to_group_owner( added_member_info, adding_member_info, group ) return group_messages.BaseResponse() except Exception as e: logger.error(e) if not e.args or e.args[0] not in Message.msg_dict: errors = [Message.get_error_object(Message.ADD_MEMBER_FAILED)] else: errors = [Message.get_error_object(e.args[0])] context.set_details(json.dumps( errors, default=lambda x: x.__dict__)) context.set_code(grpc.StatusCode.INTERNAL)
async def publish_to_group_not_owner(self, request, from_client_id, group): logger.info("publish_to_group_not_owner from client_id {}, group_id={}".format(from_client_id, str(group.id))) owner_workspace_domain = get_owner_workspace_domain() message_id = str(uuid.uuid4()) created_at = datetime.now() message_res_object = message_pb2.MessageObjectResponse( id=message_id, client_id=request.clientId, group_id=group.id, group_type=group.group_type, from_client_id=from_client_id, from_client_workspace_domain=owner_workspace_domain, message=request.message, created_at=int(created_at.timestamp() * 1000), sender_message=request.sender_message ) # publish message to user in this server first lst_client = GroupService().get_clients_in_group_owner(group.owner_group_id) push_service = NotifyPushService() for client in lst_client: for notify_token in client.User.tokens: if client.User is None: continue device_id = notify_token.device_id logger.info('device_id in real loop in handle {}'.format(device_id)) if client.GroupClientKey.client_id == from_client_id and device_id == request.from_client_device_id: continue message_channel = "message/{}/{}".format(client.GroupClientKey.client_id, device_id) new_message_res_object = deepcopy(message_res_object) new_message_res_object.group_id = client.GroupClientKey.group_id new_message_res_object.client_id = client.GroupClientKey.client_id if message_channel in client_message_queue: client_message_queue[message_channel].put(new_message_res_object) else: if new_message_res_object.group_type == 'peer' and new_message_res_object.client_id == from_client_id: logger.info('using sender_message') message_content = base64.b64encode(request.sender_message).decode('utf-8') else: logger.info('using message') message_content = base64.b64encode(new_message_res_object.message).decode('utf-8') message = { 'id': new_message_res_object.id, 'client_id': new_message_res_object.client_id, 'client_workspace_domain': owner_workspace_domain, 'created_at': new_message_res_object.created_at, 'from_client_id': new_message_res_object.from_client_id, 'from_client_workspace_domain': new_message_res_object.from_client_workspace_domain, 'group_id': client.GroupClientKey.group_id, 'group_type': new_message_res_object.group_type, 'message': message_content } await push_service.push_text_to_client_with_device(client.GroupClientKey.client_id, device_id, title="", body="You have a new message", from_client_id=new_message_res_object.from_client_id, notify_type="new_message", data=json.dumps(message), from_client_device_id=request.from_client_device_id) continue # pubish message to owner server request1 = message_pb2.WorkspacePublishRequest( from_client_id=from_client_id, from_client_workspace_domain=owner_workspace_domain, client_id=request.clientId, group_id=group.owner_group_id, group_type=group.group_type, message_id=message_id, message=request.message, created_at=int(created_at.timestamp() * 1000), sender_message=request.sender_message, from_client_device_id=request.from_client_device_id, ) res_object = ClientMessage(group.owner_workspace_domain).workspace_publish_message(request1) if res_object is None: logger.error("send message to client failed") return message_res_object
def __init__(self, *kwargs): self.service = GroupService()
async def publish_to_group_owner(self, request, from_client_id, group): logger.info("publish_to_group_owner from client_id {}, group_id={}".format(from_client_id, str(group.id))) owner_workspace_domain = get_owner_workspace_domain() # store message here message_id = str(uuid.uuid4()) created_at = datetime.now() message_res_object = MessageService().store_message( message_id=message_id, created_at=created_at, group_id=group.id, group_type=group.group_type, from_client_id=from_client_id, from_client_workspace_domain=owner_workspace_domain, client_id=request.clientId, message=request.message, sender_message=request.sender_message ) lst_client = GroupService().get_clients_in_group(group.id) push_service = NotifyPushService() for client in lst_client: if client.GroupClientKey.client_workspace_domain is None or client.GroupClientKey.client_workspace_domain == owner_workspace_domain: if client.User is None: continue new_message_res_object = deepcopy(message_res_object) new_message_res_object.client_id = client.GroupClientKey.client_id for notify_token in client.User.tokens: device_id = notify_token.device_id logger.info('device_id in real loop in handle {}'.format(device_id)) if client.GroupClientKey.client_id == from_client_id and device_id == request.from_client_device_id: continue message_channel = "message/{}/{}".format(client.GroupClientKey.client_id, device_id) if message_channel in client_message_queue: logger.info('message channel in handle {}'.format(message_channel)) client_message_queue[message_channel].put(new_message_res_object) else: if new_message_res_object.group_type == 'peer' and new_message_res_object.client_id == from_client_id: logger.info('using sender_message') message_content = base64.b64encode(request.sender_message).decode('utf-8') else: logger.info('using message') message_content = base64.b64encode(new_message_res_object.message).decode('utf-8') message = { 'id': new_message_res_object.id, 'client_id': new_message_res_object.client_id, 'client_workspace_domain': owner_workspace_domain, 'created_at': new_message_res_object.created_at, 'from_client_id': new_message_res_object.from_client_id, 'from_client_workspace_domain': owner_workspace_domain, 'group_id': new_message_res_object.group_id, 'group_type': new_message_res_object.group_type, 'message': message_content } await push_service.push_text_to_client_with_device(client.GroupClientKey.client_id, device_id, title="", body="You have a new message", from_client_id=new_message_res_object.from_client_id, notify_type="new_message", data=json.dumps(message), from_client_device_id=request.from_client_device_id) # continue else: # call to other server logger.info('push to client {} in server {}'.format(message_res_object.client_id, client.GroupClientKey.client_workspace_domain)) request2 = message_pb2.WorkspacePublishRequest( from_client_id=message_res_object.from_client_id, from_client_workspace_domain=owner_workspace_domain, client_id=message_res_object.client_id, group_id=client.GroupClientKey.client_workspace_group_id, group_type=message_res_object.group_type, message_id=message_res_object.id, message=message_res_object.message, created_at=message_res_object.created_at, updated_at=message_res_object.updated_at, from_client_device_id=request.from_client_device_id, sender_message=request.sender_message ) message_res_object2 = ClientMessage( client.GroupClientKey.client_workspace_domain).workspace_publish_message(request2) if message_res_object2 is None: logger.error("send message to client failed") return message_res_object
async def workspace_publish(self, request, context): try: logger.info("workspace_publish from client_id {}".format(request.from_client_id)) owner_workspace_domain = get_owner_workspace_domain() group = GroupService().get_group_info(request.group_id) if group.owner_workspace_domain is None or group.owner_workspace_domain == owner_workspace_domain: # store message here MessageService().store_message( message_id=request.message_id, created_at=datetime.now(), group_id=request.group_id, group_type=request.group_type, from_client_id=request.from_client_id, from_client_workspace_domain=request.from_client_workspace_domain, client_id=request.client_id, message=request.message, sender_message=request.sender_message ) new_message = message_pb2.MessageObjectResponse( id=request.message_id, client_id=request.client_id, group_id=request.group_id, group_type=request.group_type, from_client_id=request.from_client_id, from_client_workspace_domain=request.from_client_workspace_domain, message=request.message, created_at=request.created_at, updated_at=request.updated_at, sender_message=request.sender_message ) # push notification for other client lst_client = GroupService().get_clients_in_group(request.group_id) for client in lst_client: if client.GroupClientKey.client_workspace_domain != request.from_client_workspace_domain: if client.GroupClientKey.client_workspace_domain is None or client.GroupClientKey.client_workspace_domain == owner_workspace_domain: if client.User is None: continue for notify_token in client.User.tokens: device_id = notify_token.device_id logger.info('device_id in real loop in handle {}'.format(device_id)) if client.GroupClientKey.client_id == request.from_client_id and device_id == request.from_client_device_id: continue message_channel = "message/{}/{}".format(client.GroupClientKey.client_id, device_id) new_message_res_object = deepcopy(new_message) new_message_res_object.client_id = client.GroupClientKey.client_id if message_channel in client_message_queue: logger.info('message channel in handle {}'.format(message_channel)) client_message_queue[message_channel].put(new_message_res_object) else: if new_message_res_object.group_type == 'peer' and new_message_res_object.client_id == request.from_client_id: logger.info('using sender_message') message_content = base64.b64encode(request.sender_message).decode('utf-8') else: logger.info('using message') message_content = base64.b64encode(new_message_res_object.message).decode('utf-8') push_service = NotifyPushService() message = { 'id': new_message_res_object.id, 'client_id': new_message_res_object.client_id, 'client_workspace_domain': get_owner_workspace_domain(), 'created_at': new_message_res_object.created_at, 'from_client_id': new_message_res_object.from_client_id, 'from_client_workspace_domain': new_message_res_object.from_client_workspace_domain, 'group_id': new_message_res_object.group_id, 'group_type': request.group_type, 'message': message_content } await push_service.push_text_to_client_with_device(client.GroupClientKey.client_id, device_id, title="", body="You have a new message", from_client_id=new_message.from_client_id, notify_type="new_message", data=json.dumps(message), from_client_device_id=request.from_client_device_id) continue else: # call to other server request.group_id = client.GroupClientKey.client_workspace_group_id res_object = ClientMessage( client.GroupClientKey.client_workspace_domain).workspace_publish_message(request) if res_object is None: logger.error("Workspace Publish Message to client failed") return new_message except Exception as e: logger.error(e) if not e.args or e.args[0] not in Message.msg_dict: errors = [Message.get_error_object(Message.CLIENT_PUBLISH_MESSAGE_FAILED)] else: errors = [Message.get_error_object(e.args[0])] context.set_details(json.dumps( errors, default=lambda x: x.__dict__)) context.set_code(grpc.StatusCode.INTERNAL)
async def workspace_video_call(self, request, context): try: logger.info("workspace_video_call") from_client_id = request.from_client_id from_client_name = request.from_client_name if request.from_client_avatar: from_client_avatar = request.from_client_avatar else: from_client_avatar = "" group_id = request.group_id client_id = request.client_id server_info = ServerInfoService().get_server_info() webrtc_token = secrets.token_hex(10) group_obj = GroupService().get_group_info(group_id) group_obj.group_rtc_token = webrtc_token group_obj.update() # register webrtc self.service_group.register_webrtc_token(webrtc_token) # create room self.service_group.create_rtc_group(group_id, webrtc_token) logger.info('janus webrtc token={}'.format(webrtc_token)) client_ws_url = get_system_config()['janus_webrtc'].get('client_ws_url') # send push notification to all member of group lst_client_in_groups = self.service_group.get_clients_in_group(group_id) # list token for each device type owner_workspace_domain = get_owner_workspace_domain() for client in lst_client_in_groups: if client.GroupClientKey.client_workspace_domain != request.from_client_workspace_domain: push_payload = { 'notify_type': 'request_call', 'call_type': request.call_type, 'group_id': str(request.group_id), 'group_name': group_obj.group_name if group_obj.group_name else '', 'group_type': group_obj.group_type if group_obj.group_type else '', 'group_rtc_token': webrtc_token, 'group_rtc_url': client_ws_url, 'group_rtc_id': str(group_id), 'from_client_id': from_client_id, 'from_client_name': from_client_name, 'from_client_avatar': from_client_avatar, 'client_id': client_id, 'stun_server': server_info.stun_server, 'turn_server': server_info.turn_server } logger.info(push_payload) if client.GroupClientKey.client_workspace_domain is None or client.GroupClientKey.client_workspace_domain == owner_workspace_domain: await NotifyPushService().push_voip_client(client.User.id, push_payload) else: new_push_payload = deepcopy(push_payload) new_push_payload['group_id'] = str(client.GroupClientKey.client_workspace_group_id) logger.info(new_push_payload) ClientPush(client.GroupClientKey.client_workspace_domain).push_voip(client.User.id, json.dumps(new_push_payload)) stun_server_obj = json.loads(server_info.stun_server) stun_server = video_call_pb2.StunServer( server=stun_server_obj["server"], port=stun_server_obj["port"] ) turn_server_obj = json.loads(server_info.turn_server) turn_server = video_call_pb2.TurnServer( server=turn_server_obj["server"], port=turn_server_obj["port"], type=turn_server_obj["type"], user=turn_server_obj["user"], pwd=turn_server_obj["pwd"] ) return video_call_pb2.ServerResponse( group_rtc_url=client_ws_url, group_rtc_id=group_id, group_rtc_token=webrtc_token, stun_server=stun_server, turn_server=turn_server, ) except Exception as e: logger.error(e) if not e.args or e.args[0] not in Message.msg_dict: errors = [Message.get_error_object(Message.CLIENT_REQUEST_CALL_FAILED)] else: errors = [Message.get_error_object(e.args[0])] context.set_details(json.dumps( errors, default=lambda x: x.__dict__)) context.set_code(grpc.StatusCode.INTERNAL)
class GroupController(BaseController): def __init__(self, *kwargs): self.service = GroupService() @request_logged async def create_group(self, request, context): try: group_name = request.group_name group_type = request.group_type lst_client = request.lst_client obj_res = self.service.add_group(group_name, group_type, lst_client, request.created_by_client_id) return obj_res except Exception as e: logger.error(e) if not e.args or e.args[0] not in Message.msg_dict: errors = [Message.get_error_object(Message.CREATE_GROUP_CHAT_FAILED)] else: errors = [Message.get_error_object(e.args[0])] context.set_details(json.dumps( errors, default=lambda x: x.__dict__)) context.set_code(grpc.StatusCode.INTERNAL) @request_logged async def create_group_workspace(self, request, context): try: group_name = request.group_name group_type = request.group_type from_client_id = request.from_client_id client_id = request.client_id lst_client = request.lst_client owner_group_id = request.owner_group_id owner_workspace_domain = request.owner_workspace_domain obj_res = self.service.add_group_workspace(group_name, group_type, from_client_id, client_id, lst_client, owner_group_id, owner_workspace_domain) return obj_res except Exception as e: logger.error(e) if not e.args or e.args[0] not in Message.msg_dict: errors = [Message.get_error_object(Message.CREATE_GROUP_CHAT_FAILED)] else: errors = [Message.get_error_object(e.args[0])] context.set_details(json.dumps( errors, default=lambda x: x.__dict__)) context.set_code(grpc.StatusCode.INTERNAL) @request_logged async def get_group(self, request, context): try: header_data = dict(context.invocation_metadata()) introspect_token = KeyCloakUtils.introspect_token(header_data['access_token']) client_id = introspect_token['sub'] group_id = request.group_id obj_res = self.service.get_group(group_id, client_id) if obj_res is not None: return obj_res else: raise Exception(Message.GROUP_CHAT_NOT_FOUND) except Exception as e: logger.error(e) if not e.args or e.args[0] not in Message.msg_dict: errors = [Message.get_error_object(Message.GET_GROUP_CHAT_FAILED)] else: errors = [Message.get_error_object(e.args[0])] context.set_details(json.dumps( errors, default=lambda x: x.__dict__)) context.set_code(grpc.StatusCode.INTERNAL) @request_logged async def search_groups(self, request, context): try: keyword = request.keyword obj_res = self.service.search_group(keyword) return obj_res except Exception as e: logger.error(e) if not e.args or e.args[0] not in Message.msg_dict: errors = [Message.get_error_object(Message.SEARCH_GROUP_CHAT_FAILED)] else: errors = [Message.get_error_object(e.args[0])] context.set_details(json.dumps( errors, default=lambda x: x.__dict__)) context.set_code(grpc.StatusCode.INTERNAL) @request_logged @auth_required async def get_joined_groups(self, request, context): try: header_data = dict(context.invocation_metadata()) introspect_token = KeyCloakUtils.introspect_token(header_data['access_token']) client_id = introspect_token['sub'] obj_res = self.service.get_joined_group(client_id) return obj_res except Exception as e: logger.error(e) if not e.args or e.args[0] not in Message.msg_dict: errors = [Message.get_error_object(Message.REGISTER_CLIENT_SIGNAL_KEY_FAILED)] else: errors = [Message.get_error_object(e.args[0])] context.set_details(json.dumps( errors, default=lambda x: x.__dict__)) context.set_code(grpc.StatusCode.INTERNAL) @request_logged async def join_group(self, request, context): try: # TODO: implement this function pass except Exception as e: logger.error(e) if not e.args or e.args[0] not in Message.msg_dict: errors = [Message.get_error_object(Message.REGISTER_CLIENT_SIGNAL_KEY_FAILED)] else: errors = [Message.get_error_object(e.args[0])] context.set_details(json.dumps( errors, default=lambda x: x.__dict__)) context.set_code(grpc.StatusCode.INTERNAL) @request_logged async def add_member(self, request, context): try: group = GroupService().get_group_info(request.group_id) group_clients = json.loads(group.group_clients) added_member_info = request.added_member_info adding_member_info = request.adding_member_info #get workspace is active in group workspace_domains = list(set( [e['workspace_domain'] for e in group_clients if ('status' not in e or ('status' in e and e['status'] in ['active']))] )) logger.info(workspace_domains) #check added and adding member in group adding_member_in_group = False for e in group_clients: if 'status' not in e or e['status'] in ['active']: if e['id'] == added_member_info.id: raise Exception(Message.ADDED_USER_IS_ALREADY_MEMBER) if e['id'] == adding_member_info.id: adding_member_in_group = True if not adding_member_in_group: raise Exception(Message.ADDER_MEMBER_NOT_IN_GROUP) # new group clients new_group_clients = [] is_old_member = False for e in group_clients: if e['id'] == added_member_info.id: e['status'] = 'active' # turn into active member is_old_member = True new_group_clients.append(e) if not is_old_member: added_member_info.status = 'active' new_group_clients.append( MessageToDict( added_member_info, preserving_proto_field_name=True ) ) # update group members first group.group_clients = json.dumps(new_group_clients) group.updated_by = adding_member_info.id group.updated_at = datetime.datetime.now() group.update() owner_workspace_domain = get_owner_workspace_domain() # wait for service to add member to group, and return base response if no exception occured if (group.owner_workspace_domain and group.owner_workspace_domain != owner_workspace_domain): await self.service.add_member_to_group_not_owner( added_member_info, adding_member_info, group, ) else: await self.service.add_member_to_group_owner( added_member_info, adding_member_info, group ) return group_messages.BaseResponse() except Exception as e: logger.error(e) if not e.args or e.args[0] not in Message.msg_dict: errors = [Message.get_error_object(Message.ADD_MEMBER_FAILED)] else: errors = [Message.get_error_object(e.args[0])] context.set_details(json.dumps( errors, default=lambda x: x.__dict__)) context.set_code(grpc.StatusCode.INTERNAL) @request_logged async def workspace_add_member(self, request, context): try: added_member_info = request.added_member_info adding_member_info = request.adding_member_info owner_group = request.owner_group response = await self.service.workspace_add_member( added_member_info, adding_member_info, owner_group ) return response except Exception as e: logger.error(e) if not e.args or e.args[0] not in Message.msg_dict: errors = [Message.get_error_object(Message.ADD_MEMBER_FAILED)] else: errors = [Message.get_error_object(e.args[0])] context.set_details(json.dumps( errors, default=lambda x: x.__dict__)) context.set_code(grpc.StatusCode.INTERNAL) @request_logged async def leave_group(self, request, context): try: leave_member = request.leave_member leave_member_by = request.leave_member_by new_member_status = "removed" if leave_member.id == leave_member_by.id: new_member_status = "leaved" group = GroupService().get_group_info(request.group_id) group_clients = json.loads(group.group_clients) leave_member_in_group = False leave_member_by_in_group = False for e in group_clients: if e['id'] == leave_member.id: leave_member_in_group = True e['status'] = new_member_status if e['id'] == leave_member_by.id: leave_member_by_in_group = True if not leave_member_in_group: raise Exception(Message.LEAVED_MEMBER_NOT_IN_GROUP) if not leave_member_by_in_group and new_member_status == "removed": raise Exception(Message.REMOVER_MEMBER_NOT_IN_GROUP) # update field group_clients first logger.info("New group client: {}".format(group_clients)) group.group_clients = json.dumps(group_clients) group.updated_by = leave_member_by.id group.update() owner_workspace_domain = get_owner_workspace_domain() # wait for service to leave group, and return base response if no exception occured if (group.owner_workspace_domain and group.owner_workspace_domain != owner_workspace_domain): await self.service.leave_group_not_owner( leave_member, leave_member_by, group, ) else: await self.service.leave_group_owner( leave_member, leave_member_by, group, ) return group_messages.BaseResponse() except Exception as e: logger.error(e) if not e.args or e.args[0] not in Message.msg_dict: errors = [Message.get_error_object(Message.LEAVE_GROUP_FAILED)] else: errors = [Message.get_error_object(e.args[0])] context.set_details(json.dumps( errors, default=lambda x: x.__dict__)) context.set_code(grpc.StatusCode.INTERNAL) @request_logged async def workspace_leave_group(self, request, context): try: leave_member = request.leave_member leave_member_by = request.leave_member_by owner_group = request.owner_group # wait for service to call workspace leave group, and return base response if no exception occured await self.service.workspace_leave_group( leave_member, leave_member_by, owner_group ) return group_messages.BaseResponse() except Exception as e: logger.error(e) if not e.args or e.args[0] not in Message.msg_dict: errors = [Message.get_error_object(Message.ADD_MEMBER_FAILED)] else: errors = [Message.get_error_object(e.args[0])] context.set_details(json.dumps( errors, default=lambda x: x.__dict__)) context.set_code(grpc.StatusCode.INTERNAL) @request_logged async def workspace_notify_deactive_member(self, request, context): try: await self.service.workspace_notify_deactive_member( request.deactive_account_id, request.client_ids ) return group_messages.BaseResponse() except Exception as e: logger.error(e) if not e.args or e.args[0] not in Message.msg_dict: errors = [Message.get_error_object(Message.LEAVE_GROUP_FAILED)] else: errors = [Message.get_error_object(e.args[0])] context.set_details(json.dumps( errors, default=lambda x: x.__dict__)) context.set_code(grpc.StatusCode.INTERNAL)
async def call_to_group_not_owner(self, request, group, from_client_id): client_id = request.client_id call_type = request.call_type from_client_name = "" from_client_avatar = "" owner_workspace_domain = get_owner_workspace_domain() logger.info("call_to_group_not_owner, group_id={}".format(str(group.id))) # request call to owner server, response ọbject push notification lst_client = GroupService().get_clients_in_group_owner(group.owner_group_id) for client in lst_client: if client.User and client.User.id == from_client_id: from_client_name = client.User.display_name if client.User.avatar: from_client_avatar = client.User.avatar else: from_client_avatar = "" other_client_in_this_workspace = [] for client in lst_client: if client.User and client.User.id != from_client_id: client_with_group = { "client_id": client.User.id, "group_id": client.GroupClientKey.group_id } other_client_in_this_workspace.append(client_with_group) request = video_call_pb2.WorkspaceVideoCallRequest( from_client_id=from_client_id, from_client_name=from_client_name, from_client_avatar=from_client_avatar, from_client_workspace_domain=owner_workspace_domain, client_id=client_id, group_id=group.owner_group_id, call_type=call_type ) obj_res = ClientVideoCall(group.owner_workspace_domain).workspace_video_call(request) if obj_res: # push for other user in this server for client in other_client_in_this_workspace: push_payload = { 'notify_type': 'request_call', 'call_type': call_type, 'group_id': str(client["group_id"]), 'group_name': group.group_name if group.group_name else '', 'group_type': group.group_type if group.group_type else '', 'group_rtc_token': obj_res.group_rtc_token, 'group_rtc_url': obj_res.group_rtc_url, 'group_rtc_id': str(obj_res.group_rtc_id), 'from_client_id': from_client_id, 'from_client_name': from_client_name, 'from_client_avatar': from_client_avatar, 'client_id': client_id, 'stun_server': obj_res.stun_server, 'turn_server': obj_res.turn_server } logger.info(push_payload) await NotifyPushService().push_voip_client(client["client_id"], push_payload) return obj_res else: raise
def __init__(self, *kwargs): self.service = VideoCallService() self.service_group = GroupService()
class VideoCallController(BaseController): def __init__(self, *kwargs): self.service = VideoCallService() self.service_group = GroupService() @request_logged @auth_required async def video_call(self, request, context): try: header_data = dict(context.invocation_metadata()) introspect_token = KeyCloakUtils.introspect_token(header_data['access_token']) from_client_id = introspect_token['sub'] group_id = request.group_id owner_workspace_domain = get_owner_workspace_domain() group = GroupService().get_group_info(group_id) if group.owner_workspace_domain and group.owner_workspace_domain != owner_workspace_domain: return await self.call_to_group_not_owner(request, group, from_client_id) else: return await self.call_to_group_owner(request, group, from_client_id) except Exception as e: logger.error(e) if not e.args or e.args[0] not in Message.msg_dict: errors = [Message.get_error_object(Message.CLIENT_REQUEST_CALL_FAILED)] else: errors = [Message.get_error_object(e.args[0])] context.set_details(json.dumps( errors, default=lambda x: x.__dict__)) context.set_code(grpc.StatusCode.INTERNAL) @request_logged async def workspace_video_call(self, request, context): try: logger.info("workspace_video_call") from_client_id = request.from_client_id from_client_name = request.from_client_name if request.from_client_avatar: from_client_avatar = request.from_client_avatar else: from_client_avatar = "" group_id = request.group_id client_id = request.client_id server_info = ServerInfoService().get_server_info() webrtc_token = secrets.token_hex(10) group_obj = GroupService().get_group_info(group_id) group_obj.group_rtc_token = webrtc_token group_obj.update() # register webrtc self.service_group.register_webrtc_token(webrtc_token) # create room self.service_group.create_rtc_group(group_id, webrtc_token) logger.info('janus webrtc token={}'.format(webrtc_token)) client_ws_url = get_system_config()['janus_webrtc'].get('client_ws_url') # send push notification to all member of group lst_client_in_groups = self.service_group.get_clients_in_group(group_id) # list token for each device type owner_workspace_domain = get_owner_workspace_domain() for client in lst_client_in_groups: if client.GroupClientKey.client_workspace_domain != request.from_client_workspace_domain: push_payload = { 'notify_type': 'request_call', 'call_type': request.call_type, 'group_id': str(request.group_id), 'group_name': group_obj.group_name if group_obj.group_name else '', 'group_type': group_obj.group_type if group_obj.group_type else '', 'group_rtc_token': webrtc_token, 'group_rtc_url': client_ws_url, 'group_rtc_id': str(group_id), 'from_client_id': from_client_id, 'from_client_name': from_client_name, 'from_client_avatar': from_client_avatar, 'client_id': client_id, 'stun_server': server_info.stun_server, 'turn_server': server_info.turn_server } logger.info(push_payload) if client.GroupClientKey.client_workspace_domain is None or client.GroupClientKey.client_workspace_domain == owner_workspace_domain: await NotifyPushService().push_voip_client(client.User.id, push_payload) else: new_push_payload = deepcopy(push_payload) new_push_payload['group_id'] = str(client.GroupClientKey.client_workspace_group_id) logger.info(new_push_payload) ClientPush(client.GroupClientKey.client_workspace_domain).push_voip(client.User.id, json.dumps(new_push_payload)) stun_server_obj = json.loads(server_info.stun_server) stun_server = video_call_pb2.StunServer( server=stun_server_obj["server"], port=stun_server_obj["port"] ) turn_server_obj = json.loads(server_info.turn_server) turn_server = video_call_pb2.TurnServer( server=turn_server_obj["server"], port=turn_server_obj["port"], type=turn_server_obj["type"], user=turn_server_obj["user"], pwd=turn_server_obj["pwd"] ) return video_call_pb2.ServerResponse( group_rtc_url=client_ws_url, group_rtc_id=group_id, group_rtc_token=webrtc_token, stun_server=stun_server, turn_server=turn_server, ) except Exception as e: logger.error(e) if not e.args or e.args[0] not in Message.msg_dict: errors = [Message.get_error_object(Message.CLIENT_REQUEST_CALL_FAILED)] else: errors = [Message.get_error_object(e.args[0])] context.set_details(json.dumps( errors, default=lambda x: x.__dict__)) context.set_code(grpc.StatusCode.INTERNAL) async def call_to_group_owner(self, request, group_obj, from_client_id): from_client_name = "" from_client_avatar = "" owner_workspace_domain = get_owner_workspace_domain() group_id = request.group_id logger.info("call_to_group_owner, group_id= {}".format(str(group_id))) client_id = request.client_id server_info = ServerInfoService().get_server_info() webrtc_token = secrets.token_hex(10) group_obj.group_rtc_token = webrtc_token group_obj.update() # register webrtc self.service_group.register_webrtc_token(webrtc_token) # create room self.service_group.create_rtc_group(group_id, webrtc_token) logger.info('janus webrtc token={}'.format(webrtc_token)) client_ws_url = get_system_config()['janus_webrtc'].get('client_ws_url') # send push notification to all member of group lst_client_in_groups = self.service_group.get_clients_in_group(group_id) # list token for each device type for client in lst_client_in_groups: if client.User and client.User.id == from_client_id: from_client_name = client.User.display_name if client.User.avatar: from_client_avatar = client.User.avatar else: from_client_avatar = "" for client in lst_client_in_groups: if client.User is None or client.User.id != from_client_id: push_payload = { 'notify_type': 'request_call', 'call_type': request.call_type, 'group_id': str(client.GroupClientKey.group_id), 'group_name': group_obj.group_name if group_obj.group_name else '', 'group_type': group_obj.group_type if group_obj.group_type else '', 'group_rtc_token': webrtc_token, 'group_rtc_url': client_ws_url, 'group_rtc_id': str(group_id), 'from_client_id': from_client_id, 'from_client_name': from_client_name, 'from_client_avatar': from_client_avatar, 'client_id': client_id, 'stun_server': server_info.stun_server, 'turn_server': server_info.turn_server } logger.info(push_payload) if client.GroupClientKey.client_workspace_domain is None or client.GroupClientKey.client_workspace_domain == owner_workspace_domain: await NotifyPushService().push_voip_client(client.User.id, push_payload) else: logger.info("Push voip from {} to client {} in {}".format(owner_workspace_domain, client.GroupClientKey.client_workspace_domain, client.GroupClientKey.client_id)) new_push_payload = deepcopy(push_payload) new_push_payload['group_id'] = str(client.GroupClientKey.client_workspace_group_id) logger.info(new_push_payload) ClientPush(client.GroupClientKey.client_workspace_domain).push_voip(client.GroupClientKey.client_id, json.dumps(new_push_payload)) stun_server_obj = json.loads(server_info.stun_server) stun_server = video_call_pb2.StunServer( server=stun_server_obj["server"], port=stun_server_obj["port"] ) turn_server_obj = json.loads(server_info.turn_server) turn_server = video_call_pb2.TurnServer( server=turn_server_obj["server"], port=turn_server_obj["port"], type=turn_server_obj["type"], user=turn_server_obj["user"], pwd=turn_server_obj["pwd"] ) return video_call_pb2.ServerResponse( group_rtc_url=client_ws_url, group_rtc_id=group_id, group_rtc_token=webrtc_token, stun_server=stun_server, turn_server=turn_server ) async def call_to_group_not_owner(self, request, group, from_client_id): client_id = request.client_id call_type = request.call_type from_client_name = "" from_client_avatar = "" owner_workspace_domain = get_owner_workspace_domain() logger.info("call_to_group_not_owner, group_id={}".format(str(group.id))) # request call to owner server, response ọbject push notification lst_client = GroupService().get_clients_in_group_owner(group.owner_group_id) for client in lst_client: if client.User and client.User.id == from_client_id: from_client_name = client.User.display_name if client.User.avatar: from_client_avatar = client.User.avatar else: from_client_avatar = "" other_client_in_this_workspace = [] for client in lst_client: if client.User and client.User.id != from_client_id: client_with_group = { "client_id": client.User.id, "group_id": client.GroupClientKey.group_id } other_client_in_this_workspace.append(client_with_group) request = video_call_pb2.WorkspaceVideoCallRequest( from_client_id=from_client_id, from_client_name=from_client_name, from_client_avatar=from_client_avatar, from_client_workspace_domain=owner_workspace_domain, client_id=client_id, group_id=group.owner_group_id, call_type=call_type ) obj_res = ClientVideoCall(group.owner_workspace_domain).workspace_video_call(request) if obj_res: # push for other user in this server for client in other_client_in_this_workspace: push_payload = { 'notify_type': 'request_call', 'call_type': call_type, 'group_id': str(client["group_id"]), 'group_name': group.group_name if group.group_name else '', 'group_type': group.group_type if group.group_type else '', 'group_rtc_token': obj_res.group_rtc_token, 'group_rtc_url': obj_res.group_rtc_url, 'group_rtc_id': str(obj_res.group_rtc_id), 'from_client_id': from_client_id, 'from_client_name': from_client_name, 'from_client_avatar': from_client_avatar, 'client_id': client_id, 'stun_server': obj_res.stun_server, 'turn_server': obj_res.turn_server } logger.info(push_payload) await NotifyPushService().push_voip_client(client["client_id"], push_payload) return obj_res else: raise @request_logged @auth_required async def update_call(self, request, context): try: header_data = dict(context.invocation_metadata()) introspect_token = KeyCloakUtils.introspect_token(header_data['access_token']) from_client_id = introspect_token['sub'] group_id = request.group_id owner_workspace_domain = get_owner_workspace_domain() group = GroupService().get_group_info(group_id) if group.owner_workspace_domain and group.owner_workspace_domain != owner_workspace_domain: return await self.update_call_to_group_not_owner(request, group, from_client_id) else: return await self.update_call_to_group_owner(request, from_client_id) except Exception as e: logger.error(e) if not e.args or e.args[0] not in Message.msg_dict: errors = [Message.get_error_object(Message.CLIENT_UPDATE_CALL_FAILED)] else: errors = [Message.get_error_object(e.args[0])] context.set_details(json.dumps( errors, default=lambda x: x.__dict__)) context.set_code(grpc.StatusCode.INTERNAL) @request_logged async def workspace_update_call(self, request, context): try: logger.info("workspace_update_call") from_client_id = request.from_client_id from_client_name = request.from_client_name from_client_avatar = request.from_client_avatar group_id = request.group_id client_id = request.client_id update_type = request.update_type # send push notification to all member of group lst_client_in_groups = self.service_group.get_clients_in_group(group_id) # list token for each device type owner_workspace_domain = get_owner_workspace_domain() for client in lst_client_in_groups: if client.GroupClientKey.client_workspace_domain != request.from_client_workspace_domain: push_payload = { 'notify_type': update_type, 'group_id': str(client.GroupClientKey.group_id), 'from_client_id': from_client_id, 'from_client_name': from_client_name, 'from_client_avatar': from_client_avatar, 'client_id': client_id } if client.GroupClientKey.client_workspace_domain is None or client.GroupClientKey.client_workspace_domain == owner_workspace_domain: # ret_val = NotifyInAppService().notify_client_update_call(update_type, client.GroupClientKey.client_id, from_client_id, # client.GroupClientKey.group_id) # if not ret_val: new_push_payload = deepcopy(push_payload) logger.info(new_push_payload) await NotifyPushService().push_voip_client(client.GroupClientKey.client_id, new_push_payload) else: new_push_payload = deepcopy(push_payload) new_push_payload["group_id"] = str(client.GroupClientKey.client_workspace_group_id) logger.info(new_push_payload) ClientPush(client.GroupClientKey.client_workspace_domain).push_voip(client.GroupClientKey.client_id, json.dumps(new_push_payload)) #continue return video_call_pb2.BaseResponse() except Exception as e: logger.error(e) if not e.args or e.args[0] not in Message.msg_dict: errors = [Message.get_error_object(Message.CLIENT_UPDATE_CALL_FAILED)] else: errors = [Message.get_error_object(e.args[0])] context.set_details(json.dumps( errors, default=lambda x: x.__dict__)) context.set_code(grpc.StatusCode.INTERNAL) async def update_call_to_group_owner(self, request, from_client_id): logger.info("update_call_to_group_owner") from_client_name = "" from_client_avatar = "" owner_workspace_domain = get_owner_workspace_domain() group_id = request.group_id client_id = request.client_id update_type = request.update_type # send push notification to all member of group lst_client_in_groups = self.service_group.get_clients_in_group(group_id) for client in lst_client_in_groups: if client.User and client.User.id == from_client_id: from_client_name = client.User.display_name from_client_avatar = client.User.avatar for client in lst_client_in_groups: if client.User is None or client.User.id != from_client_id: push_payload = { 'notify_type': update_type, 'group_id': str(client.GroupClientKey.group_id), 'from_client_id': from_client_id, 'from_client_name': from_client_name, 'from_client_avatar': from_client_avatar, 'client_id': client_id } if client.GroupClientKey.client_workspace_domain is None or client.GroupClientKey.client_workspace_domain == owner_workspace_domain: # logger.info("update_call_to_group_owner, owner member ->client_id {}".format(client.GroupClientKey.client_id)) # ret_val = NotifyInAppService().notify_client_update_call(update_type, client.GroupClientKey.client_id, from_client_id, # client.GroupClientKey.group_id) # logger.info("notify inapp {}".format(ret_val)) # if not ret_val: # logger.info("notify push notification") new_push_payload = deepcopy(push_payload) new_push_payload["group_id"] = str(client.GroupClientKey.group_id) logger.info(new_push_payload) await NotifyPushService().push_voip_client(client.GroupClientKey.client_id, new_push_payload) else: new_push_payload = deepcopy(push_payload) new_push_payload["group_id"] = str(client.GroupClientKey.client_workspace_group_id) logger.info(new_push_payload) ClientPush(client.GroupClientKey.client_workspace_domain).push_voip(client.GroupClientKey.client_id, json.dumps(new_push_payload)) #continue return video_call_pb2.BaseResponse() async def update_call_to_group_not_owner(self, request, group, from_client_id): logger.info("update_call_to_group_not_owner") client_id = request.client_id update_type = request.update_type from_client_username = "" from_client_avatar = "" owner_workspace_domain = get_owner_workspace_domain() # update call to owner server, response ọbject push notification lst_client = GroupService().get_clients_in_group_owner(group.owner_group_id) for client in lst_client: if client.User.id == from_client_id: from_client_username = client.User.display_name from_client_avatar = client.User.avatar else: # ret_val = NotifyInAppService().notify_client_update_call(update_type, client.GroupClientKey.client_id, from_client_id, client.GroupClientKey.group_id) # if not ret_val: push_payload = { 'notify_type': update_type, 'group_id': str(client.GroupClientKey.group_id), 'from_client_id': from_client_id, 'from_client_name': from_client_username, 'from_client_avatar': from_client_avatar, 'client_id': client_id } await NotifyPushService().push_voip_client(client.GroupClientKey.client_id, push_payload) request = video_call_pb2.WorkspaceUpdateCallRequest( from_client_id=from_client_id, from_client_name=from_client_username, from_client_avatar=from_client_avatar, from_client_workspace_domain=owner_workspace_domain, client_id=client_id, group_id=group.owner_group_id, update_type=update_type ) obj_res = ClientVideoCall(group.owner_workspace_domain).workspace_update_call(request) return video_call_pb2.BaseResponse()