Browse Source

Merge branch 'develop' into dev_20241205

余尚辉 4 months ago
parent
commit
184bbbd883

+ 2 - 2
docker-compose.yml

@@ -1,11 +1,11 @@
 services:
   pjsua:
-    image: pjsua2:v2.14.1202
+    image: pjsua2:v2.14.1203
     container_name: pjsua
     restart: always
 #    network_mode: host
     volumes:
-#      - /Users/yushanghui/hongshantianping/git/voice-gateway-service:/code
+#      - /Users/davidliu/git/voice-gateway-service:/code
       - /home/hongshan/voice-gateway-service:/code
       - /root/aibot/dm/voice:/root/aibot/dm/voice
     environment:

+ 3 - 1
requirements.txt

@@ -7,4 +7,6 @@ gunicorn
 pymysql
 redis
 gevent
-flask_sqlalchemy
+flask_sqlalchemy
+prometheus_client
+prometheus_flask_exporter

+ 2 - 0
src/core/callcenter/__init__.py

@@ -7,6 +7,7 @@ from .config import BaseConfig
 from flask import Flask, request, render_template_string
 from flask_sqlalchemy import SQLAlchemy
 from flask_cors import CORS
+from prometheus_flask_exporter import PrometheusMetrics
 from .constant import error_response
 from .exception import BizException
 
@@ -16,6 +17,7 @@ logging.getLogger('apscheduler').setLevel(logging.CRITICAL)
 
 def create_app():
     _app = Flask(__name__)
+    _metrics = PrometheusMetrics(_app)
     # 添加配置文件
     _app.config.from_object(BaseConfig)
 

+ 5 - 3
src/core/callcenter/api.py

@@ -455,8 +455,8 @@ class CallInfo(BaseApi):
                  call_time=None, call_type=None, direction=None, answer_flag=None, wait_time=None, answer_count=0,
                  hangup_dir=None, sdk_hangup=0, hangup_code=None, answer_time=None, end_time=None, talk_time=None,
                  first_queue_time=None, queue_start_time=None, queue_end_time=None, overflow_count=0,
-                 uuid1=None, uuid2=None, cdr_notify_url=None, queue_level=None, transfer_agent=None, user_no_answer_end_call=False, device_list=[],
-                 device_info_map: Dict[str, Any] = {}, follow_data: Dict[str, Any] = {},
+                 uuid1=None, uuid2=None, cdr_notify_url=None, queue_level=None, transfer_agent=None, bucket_type=None,
+                 user_no_answer_end_call=False, device_list=[], device_info_map: Dict[str, Any] = {}, follow_data: Dict[str, Any] = {},
                  process_data: Dict[str, Any] = {}, next_commands=[], call_details=[]):
         self.core_uuid = core_uuid  # 通话唯一标识
         self.cti_flow_id = cti_flow_id
@@ -502,6 +502,7 @@ class CallInfo(BaseApi):
         self.cdr_notify_url = cdr_notify_url  # 话单通知地址
         self.queue_level = queue_level  # 排队等级,默认是进队列时间
         self.transfer_agent = transfer_agent #是否转人工
+        self.bucket_type = bucket_type #呼入流量类型
         self.user_no_answer_end_call = user_no_answer_end_call #用户未接听挂机
         self.device_list = device_list  # 当前通话的设备
         self.device_info_map = device_info_map
@@ -536,7 +537,7 @@ class CallInfo(BaseApi):
                    queue_end_time=data.get('queue_end_time'), overflow_count=data.get('overflow_count'),
                    uuid1=data.get('uuid1'), uuid2=data.get('uuid2'), cdr_notify_url=data.get('cdr_notify_url'),
                    queue_level=data.get('queue_level'), transfer_agent=data.get('transfer_agent'),
-                   user_no_answer_end_call=data.get('user_no_answer_end_call'),
+                   bucket_type=data.get('bucket_type'), user_no_answer_end_call=data.get('user_no_answer_end_call'),
                    device_list=data.get('device_list', []),device_info_map=device_info_map,
                    follow_data=follow_data, process_data=process_data,
                    next_commands=next_commands, call_details=call_details)
@@ -590,6 +591,7 @@ class CallInfo(BaseApi):
             "cdr_notify_url": self.cdr_notify_url,
             "queue_level": self.queue_level,
             "transfer_agent": self.transfer_agent,
+            "bucket_type": self.bucket_type,
             "user_no_answer_end_call": self.user_no_answer_end_call,
             "device_list": [x for x in self.device_list],
             "device_info_map": {key: vars(value) for key, value in self.device_info_map.items()},

+ 13 - 8
src/core/callcenter/esl/client.py

@@ -26,6 +26,7 @@ from src.core.callcenter.esl.constant.esl_constant import BRIDGE_VARIABLES, BRID
     ORIGINATE, PARK, SET, EAVESDROP, SMF_ALEG, EXECUTE, PLAYBACK, PAUSE, TRANSFER, UUID_TRANSFER, UUID_BROADCAST, \
     UUID_BREAK, UUID_HOLD, \
     UUID_RECORD, UUID_SETVAR, UUID_GETVAR, UUID_KILL
+import src.core.registry as Registry
 import src.core.callcenter.esl.utils.esl_event_util as EslEventUtil
 import src.core.callcenter.esl.handler as event_handler
 from src.core.callcenter.esl.constant.sip_header_constant import sipHeaderHoldMusic, profile1, profile2, sipHeaderCallId
@@ -522,26 +523,30 @@ class OutboundClient:
                     kwargs['variable_sip_h_P-LIBRA-CallId'] = call_id
                     kwargs['variable_sip_h_P-LIBRA-DeviceId'] = new_device_id
 
+                    _bucket_call_type = 2
+                    destination = None
                     bucket = self.server.get_bucket(call_id)
                     whitelist = self.server.get_whitelist()
                     if bucket and bucket.name == 'AI':
                         #转到ai机器人
+                        _bucket_call_type = 1
                         destination = self.server.agent.register(**kwargs)
                         self.server.logger.info("device_id=%s, destination=%s, new_device_id=%s" % (device_id, destination, new_device_id))
-                        self.build_call_info(call_id, device_id, new_device_id, str(destination), **kwargs)
+                        self.build_call_info(call_id, device_id, new_device_id, str(destination), _bucket_call_type, **kwargs)
                         self.server.cache.add_device_user_part(device_id, destination)
                         con.execute("bridge", "{sip_h_P-LIBRA-CallId=%s,sip_h_P-LIBRA-DeviceId=%s,origination_uuid=%s}user/%s" % (call_id, new_device_id, new_device_id, destination), device_id)
-                        self.server.dataHandleServer.create_record(call_id, caller_number, 1, service_category=1, destination=destination)
                     elif caller_number in whitelist:
                         # 检查白名单
-                        self.build_call_info(call_id, device_id, new_device_id, destination=None, **kwargs)
+                        _bucket_call_type = 0
+                        self.build_call_info(call_id, device_id, new_device_id, destination=None, bucket_type=_bucket_call_type, **kwargs)
                         self.server.agent.acd_service.transfer_to_agent(call_id, new_device_id)
-                        self.server.dataHandleServer.create_record(call_id, caller_number, 0, service_category=None, destination=None)
                     else:
                         # 传统服务
-                        self.build_call_info(call_id, device_id, new_device_id, destination=None, **kwargs)
+                        self.build_call_info(call_id, device_id, new_device_id, destination=None, bucket_type=_bucket_call_type,  **kwargs)
                         self.server.agent.acd_service.transfer_to_agent(call_id, new_device_id)
-                        self.server.dataHandleServer.create_record(call_id, caller_number, 2, service_category=None, destination=None)
+
+                    Registry.counter('call_incoming_requests', '呼入流量统计', ['bucket_type', str(_bucket_call_type)]).inc()
+                    self.server.dataHandleServer.create_record(call_id, caller_number, _bucket_call_type, service_category=None, destination=destination)
 
                     # # 检查白名单
                     # if caller_number in whitelist:
@@ -588,7 +593,7 @@ class OutboundClient:
             except:
                 traceback.print_exc()
 
-        def build_call_info(self, call_id, device_id, new_device_id, destination, **kwargs):
+        def build_call_info(self, call_id, device_id, new_device_id, destination, bucket_type, **kwargs):
             caller = kwargs.get('Channel-Caller-ID-Number')
             called = destination
             now = datetime.utcnow().timestamp()
@@ -596,7 +601,7 @@ class OutboundClient:
             call_info = CallInfo(call_id=call_id, agent_key=destination,
                                  caller=caller, called=called, direction=Direction.INBOUND.code,
                                  call_type=CallType.BOT_CALL.code, call_time=now,
-                                 uuid1=call_id, uuid2=device_id, saas_id=saasId,
+                                 uuid1=call_id, uuid2=device_id, saas_id=saasId, bucket_type=bucket_type,
                                  core_uuid=None, cti_flow_id=None, conference=None, group_id=None, hidden_customer=0, caller_display=None, called_display=None, number_location=None, agent_name=None, login_type=None, ivr_id=None, task_id=None, media_host=None, sip_server=None, client_host=None, record=None, record2=None, record_time=None, answer_flag=None, wait_time=None, answer_count=0, hangup_dir=None, sdk_hangup=0, hangup_code=None, answer_time=None, end_time=None, talk_time=None, first_queue_time=None, queue_start_time=None, queue_end_time=None, overflow_count=0, cdr_notify_url=None, queue_level=None, transfer_agent=None,device_list=[], device_info_map = {}, follow_data = {}, process_data = {}, next_commands=[], call_details=[])
             device_custom = DeviceInfo(device_id=device_id, call_time=now,
                                      call_id=call_id, device_type=DeviceType.CUSTOMER.code,

+ 12 - 3
src/core/callcenter/esl/handler/channel_answer_handler.py

@@ -3,9 +3,10 @@
 import json
 import time
 from datetime import datetime
-from src.core.callcenter.constant import saasId, get_record_prefix,get_record_file_name
-from src.core.callcenter.enumeration import NextType, AnswerFlag, Direction, DeviceType, AgentScene, CdrType, \
-    WorkStatus, AgentServiceState, CallStage, CallType
+import src.core.registry as Registry
+from src.core.callcenter.constant import saasId, get_record_prefix, get_record_file_name
+from src.core.callcenter.enumeration import NextType, AnswerFlag, Direction, DeviceType, AgentScene, CdrType, CallType, \
+    CallStage, CallType
 from src.core.callcenter.esl.annotation import EslEventName
 import src.core.callcenter.esl.utils.esl_event_util as EslEventUtil
 from src.core.callcenter.esl.constant.event_names import CHANNEL_ANSWER
@@ -111,6 +112,10 @@ class ChannelAnswerHandler(EslEventHandler):
     def call_bridge(self, call: CallInfo, device: DeviceInfo, next_command: NextCommand, event):
         self.logger.info("开始桥接电话: callId:%s, caller:%s, called:%s, device1:%s, device2:%s"% (call.call_id,
                          call.caller, call.called, next_command.device_id, next_command.next_value))
+        if CallType.BOT_CALL.code == call.call_type:
+            call.answer_flag = AnswerFlag.ROBOT_ANSWER.code
+            Registry.counter('call_incoming_bot_answer_requests', '呼入机器人接听量',['bucket_type', str(call.bucket_type)]).inc()
+
         self.record(event, device_id=device.device_id)
         device1 = call.device_info_map.get(next_command.device_id)
         device2 = call.device_info_map.get(next_command.next_value)
@@ -127,6 +132,9 @@ class ChannelAnswerHandler(EslEventHandler):
         # 转接电话 deviceInfo为被转接设备
         from_device_id = next_command.device_id
         device_id = EslEventUtil.getDeviceId(event)
+        call.answer_flag = AnswerFlag.AGENT_ANSWER.code
+        if CallType.BOT_CALL.code == call.call_type:
+            Registry.counter('call_incoming_transfer_requests', '呼入机器人转人量',['bucket_type', str(call.bucket_type)]).inc()
         self.record(event, device_id)
         call.next_commands.append(NextCommand(device_id, NextType.NEXT_TRANSFER_SUCCESS.code, call.device_list[1]))
         self.logger.info("转接电话中 callId:%s, from:%s, to:%s "% (call.call_id, from_device_id, device_id))
@@ -134,6 +142,7 @@ class ChannelAnswerHandler(EslEventHandler):
         self.logger.info("转接完成 callId:%s, from:%s, to:%s "% (call.call_id, device_id, call.device_list[0]))
         self.inbound_client.bridge_call(call.call_id, device_id, call.device_list[0])
 
+
     def listen(self, call: CallInfo, device: DeviceInfo, next_command: NextCommand, event):
         device_id = EslEventUtil.getDeviceId(event)
         self.logger.info("开始监听 callId:%s, deviceId:%s, nextCommandDeviceId:%s", call.call_id, device_id, next_command.next_value)

+ 20 - 0
src/core/registry.py

@@ -0,0 +1,20 @@
+#!/usr/bin/env python3
+# encoding:utf-8
+
+import prometheus_client
+from typing import Iterable
+from prometheus_client import Counter, Gauge, Histogram, Summary
+from prometheus_client.core import CollectorRegistry
+
+
+def counter(name: str, documentation: str, labelnames: Iterable[str] = ()):
+    return Counter(name, documentation, labelnames)
+
+def gauge(name: str, documentation: str, labelnames: Iterable[str] = ()):
+    return Gauge(name, documentation, labelnames)
+
+def histogram(name: str, documentation: str, labelnames: Iterable[str] = ()):
+    return Histogram(name, documentation, labelnames)
+
+def summary(name: str, documentation: str, labelnames: Iterable[str] = ()):
+    return Summary(name, documentation, labelnames)