# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/ require 'builder' class Integration::PlacetelController < ApplicationController skip_before_action :verify_csrf_token before_action :check_configured, :check_token # notify about inbound call / block inbound call def event local_params = ActiveSupport::HashWithIndifferentAccess.new(params.permit!.to_h) # do placetel event mapping if local_params['event'] == 'IncomingCall' local_params['direction'] = 'in' local_params['event'] = 'newCall' elsif local_params['event'] == 'HungUp' local_params['event'] = 'hangup' elsif local_params['event'] == 'CallAccepted' local_params['event'] = 'answer' end if local_params['user'].blank? && local_params['peer'] local_params['user'] = get_voip_user_by_peer(local_params['peer']) end if local_params['direction'].blank? entry = Cti::Log.find_by(call_id: params[:call_id]) if entry local_params['direction'] = entry.direction end end if local_params['type'] == 'missed' local_params['cause'] = 'cancel' elsif local_params['type'] == 'voicemail' local_params['cause'] = 'voicemail' elsif local_params['type'] == 'blocked' local_params['cause'] = 'blocked' elsif local_params['type'] == 'accepted' local_params['cause'] = 'normalClearing' end if local_params['direction'] == 'in' if local_params['event'] == 'newCall' config_inbound = config_integration[:inbound] || {} block_caller_ids = config_inbound[:block_caller_ids] || [] # check if call need to be blocked block_caller_ids.each do |item| next unless item[:caller_id] == local_params['from'] xml = Builder::XmlMarkup.new(indent: 2) xml.instruct! content = xml.Response() do xml.Reject('reason' => 'busy') end send_data content, type: 'application/xml; charset=UTF-8;' #local_params['Reject'] = 'busy' local_params['comment'] = 'reject, busy' if local_params['user'] local_params['comment'] = "#{local_params['user']} -> reject, busy" end Cti::Log.process(local_params) return true end end Cti::Log.process(local_params) xml = Builder::XmlMarkup.new(indent: 2) xml.instruct! content = xml.Response() send_data content, type: 'application/xml; charset=UTF-8;' return true elsif local_params['direction'] == 'out' config_outbound = config_integration[:outbound] routing_table = nil default_caller_id = nil if config_outbound.present? routing_table = config_outbound[:routing_table] default_caller_id = config_outbound[:default_caller_id] end xml = Builder::XmlMarkup.new(indent: 2) xml.instruct! # set callerId content = nil to = local_params[:to] from = nil if to && routing_table.present? routing_table.each do |row| dest = row[:dest].gsub(/\*/, '.+?') next if to !~ /^#{dest}$/ from = row[:caller_id] content = xml.Response() do xml.Dial(callerId: from) { xml.Number(params[:to]) } end break end if !content && default_caller_id.present? from = default_caller_id content = xml.Response() do xml.Dial(callerId: default_caller_id) { xml.Number(params[:to]) } end end else content = xml.Response() end send_data(content, type: 'application/xml; charset=UTF-8;') if from.present? local_params['from'] = from end Cti::Log.process(local_params) return true end response_error('Invalid direction!') end private def check_token if Setting.get('placetel_token') != params[:token] response_unauthorized('Invalid token, please contact your admin!') return end true end def check_configured http_log_config facility: 'placetel' if !Setting.get('placetel_integration') response_error('Feature is disable, please contact your admin to enable it!') return end if config_integration.blank? || config_integration[:inbound].blank? || config_integration[:outbound].blank? response_error('Feature not configured, please contact your admin!') return end true end def xml_error(error, code) xml = Builder::XmlMarkup.new(indent: 2) xml.instruct! content = xml.Response() do xml.Error(error) end send_data content, type: 'application/xml; charset=UTF-8;', status: code end def config_integration @config_integration ||= Setting.get('placetel_config') end def response_error(error) xml_error(error, 422) end def response_unauthorized(error) xml_error(error, 401) end def get_voip_user_by_peer(peer) load_voip_users[peer] end def load_voip_users return {} if config_integration.blank? || config_integration[:api_token].blank? list = Cache.get('placetelGetVoipUsers') return list if list response = UserAgent.post( 'https://api.placetel.de/api/getVoIPUsers.json', { api_key: config_integration[:api_token], }, { log: { facility: 'placetel', }, json: true, open_timeout: 4, read_timeout: 6, total_timeout: 6, }, ) if !response.success? logger.error "Can't fetch getVoipUsers from '#{url}', http code: #{response.code}" Cache.write('placetelGetVoipUsers', {}, { expires_in: 1.hour }) return {} end result = response.data if result.blank? logger.error "Can't fetch getVoipUsers from '#{url}', result: #{response.inspect}" Cache.write('placetelGetVoipUsers', {}, { expires_in: 1.hour }) return {} end if result.is_a?(Hash) && (result['result'] == '-1' || result['result_code'] == 'error') logger.error "Can't fetch getVoipUsers from '#{url}', result: #{result.inspect}" Cache.write('placetelGetVoipUsers', {}, { expires_in: 1.hour }) return {} end if !result.is_a?(Array) logger.error "Can't fetch getVoipUsers from '#{url}', result: #{result.inspect}" Cache.write('placetelGetVoipUsers', {}, { expires_in: 1.hour }) return {} end list = {} result.each do |entry| next if entry['name'].blank? if entry['uid'].present? list[entry['uid']] = entry['name'] end next if entry['uid2'].blank? list[entry['uid2']] = entry['name'] end Cache.write('placetelGetVoipUsers', list, { expires_in: 24.hours }) list end end