|
@@ -35,25 +35,6 @@ class UserStatus(Enum):
|
|
|
silence = 2
|
|
|
|
|
|
|
|
|
-# Subclass to extend the Account and get notifications etc.
|
|
|
-class Account(pj.Account):
|
|
|
- def __init__(self, user_agent):
|
|
|
- pj.Account.__init__(self)
|
|
|
- self.user_agent = user_agent
|
|
|
-
|
|
|
- def onRegState(self, prm):
|
|
|
- print("***OnRegState: " + prm.reason)
|
|
|
-
|
|
|
- def onIncomingCall(self, prm):
|
|
|
- print("***onIncomingCall: ", prm.callId)
|
|
|
-
|
|
|
- call = MyCall(self, prm.callId)
|
|
|
- call_op_param = pj.CallOpParam(True)
|
|
|
- call_op_param.statusCode = pj.PJSIP_SC_OK
|
|
|
- call.answer(call_op_param)
|
|
|
- self.user_agent.calls[prm.callId] = call
|
|
|
-
|
|
|
-
|
|
|
class MyAudioMediaPort(pj.AudioMediaPort):
|
|
|
def __init__(self, asr=None):
|
|
|
pj.AudioMediaPort.__init__(self)
|
|
@@ -85,11 +66,36 @@ class MyAudioMediaPlayer(pj.AudioMediaPlayer):
|
|
|
self.stopTransmit(self.sink)
|
|
|
|
|
|
|
|
|
+# Subclass to extend the Account and get notifications etc.
|
|
|
+class Account(pj.Account):
|
|
|
+ def __init__(self, agent, user_part, **kwargs):
|
|
|
+ pj.Account.__init__(self)
|
|
|
+ self.agent = agent
|
|
|
+ self.user_part = user_part
|
|
|
+ self.calls = {}
|
|
|
+ self.kwargs = kwargs
|
|
|
+
|
|
|
+ def onRegState(self, prm):
|
|
|
+ print("***OnRegState: " + prm.reason)
|
|
|
+
|
|
|
+ def onIncomingCall(self, prm):
|
|
|
+ print("***onIncomingCall: ", prm.callId, json.dumps(self.kwargs))
|
|
|
+
|
|
|
+ call = MyCall(self.agent, self, self.user_part, prm.callId, **self.kwargs)
|
|
|
+ call_op_param = pj.CallOpParam(True)
|
|
|
+ call_op_param.statusCode = pj.PJSIP_SC_OK
|
|
|
+ call.answer(call_op_param)
|
|
|
+ self.calls[prm.callId] = call
|
|
|
+
|
|
|
+
|
|
|
class MyCall(pj.Call):
|
|
|
|
|
|
- def __init__(self, acc, call_id):
|
|
|
+ def __init__(self, agent, acc, user_part, call_id, **kwargs):
|
|
|
pj.Call.__init__(self, acc, call_id)
|
|
|
+ self.agent = agent
|
|
|
+ self.user_part = user_part
|
|
|
self.call_id = call_id
|
|
|
+ self.kwargs = kwargs
|
|
|
self.audio_port = None
|
|
|
self.aud_med = None
|
|
|
self.recorder = None
|
|
@@ -121,6 +127,8 @@ class MyCall(pj.Call):
|
|
|
# print ('11111111111')
|
|
|
if call_info.state == pj.PJSIP_INV_STATE_DISCONNECTED:
|
|
|
print("通话结束")
|
|
|
+ # 远程挂机之后要将分机号回收
|
|
|
+ self.agent.unregister(self.user_part)
|
|
|
|
|
|
def onCallMediaState(self, prm):
|
|
|
call_info = self.getInfo()
|
|
@@ -181,21 +189,26 @@ class MyCall(pj.Call):
|
|
|
# time.sleep(0.1) # 每隔1秒更新一次进度
|
|
|
|
|
|
|
|
|
-class UserAgent:
|
|
|
- def __init__(self, user_part, host="192.168.124.6", port="5060", password="slibra@#123456"):
|
|
|
- self.user_part, self.host, self.port, self.password = user_part, host, port, password
|
|
|
+class BotAgent:
|
|
|
+
|
|
|
+ def __init__(self, logger, user_part_range=range(1000, 1010), host="192.168.124.6", port="5060", password="slibra@#123456"):
|
|
|
+ self.logger = logger
|
|
|
+ self.user_part_range, self.host, self.port, self.password = user_part_range, host, port, password
|
|
|
+ self.pool = queue.Queue(maxsize=len(user_part_range))
|
|
|
+ self.accounts = {}
|
|
|
self.calls = {}
|
|
|
self.ep = pj.Endpoint()
|
|
|
- self.acc = Account(self)
|
|
|
self.is_stopping = False
|
|
|
-
|
|
|
- def start(self):
|
|
|
threading.Thread(target=self.create_pjsua2, args=()).start()
|
|
|
|
|
|
- # pjsua2 test function
|
|
|
def create_pjsua2(self):
|
|
|
# Create and initialize the library
|
|
|
ep_cfg = pj.EpConfig()
|
|
|
+ ep_cfg.uaConfig.threadCnt = 1
|
|
|
+ ep_cfg.uaConfig.mainThreadOnly = True
|
|
|
+ ep_cfg.uaConfig.maxCalls = 8
|
|
|
+ ep_cfg.logConfig.level = 5
|
|
|
+ ep_cfg.logConfig.consoleLevel = 5
|
|
|
self.ep.libCreate()
|
|
|
self.ep.libInit(ep_cfg)
|
|
|
|
|
@@ -206,72 +219,71 @@ class UserAgent:
|
|
|
sipTpConfig = pj.TransportConfig()
|
|
|
sipTpConfig.port = 30506
|
|
|
self.ep.transportCreate(pj.PJSIP_TRANSPORT_UDP, sipTpConfig)
|
|
|
-
|
|
|
# Start the library
|
|
|
self.ep.libStart()
|
|
|
- acfg = pj.AccountConfig()
|
|
|
- acfg.idUri = f"sip:{self.user_part}@{self.host}:{self.port}"
|
|
|
- acfg.regConfig.registrarUri = f"sip:{self.host}:{self.port}"
|
|
|
- cred = pj.AuthCredInfo("digest", "*", f"{self.user_part}", 0, self.password)
|
|
|
- acfg.sipConfig.authCreds.append(cred)
|
|
|
- # Create the account
|
|
|
- self.acc.create(acfg)
|
|
|
+
|
|
|
+ for user_part in self.user_part_range:
|
|
|
+ acfg = pj.AccountConfig()
|
|
|
+ acfg.idUri = f"sip:{user_part}@{self.host}:{self.port}"
|
|
|
+ acfg.regConfig.registrarUri = f"sip:{self.host}:{self.port}"
|
|
|
+ cred = pj.AuthCredInfo("digest", "*", f"{user_part}", 0, self.password)
|
|
|
+ acfg.sipConfig.authCreds.append(cred)
|
|
|
+
|
|
|
+ # Create the account
|
|
|
+ acc = Account(self, user_part)
|
|
|
+ acc.create(acfg)
|
|
|
+
|
|
|
+ self.pool.put(user_part)
|
|
|
+ self.accounts[user_part] = acc
|
|
|
|
|
|
while not self.is_stopping:
|
|
|
self.ep.libHandleEvents(100)
|
|
|
|
|
|
- def hangup(self, reason="NORMAL_CLEARING", **sip_headers):
|
|
|
+ def hangup(self, user_part, reason="NORMAL_CLEARING", **sip_headers):
|
|
|
call_op_param = pj.CallOpParam(True)
|
|
|
call_op_param.statusCode = pj.PJSIP_SC_OK
|
|
|
call_op_param.reason = reason
|
|
|
for k, v in sip_headers:
|
|
|
call_op_param.headers.append(pj.SipHeader(f"sip_h_{k}", v))
|
|
|
- for k, v in self.calls.items():
|
|
|
- v.hangup(call_op_param)
|
|
|
-
|
|
|
- def register(self, renew=False):
|
|
|
- self.acc.setRegistration(renew=renew)
|
|
|
+ acc = self.accounts.get(user_part)
|
|
|
+ if acc:
|
|
|
+ for k, v in acc.calls.items():
|
|
|
+ v.hangup(call_op_param)
|
|
|
+ # 机器人主动挂机回收分机号
|
|
|
+ self.release(user_part)
|
|
|
+
|
|
|
+ def register(self, **kwargs):
|
|
|
+ user_part = self.pool.get()
|
|
|
+ self.logger.info('register, user_part :%d, args: %s', user_part, json.dumps(kwargs))
|
|
|
+ acc = self.accounts.get(user_part)
|
|
|
+ if acc:
|
|
|
+ # acc.setRegistration(renew=True)
|
|
|
+ acc.kwargs = kwargs
|
|
|
+ return user_part
|
|
|
+
|
|
|
+ def unregister(self, user_part):
|
|
|
+ acc = self.accounts.get(user_part)
|
|
|
+ if acc:
|
|
|
+ acc.setRegistration(renew=False)
|
|
|
+ # 用户远程挂机回收分机号
|
|
|
+ self.release(user_part)
|
|
|
+
|
|
|
+ def release(self, user_part):
|
|
|
+ def element_in_queue(q, element):
|
|
|
+ with q.mutex: # 确保线程安全
|
|
|
+ for item in list(q.queue): # 将队列转换为列表进行遍历
|
|
|
+ if item == element:
|
|
|
+ return True
|
|
|
+ return False
|
|
|
+
|
|
|
+ if element_in_queue(self.pool, user_part):
|
|
|
+ return
|
|
|
+ self.pool.put(user_part)
|
|
|
|
|
|
def destroy(self):
|
|
|
self.is_stopping = True
|
|
|
# Destroy the library
|
|
|
self.ep.libDestroy()
|
|
|
|
|
|
-
|
|
|
-class UserAgentPool:
|
|
|
- def __init__(self, pool_size, user_part_range=range(1000, 1019)):
|
|
|
- self.pool_size = pool_size
|
|
|
- self.pool = queue.Queue(maxsize=pool_size)
|
|
|
-
|
|
|
- # 初始化连接池
|
|
|
- for i in range(pool_size):
|
|
|
- conn = self.create(user_part_range[i])
|
|
|
- self.pool.put(conn)
|
|
|
-
|
|
|
- def create(self, user_part, host='192.168.124.6', port='6379', password="slibra@#123456"):
|
|
|
- return UserAgent(user_part, host=host, port=port, password=password)
|
|
|
-
|
|
|
- def get(self):
|
|
|
- return self.pool.get()
|
|
|
-
|
|
|
- def release(self, conn):
|
|
|
- self.pool.put(conn)
|
|
|
-
|
|
|
- def destroy(self):
|
|
|
- while not self.pool.empty():
|
|
|
- agent = self.pool.get()
|
|
|
- agent.destroy()
|
|
|
-
|
|
|
-
|
|
|
-class Voip:
|
|
|
-
|
|
|
- def __init__(self, logger):
|
|
|
- self.logger = logger
|
|
|
- # self.pool = UserAgentPool(pool_size=2)
|
|
|
- agent = UserAgent('1001')
|
|
|
- agent.start()
|
|
|
-
|
|
|
-
|
|
|
-# if __name__ == "__main__":
|
|
|
-# voip = Voip()
|
|
|
-# voip.create_pjsua2()
|
|
|
+ def __del__(self):
|
|
|
+ self.destroy()
|