api.py 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825
  1. #!/usr/bin/env python3
  2. # encoding:utf-8
  3. import json
  4. import sys
  5. from typing import Dict, Any, Optional, List
  6. from src.core.callcenter.constant import get_json_dict
  7. from src.core.callcenter.esl.constant.sip_header_constant import sipHeaderCallId, sipHeaderCaller, sipHeaderCalled, \
  8. sipHeaderDeviceId, sipHeaderDeviceType
  9. from src.core.callcenter.exception import SipUriSyntaxException
  10. from src.core.datasource import SIP_SERVER
  11. from src.core.callcenter.esl.constant.esl_constant import SPLIT, SIP_HEADER
  12. from src.core.callcenter.enumeration import CallType, DeviceType, Direction, CdrType, NextType
  13. class BaseApi:
  14. @classmethod
  15. def from_json(cls, json_string=None):
  16. json_dict = get_json_dict(json_string)
  17. return cls(**json_dict)
  18. def to_json_string(self):
  19. return json.dumps(self, default=lambda x: x.__dict__, ensure_ascii=False)
  20. def __repr__(self):
  21. return self.to_json_string()
  22. class AgentCallRequest(BaseApi):
  23. """
  24. 呼叫请求对象
  25. """
  26. def __init__(self, saas_id, agent_id, call_type: CallType, caller, called, caller_display="", called_display="",
  27. cti_flow_id=None, uuid1=None, uuid2=None, follow_data: Dict[str, Any] = {}):
  28. # 租户id
  29. self.saas_id = saas_id
  30. # 坐席号
  31. self.agent_id = agent_id
  32. # 呼叫类型
  33. self.call_type: CallType = call_type
  34. # 主叫,如果没有传,则使用坐席号码
  35. self.caller = caller
  36. # 被叫
  37. self.called = called
  38. # 主叫显号,接口没有传就用主技能组配置
  39. self.caller_display = caller_display
  40. # 被叫显号,接口没传就用主技能配置
  41. self.called_display = called_display
  42. self.cti_flow_id = cti_flow_id
  43. # uuid1
  44. self.uuid1 = uuid1
  45. # uuid2
  46. self.uuid2 = uuid2
  47. # 随路数据
  48. self.follow_data = follow_data
  49. class AgentRequest(BaseApi):
  50. def __init__(self, saas_id, agent_number, agent_name, out_id, agent_password, agent_type, phone_number, distribute,
  51. agent_state, identity_type):
  52. # 租户隔离
  53. self.saas_id = saas_id
  54. # 坐席工号, 生成返回
  55. self.agent_number = agent_number
  56. # 坐席名称
  57. self.agent_name = agent_name
  58. # 外部展示Id
  59. self.out_id = out_id
  60. # 坐席密码
  61. self.agent_password = agent_password
  62. # 坐席类型 0:普通坐席 ;1:组长:2:主管
  63. self.agent_type = agent_type
  64. # 分机号
  65. self.phone_number = phone_number
  66. # 分配标志 0:不参与排队;1:参与排队
  67. self.distribute = distribute
  68. # 账号状态 0:可用;1:禁用
  69. self.agent_state = agent_state
  70. # 身份状态
  71. self.identity_type = identity_type
  72. class AgentQueryRequest(BaseApi):
  73. def __init__(self, saas_id, agent_number, out_id, agent_type, agent_name, page=1, size=10):
  74. # 租户隔离
  75. self.saas_id = saas_id
  76. # 坐席工号
  77. self.agent_number = agent_number
  78. # 坐席名称
  79. self.agent_name = agent_name
  80. # 外部展示Id
  81. self.out_id = out_id
  82. # 坐席类型 0:普通坐席 ;1:组长:2:主管
  83. self.agent_type = agent_type
  84. self.page = page,
  85. self.size = size
  86. class AgentActionRequest(BaseApi):
  87. """
  88. 坐席操作
  89. """
  90. def __init__(self, saas_id, agent_id=None, agent_number=None, out_id=None, identity_type=None, scene='manual', service_id=None, **kwargs):
  91. self.saas_id =saas_id
  92. # 坐席工号
  93. self.agent_id = agent_id
  94. # # 坐席工号
  95. self.agent_number = agent_number
  96. # 外部展示Id
  97. self.out_id = out_id
  98. # 身份状态
  99. self.identity_type = identity_type
  100. # 场景 manual:手动外呼; robot:机器人外呼; monitor:监听
  101. self.scene = scene
  102. self.service_id = service_id
  103. # 处理多余的参数
  104. for key, value in kwargs.items():
  105. setattr(self, key, value)
  106. class HumanServiceQueryRequest(BaseApi):
  107. """人工组查询"""
  108. def __init__(self, saas_id, service_id="00000000000000000", agent_state=None, agent_ids: Dict[str, Any] = {}, **kwargs):
  109. self.saas_id = saas_id
  110. self.serviceId = service_id
  111. self.agentState = agent_state
  112. self.agentIds = agent_ids
  113. # 处理多余的参数
  114. for key, value in kwargs.items():
  115. setattr(self, key, value)
  116. class AgentMonitorData(BaseApi):
  117. """坐席状态信息"""
  118. def __init__(self, saas_id=None, agent_num=None, agent_name=None, out_id=None,
  119. check_state=None, online_state=None, check_in_time=None, check_out_time=None,
  120. service_state=None, busy_time=None, idle_time=None, call_time=None, hang_time=None,
  121. heart_state=None, heart_time=None, update_time=None, check_scene=None):
  122. self.saas_id = saas_id
  123. self.agent_num = agent_num
  124. self.agent_name = agent_name
  125. self.out_id = out_id
  126. self.check_state = check_state
  127. self.online_state = online_state
  128. self.check_in_time = check_in_time
  129. self.check_out_time = check_out_time
  130. self.service_state = service_state
  131. self.busy_time = busy_time
  132. self.idle_time = idle_time
  133. self.call_time = call_time
  134. self.hang_time = hang_time
  135. self.heart_state = heart_state
  136. self.heart_time = heart_time
  137. self.update_time = update_time
  138. self.check_scene = check_scene
  139. class AgentEventData(BaseApi):
  140. def __init__(self, app_code, user_id, data: Dict[str, Any] = {}):
  141. self.appCode = app_code
  142. self.userId = user_id
  143. self.data = data
  144. class AgentStateData(BaseApi):
  145. def __init__(self, _s=None, _t=None, status=None, time=None, assign_time:int=0, phone_num=None):
  146. self._s = _s
  147. self._t = _t
  148. self.status = status
  149. self.time = time
  150. self.assign_time = assign_time
  151. self.phone_num = phone_num
  152. class AgentDelayStateData(BaseApi):
  153. def __init__(self, saas_id=None, flow_id=None, call_id=None, agent_num=None, service_state=None, scene=None):
  154. self.saas_id = saas_id
  155. self.flow_id = flow_id
  156. self.call_id = call_id
  157. self.agent_num = agent_num
  158. self.service_state = service_state
  159. self.scene = scene
  160. class HangupCallRequest(BaseApi):
  161. def __init__(self, saas_id=None, flow_id=None, agent_id=None, called=None, call_id=None, scene=None):
  162. # SAAS_ID(必填)
  163. self.saas_id = saas_id
  164. self.flow_id = flow_id
  165. # 呼叫唯一id(选填)
  166. self.call_id = call_id
  167. self.called = called
  168. # 分机号(必填)
  169. self.agent_id = agent_id
  170. self.scene = scene
  171. class CheckInCallRequest(BaseApi):
  172. """检查是否在通话中实体"""
  173. def __init__(self, saas_id, agent_number):
  174. self.saas_id = saas_id
  175. self.agent_number = agent_number
  176. class MakeCallContext(BaseApi):
  177. def __init__(self,
  178. sip_server=SIP_SERVER,
  179. route_gateway_name="fxo-gateway",
  180. display: Optional[str] = None,
  181. caller: Optional[str] = None,
  182. called: Optional[str] = None,
  183. call_id: Optional[str] = None,
  184. device_id: Optional[str] = None,
  185. eavesdrop: Optional[str] = None,
  186. device_type: Optional[int] = None,
  187. timeout: Optional[int] = 60*2 + 10,
  188. originate_timeout: Optional[int] = 60*2 + 10,
  189. sip_header_map: Optional[Dict[str, str]] = {},
  190. called_prefix: Optional[str] = "",
  191. service_id: Optional[str] = None,
  192. call_type: Optional[int] = None):
  193. # fs 地址
  194. self.sip_server = sip_server
  195. # 线路名(非必传)
  196. self.route_gateway_name = route_gateway_name
  197. # 外显号(必传)
  198. self.display = display
  199. # 该腿的呼叫者(必传)
  200. self.caller = caller
  201. # 该腿的被呼叫者(必传)
  202. self.called = called
  203. # 该腿的callId (必传)
  204. self.call_id = call_id
  205. # 该腿的uuid(必传)
  206. self.device_id = device_id
  207. # 是否是监听腿 监听腿必填true,其它不填
  208. self.eavesdrop = eavesdrop
  209. # 当前腿的类型(必填 客户 坐席 监听者 机器人)
  210. self.device_type = device_type
  211. # 超时挂断
  212. self.timeout = timeout
  213. # 超时挂断(后面可以考虑删掉)
  214. self.originate_timeout = originate_timeout
  215. # 额外的sip头添加(非必填)
  216. self.sip_header_map = sip_header_map or {}
  217. # 被叫前缀 (用户腿必填)
  218. self.called_prefix = called_prefix
  219. # 任务id(机器人外呼必填)
  220. self.service_id = service_id
  221. # 呼叫类型(必填)
  222. self.call_type = call_type
  223. def get_sip_header(self) -> str:
  224. headers = [
  225. f"{SIP_HEADER}contact_user={self.display}",
  226. "ring_asr=true",
  227. f"origination_caller_id_number={self.display}",
  228. f"origination_caller_id_name={self.display}",
  229. f"origination_uuid={self.device_id}",
  230. "absolute_codec_string=^^:PCMU:PCMA",
  231. ]
  232. if self.originate_timeout is not None:
  233. headers.append(f"originate_timeout={self.originate_timeout}")
  234. headers += [
  235. "RECORD_STEREO=true",
  236. "RECORD_APPEND=true",
  237. f"{SIP_HEADER}{sipHeaderCallId}={self.call_id}",
  238. f"{SIP_HEADER}{sipHeaderCaller}={self.caller}",
  239. f"{SIP_HEADER}{sipHeaderCalled}={self.called}",
  240. f"{SIP_HEADER}{sipHeaderDeviceId}={self.device_id}",
  241. f"{SIP_HEADER}{sipHeaderDeviceType}={self.device_type}",
  242. ]
  243. if self.eavesdrop:
  244. headers.append(f"{SIP_HEADER}eavesdrop={self.eavesdrop}")
  245. if self.service_id:
  246. headers.append(f"{SIP_HEADER}service_id={self.service_id}")
  247. if self.device_type == DeviceType.CUSTOMER.code:
  248. headers += [
  249. "RECORD_STEREO_SWAP=true",
  250. "rx_gain=30"
  251. ]
  252. else:
  253. headers += [
  254. "RECORD_STEREO_SWAP=false",
  255. "continue_on_fail=true",
  256. "rx_gain=30"
  257. ]
  258. if self.sip_header_map:
  259. headers.extend([f"{SIP_HEADER}{k}={v}" for k, v in self.sip_header_map.items()])
  260. return SPLIT.join(headers)
  261. def get_called(self) -> str:
  262. if self.called_prefix and self.device_type == DeviceType.CUSTOMER.code:
  263. return f"{self.called_prefix}{self.called}"
  264. return self.called
  265. def get_realm(self):
  266. sip_uri = SipURI(self.sip_server)
  267. return sip_uri.get_host()
  268. class SipURI:
  269. DEFAULT_PORT = -1
  270. SIP_SCHEME = "sip"
  271. SCHEME_SEPARATOR = ':'
  272. def __init__(self, sip_uri: str):
  273. self.string_representation = sip_uri
  274. self.userinfo = None
  275. self.host = None
  276. self.port = self.DEFAULT_PORT
  277. self.uri_parameters = {}
  278. scheme = f"{self.SIP_SCHEME}{self.SCHEME_SEPARATOR}"
  279. # print('------->', sip_uri, sip_uri.startswith(scheme))
  280. if not sip_uri.startswith(scheme):
  281. raise SipUriSyntaxException(f"SIP URI must start with {scheme}")
  282. buf = sip_uri[len(scheme):]
  283. at_pos = buf.find("@")
  284. if at_pos == 0:
  285. raise SipUriSyntaxException("userinfo cannot start with a '@'")
  286. if at_pos > 0:
  287. self.userinfo = buf[:at_pos]
  288. buf = buf[at_pos + 1:]
  289. end_hostport = buf.find(";")
  290. if end_hostport == 0:
  291. raise SipUriSyntaxException("hostport not present or it cannot start with ';'")
  292. if end_hostport < 0:
  293. end_hostport = len(buf)
  294. hostport = buf[:end_hostport]
  295. buf = buf[end_hostport:]
  296. colon_pos = hostport.find(":")
  297. if colon_pos > -1:
  298. if colon_pos == len(hostport) - 1:
  299. raise SipUriSyntaxException("hostport cannot terminate with a ':'")
  300. self.port = int(hostport[colon_pos + 1:])
  301. self.host = hostport[:colon_pos]
  302. else:
  303. self.host = hostport
  304. if buf == ";":
  305. buf = ""
  306. while buf:
  307. if buf[0] == ";":
  308. buf = buf[1:]
  309. next_semicolon = buf.find(";")
  310. if next_semicolon < 0:
  311. next_semicolon = len(buf)
  312. next_equals = buf.find("=")
  313. if next_equals < 0 or next_equals > next_semicolon:
  314. next_equals = next_semicolon
  315. key = buf[:next_equals]
  316. value = buf[next_equals + 1:next_semicolon] if next_equals < next_semicolon else ""
  317. self.uri_parameters[key] = value
  318. buf = buf[next_semicolon:]
  319. def __str__(self):
  320. return self.string_representation
  321. def get_host(self):
  322. return self.host
  323. def get_port(self):
  324. return self.port
  325. def get_uri_parameters(self):
  326. return self.uri_parameters
  327. def get_userinfo(self):
  328. return self.userinfo
  329. class AgentInfo(BaseApi):
  330. def __init__(self, saas_id=None, agent_number=None, realm=None, sip_server=None,
  331. call_id=None, device_id=None, real_device_id=None, line_id=None, fs_user=None, domain=None,
  332. service_time=0, max_ready_time=0, total_ready_time=0, ready_times=0, not_ready_times=0,
  333. total_after_time=0, max_talk_time=0, total_talk_time=0, total_ring_times=0, total_answer_times=0):
  334. self.saas_id = saas_id
  335. self.agent_number = agent_number
  336. self.realm = realm
  337. self.sip_server = sip_server
  338. self.call_id = call_id
  339. self.device_id = device_id
  340. # 机器人外呼时真实fs
  341. self.real_device_id = real_device_id
  342. self.line_id = line_id
  343. self.fs_user = fs_user
  344. self.domain = domain
  345. # 坐席最近的一次服务时间,电话则是振铃时间(秒)
  346. self.service_time = service_time
  347. # 最大空闲时长
  348. self.max_ready_time = max_ready_time
  349. # 累计空闲
  350. self.total_ready_time = total_ready_time
  351. # 空闲次数
  352. self.ready_times = ready_times
  353. # 忙碌次数
  354. self.not_ready_times = not_ready_times
  355. # 累计话后时间长
  356. self.total_after_time = total_after_time
  357. # 最大通话时长
  358. self.max_talk_time = max_talk_time
  359. # 当日累计通话时长
  360. self.total_talk_time = total_talk_time
  361. # 振铃次数
  362. self.total_ring_times = total_ring_times
  363. # 当日累计接听次数
  364. self.total_answer_times = total_answer_times
  365. def get_realm(self):
  366. """
  367. Get the realm by extracting the host from the SIP server if available,
  368. otherwise return the current realm.
  369. """
  370. try:
  371. if self.sip_server:
  372. sip_uri = SipURI(self.sip_server)
  373. return sip_uri.get_host() if sip_uri.get_host() else self.realm
  374. except Exception as ex:
  375. print(f"Error parsing SIP server URI: {ex}")
  376. return self.realm
  377. def is_fs_user(self):
  378. """
  379. Determine if the agent is a FreeSWITCH user.
  380. Default is True if fs_user is None or True.
  381. """
  382. return self.fs_user is None or self.fs_user is True
  383. class CallInfo(BaseApi):
  384. def __init__(self, core_uuid=None, cti_flow_id=None, call_id=None, conference=None, saas_id=None, group_id=None,
  385. hidden_customer=0, caller_display=None, caller=None, called_display=None, called=None,
  386. number_location=None, agent_key=None, agent_name=None, login_type=None, ivr_id=None, task_id=None,
  387. media_host=None, sip_server=None, client_host=None, record=None, record2=None, record_time=None,
  388. call_time=None, call_type=None, direction=None, answer_flag=None, wait_time=None, answer_count=0,
  389. hangup_dir=None, sdk_hangup=0, hangup_code=None, answer_time=None, end_time=None, talk_time=None,
  390. first_queue_time=None, queue_start_time=None, queue_end_time=None, overflow_count=0,
  391. uuid1=None, uuid2=None, cdr_notify_url=None, queue_level=None, transfer_agent=None, bucket_type=None,
  392. user_no_answer_end_call=False, hangup_count=0, device_list=[], device_info_map: Dict[str, Any] = {}, follow_data: Dict[str, Any] = {},
  393. process_data: Dict[str, Any] = {}, next_commands=[], call_details=[]):
  394. self.core_uuid = core_uuid # 通话唯一标识
  395. self.cti_flow_id = cti_flow_id
  396. self.call_id = call_id # 通话唯一标识
  397. self.conference = conference # 会议号
  398. self.saas_id = saas_id # 企业id
  399. self.group_id = group_id # 所在技能组id
  400. self.hidden_customer = hidden_customer # 隐藏客户号码(0:不隐藏;1:隐藏)
  401. self.caller_display = caller_display # 主叫显号
  402. self.caller = caller # 主叫
  403. self.called_display = called_display # 被叫显号
  404. self.called = called # 被叫
  405. self.number_location = number_location # 号码归属地
  406. self.agent_key = agent_key # 坐席
  407. self.agent_name = agent_name # 坐席名称
  408. self.login_type = login_type # 坐席登录类型
  409. self.ivr_id = ivr_id # ivr
  410. self.task_id = task_id # 任务ID
  411. self.media_host = media_host # 媒体
  412. self.sip_server = sip_server # 服务地址
  413. self.client_host = client_host # 客户服务地址
  414. self.record = record # 录音地址
  415. self.record2 = record2 # 第三方存储地址
  416. self.record_time = record_time # 录音开始时间
  417. self.call_time = call_time # 呼叫开始时间
  418. self.call_type = call_type # 呼叫类型 (Enum CallType)
  419. self.direction = direction # 呼叫方向 (Enum Direction)
  420. self.answer_flag = answer_flag # 通话标识
  421. self.wait_time = wait_time # 等待时长
  422. self.answer_count = answer_count # 应答设备数
  423. self.hangup_dir = hangup_dir # 挂机方向 (1主叫挂机, 2:被叫挂机, 3:平台挂机)
  424. self.sdk_hangup = sdk_hangup # sdk挂机标志
  425. self.hangup_code = hangup_code # 挂机原因
  426. self.answer_time = answer_time # 接听时间
  427. self.end_time = end_time # 最后一侧电话挂机时间
  428. self.talk_time = talk_time # 通话时长(秒)
  429. self.first_queue_time = first_queue_time # 第一次进队列时间
  430. self.queue_start_time = queue_start_time # 进入技能组时间
  431. self.queue_end_time = queue_end_time # 出技能组时间
  432. self.overflow_count = overflow_count # 溢出次数
  433. self.uuid1 = uuid1 # uuid1
  434. self.uuid2 = uuid2 # uuid2
  435. self.cdr_notify_url = cdr_notify_url # 话单通知地址
  436. self.queue_level = queue_level # 排队等级,默认是进队列时间
  437. self.transfer_agent = transfer_agent #是否转人工
  438. self.bucket_type = bucket_type #呼入流量类型
  439. self.user_no_answer_end_call = user_no_answer_end_call #用户未接听挂机
  440. self.hangup_count = hangup_count
  441. self.device_list = device_list # 当前通话的设备
  442. self.device_info_map = device_info_map
  443. self.follow_data = follow_data # 呼叫随路数据(作为落单数据)
  444. self.process_data = process_data # 模块流程间数据
  445. self.next_commands = next_commands # 执行下一步命令
  446. self.call_details = call_details # 电话流程
  447. @classmethod
  448. def from_json(cls, json_string=None):
  449. data = get_json_dict(json_string)
  450. device_info_map: Dict[str, Any] = {key: DeviceInfo(**value) for key, value in data.get('device_info_map', {}).items()}
  451. follow_data: Dict[str, Any] = {key: value for key, value in data.get('follow_data', {}).items()}
  452. process_data: Dict[str, Any] = {key: value for key, value in data.get('process_data', {}).items()}
  453. next_commands = [NextCommand(**x) for x in data.get('next_commands', [])]
  454. call_details = [CallDetail(**x) for x in data.get('call_details', [])]
  455. return cls(core_uuid=data.get('core_uuid'), cti_flow_id=data.get('cti_flow_id'), call_id=data.get('call_id'),
  456. conference=data.get('conference'), saas_id=data.get('saas_id'), group_id=data.get('group_id'),
  457. hidden_customer=data.get('hidden_customer'), caller_display=data.get('caller_display'),
  458. caller=data.get('caller'), called_display=data.get('called_display'), called=data.get('called'),
  459. number_location=data.get('number_location'), agent_key=data.get('agent_key'),
  460. agent_name=data.get('agent_name'), login_type=data.get('login_type'), ivr_id=data.get('ivr_id'),
  461. task_id=data.get('task_id'), media_host=data.get('media_host'), sip_server=data.get('sip_server'),
  462. client_host=data.get('client_host'), record=data.get('record'), record2=data.get('record2'),
  463. record_time=data.get('record_time'), call_time=data.get('call_time'),
  464. call_type=data.get('call_type'), direction=data.get('direction'),
  465. answer_flag=data.get('answer_flag'), wait_time=data.get('wait_time'),
  466. answer_count=data.get('answer_count'), hangup_dir=data.get('hangup_dir'),
  467. sdk_hangup=data.get('sdk_hangup'), hangup_code=data.get('hangup_code'),
  468. answer_time=data.get('answer_time'), end_time=data.get('end_time'), talk_time=data.get('talk_time'),
  469. first_queue_time=data.get('first_queue_time'), queue_start_time=data.get('queue_start_time'),
  470. queue_end_time=data.get('queue_end_time'), overflow_count=data.get('overflow_count'),
  471. uuid1=data.get('uuid1'), uuid2=data.get('uuid2'), cdr_notify_url=data.get('cdr_notify_url'),
  472. queue_level=data.get('queue_level'), transfer_agent=data.get('transfer_agent'),
  473. bucket_type=data.get('bucket_type'), user_no_answer_end_call=data.get('user_no_answer_end_call'),
  474. hangup_count=data.get('hangup_count'), device_list=data.get('device_list', []),device_info_map=device_info_map,
  475. follow_data=follow_data, process_data=process_data,
  476. next_commands=next_commands, call_details=call_details)
  477. def __str__(self):
  478. return self.to_json_string()
  479. def to_json_string(self):
  480. new_dict = {
  481. "core_uuid": self.core_uuid,
  482. "cti_flow_id": self.cti_flow_id,
  483. "call_id": self.call_id,
  484. "conference": self.conference,
  485. "saas_id": self.saas_id,
  486. "group_id": self.group_id,
  487. "hidden_customer": self.hidden_customer,
  488. "caller_display": self.caller_display,
  489. "caller": self.caller,
  490. "called_display": self.called_display,
  491. "called": self.called,
  492. "number_location": self.number_location,
  493. "agent_key": self.agent_key,
  494. "agent_name": self.agent_name,
  495. "login_type": self.login_type,
  496. "ivr_id": self.ivr_id,
  497. "task_id": self.task_id,
  498. "media_host": self.media_host,
  499. "sip_server": self.sip_server,
  500. "client_host": self.client_host,
  501. "record": self.record,
  502. "record2": self.record2,
  503. "record_time": self.record_time,
  504. "call_time": self.call_time,
  505. "call_type": self.call_type,
  506. "direction": self.direction,
  507. "answer_flag": self.answer_flag,
  508. "wait_time": self.wait_time,
  509. "answer_count": self.answer_count,
  510. "hangup_dir": self.hangup_dir,
  511. "sdk_hangup": self.sdk_hangup,
  512. "hangup_code": self.hangup_code,
  513. "answer_time": self.answer_time,
  514. "end_time": self.end_time,
  515. "talk_time": self.talk_time,
  516. "first_queue_time": self.first_queue_time,
  517. "queue_start_time": self.queue_start_time,
  518. "queue_end_time": self.queue_end_time,
  519. "overflow_count": self.overflow_count,
  520. "uuid1": self.uuid1,
  521. "uuid2": self.uuid2,
  522. "cdr_notify_url": self.cdr_notify_url,
  523. "queue_level": self.queue_level,
  524. "transfer_agent": self.transfer_agent,
  525. "bucket_type": self.bucket_type,
  526. "user_no_answer_end_call": self.user_no_answer_end_call,
  527. "hangup_count": self.hangup_count,
  528. "device_list": [x for x in self.device_list],
  529. "device_info_map": {key: vars(value) for key, value in self.device_info_map.items()},
  530. "follow_data": {key: vars(value) for key, value in self.follow_data.items()},
  531. "process_data": {key: vars(value) for key, value in self.process_data.items()},
  532. "next_commands": [vars(x) for x in self.next_commands],
  533. "call_details": [vars(x) for x in self.call_details],
  534. }
  535. # print('call_info is :', new_dict, flush=True)
  536. return json.dumps(new_dict, default=lambda x: x.__dict__, ensure_ascii=False)
  537. class DeviceInfo(BaseApi):
  538. def __init__(self, cti_flow_id=None, call_id=None, conference=None, device_id=None, agent_key=None, agent_name=None, device_type=None,
  539. cdr_type=0, from_agent=None, caller=None, called=None, display=None, called_location=None,
  540. caller_location=None, call_time=None, ring_start_time=None, ring_end_time=None, answer_time=None,
  541. bridge_time=None, end_time=None, talk_time=None, sip_protocol=None, channel_name=None,
  542. hangup_cause=None, ring_cause=None, sip_status=None, record=None, record_time=None,
  543. record_start_time=None, state=None, apparent_number=None, caller_display=None):
  544. self.cti_flow_id = cti_flow_id
  545. self.call_id = call_id # 通话唯一标识
  546. self.conference = conference # 会议模式
  547. self.device_id = device_id # 设备id
  548. self.agent_key = agent_key # 坐席
  549. self.agent_name = agent_name # 坐席
  550. self.device_type = device_type # 1:坐席,2:客户,3:外线
  551. self.cdr_type = cdr_type # 1:呼入,2:外呼,3:内呼,4:转接,5:咨询,6:监听,7:强插,8:耳语
  552. self.from_agent = from_agent # 咨询或转接来源
  553. self.caller = caller # 主叫
  554. self.called = called # 被叫
  555. self.display = display # 显号
  556. self.called_location = called_location # 被叫归属地
  557. self.caller_location = caller_location # 被叫归属地
  558. self.call_time = call_time # 呼叫开始时间
  559. self.ring_start_time = ring_start_time # 振铃开始时间
  560. self.ring_end_time = ring_end_time # 振铃结束时间
  561. self.answer_time = answer_time # 接通时间
  562. self.bridge_time = bridge_time # 桥接时间
  563. self.end_time = end_time # 结束时间
  564. self.talk_time = talk_time # 通话时长
  565. self.sip_protocol = sip_protocol # 信令协议(tcp/udp)
  566. self.channel_name = channel_name # 呼叫地址
  567. self.hangup_cause = hangup_cause # 挂机原因
  568. self.ring_cause = ring_cause # 回铃音识别
  569. self.sip_status = sip_status # sip状态
  570. self.record = record # 录音地址
  571. self.record_time = record_time # 录音时长
  572. self.record_start_time = record_start_time # 录音开始时间
  573. self.state = state # 当前设备状态
  574. self.apparent_number = apparent_number # 外显号
  575. self.caller_display = caller_display # 主叫号
  576. class NextCommand(BaseApi):
  577. def __init__(self, device_id, next_type, next_value=None):
  578. # 记录执行设备
  579. self.device_id = device_id
  580. # 下一步执行命令
  581. self.next_type = next_type
  582. # 执行参数
  583. self.next_value = next_value
  584. class DelayAction(BaseApi):
  585. def __init__(self, uuid=None, call_id=None, device_id=None):
  586. self.uuid=uuid
  587. self.call_id = call_id
  588. self.device_id = device_id
  589. class CallDetail(BaseApi):
  590. def __init__(self, id=None, cts=None, start_time=None, end_time=None, call_id=None,
  591. detail_index=None, transfer_type=None, transfer_id=None, reason=None,
  592. month=None, ext1=None, ext2=None, status=None):
  593. self.id = id # PK
  594. self.cts = cts # 创建时间
  595. self.start_time = start_time # 开始时间
  596. self.end_time = end_time # 结束时间
  597. self.call_id = call_id # 通话ID
  598. self.detail_index = detail_index # 顺序
  599. self.transfer_type = transfer_type # 类型
  600. self.transfer_id = transfer_id # 转接ID
  601. self.reason = reason # 出队列原因
  602. self.month = month # 月份
  603. self.ext1 = ext1 # 扩展字段1
  604. self.ext2 = ext2 # 扩展字段2
  605. self.status = status # 状态
  606. class RouteGateway(BaseApi):
  607. def __init__(self, id=None, saas_id=None, cts=None, uts=None, name=None, media_host=None,
  608. media_port=None, caller_prefix=None, called_prefix=None,
  609. profile='gateway/gateway-fxo', sip_header1='P-LIBRA-CallId=#{[callId]}',
  610. sip_header2='P-LIBRA-deviceId=#{[deviceId]}', sip_header3=None, status=None):
  611. self.id = id # PK
  612. self.saas_id = saas_id
  613. self.cts = cts # 创建时间
  614. self.uts = uts # 修改时间
  615. self.name = name # 号码
  616. self.media_host = media_host # 媒体地址
  617. self.media_port = media_port # 媒体端口
  618. self.caller_prefix = caller_prefix # 主叫号码前缀
  619. self.called_prefix = called_prefix # 被叫号码前缀
  620. self.profile = profile # 媒体拨号计划文件
  621. self.sip_header1 = sip_header1 # SIP头1
  622. self.sip_header2 = sip_header2 # SIP头2
  623. self.sip_header3 = sip_header3 # SIP头3
  624. self.status = status # 状态
  625. # 机器人外呼
  626. class BotChatRequest:
  627. def __init__(self, nodeId=None, userId=None, sessionId=None, taskId=None, asrText=None, recordId=None,
  628. ext: Optional[str] = None):
  629. self.nodeId = nodeId # 节点id
  630. self.userId = userId # 用户id
  631. self.sessionId = sessionId # 会话id
  632. self.recordId = recordId # 唯一标识
  633. self.taskId = taskId # 机器人任务id
  634. self.asrText = asrText # asr识别的文本
  635. self.ext = ext
  636. def to_json_string(self):
  637. return json.dumps(self.__dict__, ensure_ascii=False)
  638. @classmethod
  639. def from_json(cls, json_string):
  640. data = json.loads(json_string)
  641. return cls(**data)
  642. # def __repr__(self):
  643. # return (f"OutboundRobotRequestParams(node_id={self.node_id}, user_id={self.user_id}, "
  644. # f"session_id={self.session_id}, record_id={self.record_id}, task_id={self.task_id}, "
  645. # f"event_type={self.event_type}, asr_text={self.asr_text}, key_input={self.key_input})")
  646. class ChatAction:
  647. def __init__(self, action_code=None, action_content=None):
  648. self.action_code = action_code # normal:正常通话;hang:挂断;transfer:转人工
  649. self.action_content = action_content # 动作内容
  650. def to_json_string(self):
  651. return json.dumps(self.__dict__, ensure_ascii=False)
  652. @classmethod
  653. def from_json(cls, json_data):
  654. return cls(
  655. action_code=json_data.get("action_code"),
  656. action_content=json_data.get("action_content")
  657. )
  658. class ChatContent:
  659. def __init__(self, content_type=None, content=None, voice_url=None, voice_content=None):
  660. self.content_type = content_type # 播放类型
  661. self.content = content # 播放内容
  662. self.voice_url = voice_url # 语音地址
  663. self.voice_content = voice_content # 语音文本
  664. def to_json_string(self):
  665. return json.dumps(self.__dict__, ensure_ascii=False)
  666. @classmethod
  667. def from_json(cls, json_data):
  668. return cls(
  669. content_type=json_data.get("content_type"),
  670. content=json_data.get("content"),
  671. voice_url=json_data.get("voice_url"),
  672. voice_content=json_data.get("voice_content")
  673. )
  674. class ChatMessage:
  675. def __init__(self, node_id=None, contents=None, wait_time=None,
  676. action=None, inputType=None):
  677. self.node_id = node_id # 节点id
  678. self.contents = contents if contents is not None else [] # 内容列表
  679. self.wait_time = wait_time # 用户静默时长
  680. self.action = action # 动作代码
  681. self.inputType = inputType # dtmf类型
  682. def to_json_string(self):
  683. return json.dumps({
  684. "node_id": self.node_id,
  685. "contents": [content.__dict__ for content in self.contents],
  686. "wait_time": self.wait_time,
  687. "action": self.action.__dict__ if self.action else None,
  688. "inputType": self.inputType
  689. }, ensure_ascii=False)
  690. @classmethod
  691. def from_json(cls, json_data):
  692. contents = [ChatContent.from_json(item) for item in json_data.get("contents", [])]
  693. action = ChatAction.from_json(json_data.get("action", {})) if json_data.get("action") else None
  694. return cls(
  695. node_id=json_data.get("node_id"),
  696. contents=contents,
  697. wait_time=json_data.get("wait_time"),
  698. action=action,
  699. inputType=json_data.get("inputType")
  700. )
  701. class ChatResponse:
  702. def __init__(self, data=None, message=None, code=None):
  703. self.data = data if data is not None else ChatMessage()
  704. self.message = message
  705. self.code = code
  706. def to_json_string(self):
  707. return json.dumps({
  708. "data": json.loads(self.data.to_json_string()),
  709. "message": self.message,
  710. "code": self.code
  711. }, ensure_ascii=False)
  712. @classmethod
  713. def from_json(cls, json_string):
  714. data = json.loads(json_string)
  715. response_data = ChatMessage.from_json(data.get("data", {}))
  716. return cls(
  717. data=response_data,
  718. message=data.get("message"),
  719. code=data.get("code")
  720. )