def create_from_user(self, initiator_call, exten, flow, timeout, user_uuid): if not Channel(initiator_call, self.ari).exists(): raise TransferCreationError('initiator channel not found') if Channel(initiator_call, self.ari).user() != user_uuid: raise UserPermissionDenied(user_uuid, {'call': initiator_call}) try: transferred_call = Channel(initiator_call, self.ari).only_connected_channel().id except TooManyChannels as e: raise TooManyTransferredCandidates(e.channels) except NotEnoughChannels: raise TransferCreationError('transferred channel not found') context = User(user_uuid, self.confd_client).main_line().context() return self.create(transferred_call, initiator_call, context, exten, flow, variables={}, timeout=timeout)
def create(self, transferred_call, initiator_call, context, exten, flow, variables, timeout): try: transferred_channel = self.ari.channels.get( channelId=transferred_call) initiator_channel = self.ari.channels.get(channelId=initiator_call) except ARINotFound: raise TransferCreationError('channel not found') if not ami.extension_exists(self.amid_client, context, exten): raise InvalidExtension(context, exten) if not self.transfer_lock.acquire(initiator_call): raise TransferAlreadyStarted(initiator_call) if not (Channel(transferred_call, self.ari).is_in_stasis() and Channel(initiator_call, self.ari).is_in_stasis()): transfer_state = self.state_factory.make_from_class( TransferStateReadyNonStasis) else: transfer_state = self.state_factory.make_from_class( TransferStateReady) try: new_state = transfer_state.create(transferred_channel, initiator_channel, context, exten, flow, variables, timeout) except Exception: self.transfer_lock.release(initiator_call) raise if flow == 'blind': new_state = new_state.complete() return new_state.transfer
def create(self, transferred_channel, initiator_channel, context, exten, flow, variables, timeout): channel = Channel(initiator_channel.id, self._ari) initiator_uuid = channel.user() initiator_tenant_uuid = channel.tenant_uuid() if initiator_uuid is None: raise TransferCreationError('initiator has no user UUID') transfer_id = str(uuid.uuid4()) try: ari_helpers.convert_transfer_to_stasis(self._ari, self._amid, transferred_channel.id, initiator_channel.id, context, exten, transfer_id, variables, timeout) except ARINotFound: raise TransferCreationError('channel not found') self.transfer = Transfer(transfer_id, initiator_uuid, initiator_tenant_uuid) self.transfer.initiator_call = initiator_channel.id self.transfer.transferred_call = transferred_channel.id self.transfer.status = self.name self.transfer.flow = flow self._notifier.created(self.transfer) return TransferStateStarting.from_state(self)
def _verify_user(self, call_id, user_uuid): channel = Channel(call_id, self._ari) if not channel.exists() or channel.is_local(): raise NoSuchCall(call_id) if channel.user() != user_uuid: raise UserPermissionDenied(user_uuid, {'call': call_id})
def kick_participant(self, tenant_uuid, meeting_uuid, participant_id): meeting = Meeting(tenant_uuid, meeting_uuid, self._confd) if not meeting.exists(): raise NoSuchMeeting(tenant_uuid, meeting_uuid) channel = Channel(participant_id, self._ari) try: self._amid.action( 'ConfbridgeKick', { 'Conference': meeting.asterisk_name(), 'Channel': channel.asterisk_name(), }, ) except AmidProtocolError as e: if e.message in [ 'No Conference by that name found.', # This conference is not running at this time. 'No active conferences.', # No conferences are taking place at this time. 'No Channel by that name found in Conference.', # Participant not found. ]: logger.debug('No participants found to kick out of meeting %s', meeting_uuid) raise NoSuchMeetingParticipant(tenant_uuid, meeting_uuid, participant_id) raise except RequestException as e: raise WazoAmidError(self._amid, e)
def new_queued_call(self, tenant_uuid, switchboard_uuid, channel_id): logger.debug('New_queued_call: %s, %s, %s', tenant_uuid, switchboard_uuid, channel_id) bridge_id = BRIDGE_QUEUE_ID.format(uuid=switchboard_uuid) try: bridge = self._ari.bridges.get(bridgeId=bridge_id) except ARINotFound: bridge = self._ari.bridges.createWithId(type='holding', bridgeId=bridge_id) if len(bridge.json['channels']) == 0: moh_class = SwitchboardConfd(tenant_uuid, switchboard_uuid, self._confd).queue_moh() if moh_class: bridge.startMoh(mohClass=moh_class) else: bridge.startMoh() channel = self._ari.channels.get(channelId=channel_id) channel.setChannelVar(variable='WAZO_SWITCHBOARD_QUEUE', value=switchboard_uuid) channel.setChannelVar(variable='WAZO_TENANT_UUID', value=tenant_uuid) channel.answer() bridge.addChannel(channel=channel_id) calls = self.queued_calls(tenant_uuid, switchboard_uuid) self._notifier.queued_calls(tenant_uuid, switchboard_uuid, calls) noanswer_timeout = Channel(channel_id, self._ari).switchboard_timeout() if not noanswer_timeout: logger.debug( 'Switchboard %s: ignoring no answer timeout = %s', switchboard_uuid, noanswer_timeout, ) return noanswer_fallback_action = Channel( channel_id, self._ari).switchboard_noanswer_fallback_action() if not noanswer_fallback_action: logger.debug( 'Switchboard %s: ignoring no answer timeout because there is no fallback', switchboard_uuid, ) return logger.debug( 'Switchboard %s: starting no answer timeout for call %s after %s seconds', switchboard_uuid, channel_id, noanswer_timeout, ) self._asyncio.call_later( noanswer_timeout, self.on_queued_call_noanswer_timeout, tenant_uuid, switchboard_uuid, channel_id, )
def list_participants(self, tenant_uuid, conference_id): if not Conference(tenant_uuid, conference_id, self._confd).exists(): raise NoSuchConference(tenant_uuid, conference_id) try: participant_list = self._amid.action( 'ConfBridgeList', {'conference': f'wazo-conference-{conference_id}'}) except AmidProtocolError as e: no_conf_msg = [ 'No active conferences.', # No conferences are taking place at this time. 'No Conference by that name found.', # This conference is not running at this time. ] if e.message in no_conf_msg: return [] raise ConferenceParticipantError( tenant_uuid, conference_id, participant_id=None, message=e.message, ) except RequestException as e: raise WazoAmidError(self._amid, e) result = [] del participant_list[0] for participant_list_item in participant_list: if participant_list_item['Event'] != 'ConfbridgeList': continue raw_participant = { 'id': participant_list_item['Uniqueid'], 'caller_id_name': participant_list_item['CallerIDName'], 'caller_id_number': participant_list_item['CallerIDNum'], 'muted': participant_list_item['Muted'] == 'Yes', 'join_time': participant_list_item['AnsweredTime'], 'admin': participant_list_item['Admin'] == 'Yes', 'language': participant_list_item['Language'], 'call_id': participant_list_item['Uniqueid'], 'user_uuid': Channel(participant_list_item['Uniqueid'], self._ari).user(), } try: participant = participant_schema.load(raw_participant) except ValidationError as e: raise ConferenceParticipantError(tenant_uuid, conference_id, participant_id=None, message=str(e)) result.append(participant) return result
def snoop_create(self, application, snooped_call_id, snooping_call_id, whisper_mode): if not Channel(snooping_call_id, self._ari).is_in_stasis(): raise CallNotInApplication(application['uuid'], snooping_call_id) if not Channel(snooped_call_id, self._ari).is_in_stasis(): raise CallNotInApplication(application['uuid'], snooped_call_id) snoop = self._snoop_helper.create( application, snooped_call_id, snooping_call_id, whisper_mode, ) self._notifier.snoop_created(application, snoop) return snoop
def process_lost_hangups(self): transfers = list(self.state_persistor.list()) logger.debug('Processing lost hangups since last stop...') for transfer in transfers: transfer_state = self.state_factory.make(transfer) if not Channel(transfer.transferred_call, self.ari).exists(): logger.debug('Transferred hangup from transfer %s', transfer.id) transfer_state = transfer_state.transferred_hangup() if not Channel(transfer.initiator_call, self.ari).exists(): logger.debug('Initiator hangup from transfer %s', transfer.id) transfer_state = transfer_state.initiator_hangup() if not Channel(transfer.recipient_call, self.ari).exists(): logger.debug('Recipient hangup from transfer %s', transfer.id) transfer_state = transfer_state.recipient_hangup() logger.debug('Done.')
def on_channel_entered_bridge(self, channel, event): if event['application'] != ADHOC_CONFERENCE_STASIS_APP: return adhoc_conference_id = event['bridge']['id'] channel_id = event['channel']['id'] logger.debug('adhoc conference %s: channel %s entered', adhoc_conference_id, channel_id) try: is_adhoc_conference_host = channel.getChannelVar( variable='WAZO_IS_ADHOC_CONFERENCE_HOST')['value'] == 'true' except ARINotFound: logger.error( 'adhoc conference %s: channel %s hungup too early or variable not found', adhoc_conference_id, channel_id) return bridge_helper = BridgeSnapshot(event['bridge'], self._ari) if is_adhoc_conference_host: host_channel = Channel(channel_id, self._ari) host_user_uuid = host_channel.user() host_tenant_uuid = host_channel.tenant_uuid() bridge_helper.global_variables.set('WAZO_HOST_CHANNEL_ID', channel_id) bridge_helper.global_variables.set('WAZO_HOST_USER_UUID', host_user_uuid) bridge_helper.global_variables.set('WAZO_HOST_TENANT_UUID', host_tenant_uuid) self._notify_host_of_channels_already_present( adhoc_conference_id, event['bridge']['channels'], host_user_uuid) logger.debug('adhoc conference %s: setting host connectedline', adhoc_conference_id) self._set_host_connectedline(channel_id, adhoc_conference_id) participant_call = CallsService.make_call_from_channel( self._ari, channel) other_participant_uuids = bridge_helper.valid_user_uuids() self._notifier.participant_joined(adhoc_conference_id, other_participant_uuids, participant_call)
def _add_sip_call_id(self, event): if not event['Channel'].startswith('PJSIP/'): return channel_id = event['Uniqueid'] channel = Channel(channel_id, self.ari) sip_call_id = channel.sip_call_id_unsafe() if not sip_call_id: return try: self.ari.channels.setChannelVar( channelId=channel_id, variable='WAZO_SIP_CALL_ID', value=sip_call_id, bypassStasis=True, ) except ARINotFound: logger.debug('channel %s not found', channel_id)
def list_calls_user(self, user_uuid, application_filter=None, application_instance_filter=None): calls = self.list_calls(application_filter, application_instance_filter) return [ call for call in calls if call.user_uuid == user_uuid and not Channel(call.id_, self._ari).is_local() ]
def list_participants(self, tenant_uuid, meeting_uuid): meeting = Meeting(tenant_uuid, meeting_uuid, self._confd) if not meeting.exists(): raise NoSuchMeeting(tenant_uuid, meeting_uuid) try: participant_list = self._amid.action( 'ConfBridgeList', {'Conference': meeting.asterisk_name()}, ) except AmidProtocolError as e: if e.message in [ 'No active conferences.', 'No Conference by that name found.', ]: return [] raise MeetingParticipantError( tenant_uuid, meeting_uuid, participant_id=None, message=e.message, ) except RequestException as e: raise WazoAmidError(self._amid, e) result = [] del participant_list[0] for participant_list_item in participant_list: if participant_list_item['Event'] != 'ConfbridgeList': continue raw_participant = { 'id': participant_list_item['Uniqueid'], 'caller_id_name': participant_list_item['CallerIDName'], 'caller_id_number': participant_list_item['CallerIDNum'], 'call_id': participant_list_item['Uniqueid'], 'user_uuid': Channel(participant_list_item['Uniqueid'], self._ari).user(), } try: participant = participant_schema.load(raw_participant) except ValidationError as e: raise MeetingParticipantError(tenant_uuid, meeting_uuid, participant_id=None, message=str(e)) result.append(participant) return result
def create(self, initiator_call, destination, location, completions, timeout, relocate=None): try: relocated_channel = Channel(initiator_call, self.ari).only_connected_channel() except TooManyChannels as e: raise TooManyChannelCandidates(e.channels) except NotEnoughChannels: raise RelocateCreationError('relocated channel not found') initiator_channel = Channel(initiator_call, self.ari) if not initiator_channel.exists(): details = {'initiator_call': initiator_call} raise RelocateCreationError('initiator call not found', details) try: destination = self.destination_factory.from_type( destination, location, initiator_channel, ) except InvalidDestination: details = {'destination': destination, 'location': location} raise RelocateCreationError('invalid destination', details) with self.duplicate_relocate_lock: if self.relocates.find_by_channel(initiator_channel.id): raise RelocateAlreadyStarted(initiator_channel.id) if not relocate: relocate = Relocate(self.state_factory) relocate.relocated_channel = relocated_channel.id relocate.initiator_channel = initiator_channel.id relocate.completions = completions relocate.timeout = timeout self.relocates.add(relocate) self.notifier.observe(relocate) with relocate.locked(): relocate.initiate(destination) return relocate
def create(self, transferred_channel, initiator_channel, context, exten, flow, variables, timeout): channel = Channel(initiator_channel.id, self._ari) initiator_uuid = channel.user() initiator_tenant_uuid = channel.tenant_uuid() if initiator_uuid is None: raise TransferCreationError('initiator has no user UUID') transfer_bridge = self._ari.bridges.create(type='mixing', name='transfer') transfer_id = transfer_bridge.id try: transferred_channel.setChannelVar(variable='XIVO_TRANSFER_ROLE', value='transferred') transferred_channel.setChannelVar(variable='XIVO_TRANSFER_ID', value=transfer_id) initiator_channel.setChannelVar(variable='XIVO_TRANSFER_ROLE', value='initiator') initiator_channel.setChannelVar(variable='XIVO_TRANSFER_ID', value=transfer_id) transfer_bridge.addChannel(channel=transferred_channel.id) transfer_bridge.addChannel(channel=initiator_channel.id) except ARINotFound: raise TransferCreationError('some channel got hung up') try: ari_helpers.hold_transferred_call(self._ari, self._amid, transferred_channel.id) except ARINotFound: raise TransferCreationError('transferred call hung up') try: self._ari.channels.ring(channelId=initiator_channel.id) except ARINotFound: raise TransferCreationError('initiator call hung up') recipient_call = self._services.originate_recipient(initiator_channel.id, context, exten, transfer_id, variables, timeout) self.transfer = Transfer(transfer_id, initiator_uuid, initiator_tenant_uuid) self.transfer.transferred_call = transferred_channel.id self.transfer.initiator_call = initiator_channel.id self.transfer.recipient_call = recipient_call self.transfer.status = self.name self.transfer.flow = flow self._notifier.created(self.transfer) return TransferStateRingback.from_state(self)
def create_from_user(self, initiator_call, destination, location, completions, timeout, auto_answer, user_uuid): initiator_channel = Channel(initiator_call, self.ari) user = User(user_uuid, self.confd_client) variables = {} if initiator_channel.user() != user_uuid: raise UserPermissionDenied(user_uuid, {'call': initiator_call}) tenant_uuid = initiator_channel.tenant_uuid() if tenant_uuid: variables['WAZO_TENANT_UUID'] = tenant_uuid if destination == 'line': try: destination_interface = user.line(location['line_id']).interface() except (InvalidUserUUID, InvalidUserLine): raise RelocateCreationError('invalid line for user', details={'user_uuid': user_uuid, 'line_id': location['line_id']}) destination = 'interface' location['interface'] = destination_interface elif destination == 'mobile': try: mobile = user.mobile_phone_number() line_context = user.main_line().context() except (InvalidUserUUID, InvalidUserLine): details = {'user_uuid': user_uuid} raise RelocateCreationError('invalid user: could not find main line', details=details) destination = 'extension' location = {'exten': mobile, 'context': line_context} variables['WAZO_DEREFERENCED_USERUUID'] = user_uuid if auto_answer: variables.update(AUTO_ANSWER_VARIABLES) relocate = Relocate(self.state_factory) relocate.initiator = user_uuid relocate.recipient_variables = variables return self.create(initiator_call, destination, location, completions, timeout, relocate=relocate)
def complete(self, relocate): completer = RelocateCompleter(self._amid, self._ari) if Channel(relocate.relocated_channel, self._ari).is_in_stasis(): completer.bridge(relocate) try: self._ari.channels.hangup(channelId=relocate.initiator_channel) except ARINotFound: pass except ARIException as e: logger.exception('ARI error: %s', e) relocate.set_state('ended') else: completer.move_to_stasis(relocate) relocate.set_state('waiting_for_relocated')
def recipient_answered(self, relocate): relocate.events.publish('answered', relocate) if 'answer' in relocate.completions: completer = RelocateCompleter(self._amid, self._ari) if Channel(relocate.relocated_channel, self._ari).is_in_stasis(): completer.bridge(relocate) try: self._ari.channels.hangup( channelId=relocate.initiator_channel) except ARINotFound: pass except ARIException as e: logger.exception('ARI error: %s', e) relocate.set_state('ended') else: completer.move_to_stasis(relocate) relocate.set_state('waiting_for_relocated') elif 'api' in relocate.completions: relocate.set_state('waiting_for_completion') else: raise NotImplementedError()
def add_participant_from_user(self, adhoc_conference_id, participant_call_id, user_uuid): bridge_helper = Bridge(adhoc_conference_id, self._ari) if not bridge_helper.exists(): raise AdhocConferenceNotFound(adhoc_conference_id) if not Channel(participant_call_id, self._ari).exists(): raise ParticipantCallNotFound(participant_call_id) if bridge_helper.global_variables.get( variable='WAZO_HOST_USER_UUID') != user_uuid: raise AdhocConferenceNotFound(adhoc_conference_id) current_participant_call_ids = self._ari.bridges.get( bridgeId=adhoc_conference_id).json['channels'] if participant_call_id in current_participant_call_ids: raise ParticipantCallAlreadyInConference(participant_call_id) try: discarded_host_wazo_channel = self._find_peer_channel( participant_call_id) except NotEnoughChannels: logger.error( 'adhoc conference %s: participant %s is a lone channel', adhoc_conference_id, participant_call_id) raise ParticipantCallNotFound(participant_call_id) except TooManyChannels as e: logger.error( 'adhoc conference %s: participant %s is already talking to %s channels', adhoc_conference_id, participant_call_id, len(list(e.channels))) raise ParticipantCallNotFound(participant_call_id) if discarded_host_wazo_channel.user() != user_uuid: raise ParticipantCallNotFound(participant_call_id) self._redirect_participant(participant_call_id, discarded_host_wazo_channel.id, adhoc_conference_id)
def remove_participant_from_user(self, adhoc_conference_id, participant_call_id, user_uuid): bridge_helper = Bridge(adhoc_conference_id, self._ari) if not bridge_helper.exists(): raise AdhocConferenceNotFound(adhoc_conference_id) if bridge_helper.global_variables.get( variable='WAZO_HOST_USER_UUID') != user_uuid: raise AdhocConferenceNotFound(adhoc_conference_id) if not Channel(participant_call_id, self._ari).exists(): raise ParticipantCallNotFound(participant_call_id) participants = self._ari.bridges.get( bridgeId=adhoc_conference_id).json['channels'] if participant_call_id not in participants: raise ParticipantCallNotFound(participant_call_id) try: self._ari.channels.hangup(channelId=participant_call_id) except ARINotFound: pass
def _find_peer_channel(self, call_id): return Channel(call_id, self._ari).only_connected_channel()
def create_from_user(self, host_call_id, participant_call_ids, user_uuid): logger.debug( 'creating adhoc conference from user %s with host %s and participants %s', user_uuid, host_call_id, participant_call_ids) host_channel = Channel(host_call_id, self._ari) if not host_channel.exists(): raise HostCallNotFound(host_call_id) if host_channel.user() != user_uuid: raise HostCallNotFound(host_call_id) logger.debug('adhoc conference: looking for peer of host %s', host_call_id) try: host_peer_wazo_channel = self._find_peer_channel(host_call_id) except NotEnoughChannels: raise AdhocConferenceCreationError( f'could not determine peer of call {host_call_id}: call has no peers' ) except TooManyChannels: raise HostCallAlreadyInConference(host_call_id) for participant_call_id in participant_call_ids: if not Channel(participant_call_id, self._ari).exists(): raise ParticipantCallNotFound(participant_call_id) try: peer_wazo_channel = self._find_peer_channel( participant_call_id) except NotEnoughChannels: logger.error( 'adhoc conference: participant %s is a lone channel', participant_call_id) raise ParticipantCallNotFound(participant_call_id) except TooManyChannels as e: logger.error( 'adhoc conference: participant %s is already talking to %s channels', participant_call_id, len(list(e.channels))) raise ParticipantCallNotFound(participant_call_id) if peer_wazo_channel.user() != user_uuid: raise ParticipantCallNotFound(participant_call_id) adhoc_conference_id = str(uuid.uuid4()) tenant_uuid = host_channel.tenant_uuid() logger.debug('creating adhoc conference %s', adhoc_conference_id) self._notifier.created(adhoc_conference_id, tenant_uuid, user_uuid) self._redirect_host(host_call_id, host_peer_wazo_channel.id, adhoc_conference_id) remaining_participant_call_ids = set(participant_call_ids) - { host_peer_wazo_channel.id } logger.debug('adhoc conference %s: remaining participants %s', adhoc_conference_id, remaining_participant_call_ids) for participant_call_id in remaining_participant_call_ids: logger.debug( 'adhoc conference %s: looking for peer of participant %s', adhoc_conference_id, participant_call_id) discarded_host_channel_id = self._find_peer_channel( participant_call_id).id logger.debug( 'adhoc conference %s: processing participant %s and peer %s', adhoc_conference_id, participant_call_id, discarded_host_channel_id) self._redirect_participant(participant_call_id, discarded_host_channel_id, adhoc_conference_id) return { 'conference_id': adhoc_conference_id, }
def make_call_from_channel(ari, channel): channel_variables = channel.json.get('channelvars', {}) channel_helper = Channel(channel.id, ari) call = Call(channel.id) call.conversation_id = channel_helper.conversation_id() call.creation_time = channel.json['creationtime'] call.answer_time = channel_variables.get('WAZO_ANSWER_TIME') or None call.status = channel.json['state'] call.caller_id_name = channel.json['caller']['name'] call.caller_id_number = channel.json['caller']['number'] call.peer_caller_id_name = channel.json['connected']['name'] call.peer_caller_id_number = channel.json['connected']['number'] call.user_uuid = channel_helper.user() call.tenant_uuid = channel_helper.tenant_uuid() call.on_hold = channel_helper.on_hold() call.muted = channel_helper.muted() call.record_state = 'active' if channel_variables.get( 'WAZO_CALL_RECORD_ACTIVE') == '1' else 'inactive' call.bridges = [ bridge.id for bridge in ari.bridges.list() if channel.id in bridge.json['channels'] ] call.talking_to = { connected_channel.id: connected_channel.user() for connected_channel in channel_helper.connected_channels() } call.is_caller = channel_helper.is_caller() call.is_video = channel_variables.get( 'CHANNEL(videonativeformat)') != '(nothing)' call.dialed_extension = channel_helper.dialed_extension() call.sip_call_id = channel_helper.sip_call_id() call.line_id = channel_helper.line_id() return call
def _partial_call_from_channel_id(self, channel_id): channel = Channel(channel_id, self.ari) call = Call(channel.id) call.user_uuid = channel.user() call.tenant_uuid = channel.tenant_uuid() return call