mixer is called when someone joins the bridge
This commit is contained in:
parent
7c2c5a212a
commit
e5eb477475
1 changed files with 196 additions and 0 deletions
196
transfer_back/mixer.py
Normal file
196
transfer_back/mixer.py
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""
|
||||||
|
Questo script tiene d'occhio una specifica ConfBridge.
|
||||||
|
SE c'e' almeno un partecipante, allora cerca di far entrare lo speaker (ovvero
|
||||||
|
quello che noi chiamiamo "mixer", e che è semplicemente un'estensione PJSIP)
|
||||||
|
dentro la confbridge
|
||||||
|
|
||||||
|
Nella configurazione di asterisk si deve porre attenzione ad un dettaglio: lo
|
||||||
|
speaker si deve trovare in un contesto diverso da tutti gli altri telefoni, e in
|
||||||
|
particolare da quello in cui si trovano gli utenti remoti che entrano nella
|
||||||
|
confbridge in questione. In questo modo si puo' facilmente distinguere lo
|
||||||
|
speaker.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from panoramisk import Manager
|
||||||
|
from panoramisk.actions import Action
|
||||||
|
|
||||||
|
LOOP = asyncio.get_event_loop()
|
||||||
|
CONFERENCE = "400"
|
||||||
|
APP = None
|
||||||
|
|
||||||
|
log = logging.getLogger("transferback")
|
||||||
|
|
||||||
|
|
||||||
|
async def on_tutto(manager, msg):
|
||||||
|
for prefix in ["Var", "RTC"]:
|
||||||
|
if msg.event.startswith(prefix):
|
||||||
|
return
|
||||||
|
log.info("... Event %s", msg.event)
|
||||||
|
|
||||||
|
|
||||||
|
async def refresh(manager, *args):
|
||||||
|
ret = await manager.send_action(
|
||||||
|
Action({"Action": "ConfbridgeList", "Conference": CONFERENCE}), as_list=False
|
||||||
|
)
|
||||||
|
if ret.response == "Error":
|
||||||
|
await APP.on_users(None, ret)
|
||||||
|
|
||||||
|
|
||||||
|
class ConfbridgeInviter:
|
||||||
|
def __init__(self, manager):
|
||||||
|
self.manager = manager
|
||||||
|
|
||||||
|
self.users = []
|
||||||
|
self.tmp_users = []
|
||||||
|
self.log = logging.getLogger(self.__class__.__name__)
|
||||||
|
|
||||||
|
self.manager.register_event("EndpointDetail", self.on_speaker_endpoint_info)
|
||||||
|
self.on_speaker_endpoint_info_active = False
|
||||||
|
|
||||||
|
def get_context(self):
|
||||||
|
return "from-mixer"
|
||||||
|
|
||||||
|
def get_extension(self):
|
||||||
|
return "9400"
|
||||||
|
|
||||||
|
def get_speaker_channel(self):
|
||||||
|
return "PJSIP/400"
|
||||||
|
|
||||||
|
def get_conference(self):
|
||||||
|
return CONFERENCE
|
||||||
|
|
||||||
|
def is_speaker(self, msg):
|
||||||
|
"""
|
||||||
|
questo metodo capisce se un utente e' lo speaker.
|
||||||
|
Si sarebbe potuto fare anche in altri modi, ad esempio guardando il
|
||||||
|
channel, o la Exten, o utilizzando admin/marked in maniera opportuna.
|
||||||
|
"""
|
||||||
|
return msg.context == self.get_context()
|
||||||
|
|
||||||
|
async def run(self):
|
||||||
|
self.log.info("runno")
|
||||||
|
self.manager.register_event("ConfbridgeList", self.on_user)
|
||||||
|
self.manager.register_event("ConfbridgeListComplete", self.on_users)
|
||||||
|
|
||||||
|
async def on_user(self, _, msg):
|
||||||
|
if msg.conference != self.get_conference():
|
||||||
|
return
|
||||||
|
self.tmp_users.append(msg)
|
||||||
|
|
||||||
|
async def on_users(self, _, _msg):
|
||||||
|
self.users = self.tmp_users
|
||||||
|
self.tmp_users = []
|
||||||
|
|
||||||
|
someone_inside = len(self.users) > 0
|
||||||
|
speaker_inside = any(True for u in self.users if self.is_speaker(u))
|
||||||
|
|
||||||
|
for u in self.users:
|
||||||
|
self.log.debug(" - %s %s", u.context, u.channel)
|
||||||
|
self.log.debug(
|
||||||
|
"%d users - speaker_inside=%s", len(self.users), str(speaker_inside)
|
||||||
|
)
|
||||||
|
|
||||||
|
if not speaker_inside:
|
||||||
|
if someone_inside:
|
||||||
|
await self.maybe_invite_speaker()
|
||||||
|
|
||||||
|
self.users = []
|
||||||
|
|
||||||
|
async def maybe_invite_speaker(self):
|
||||||
|
self.log.debug("Maybe?")
|
||||||
|
self.on_speaker_endpoint_info_active = True
|
||||||
|
ret = await self.manager.send_action(
|
||||||
|
Action(
|
||||||
|
{
|
||||||
|
"Action": "PJSIPShowEndpoint",
|
||||||
|
"Endpoint": self.get_speaker_channel().split("/")[-1],
|
||||||
|
}
|
||||||
|
),
|
||||||
|
as_list=False,
|
||||||
|
)
|
||||||
|
self.on_speaker_endpoint_info_active = ret.actionid
|
||||||
|
|
||||||
|
async def on_speaker_endpoint_info(self, _, msg):
|
||||||
|
self.log.debug("Vediamo")
|
||||||
|
active = self.on_speaker_endpoint_info_active
|
||||||
|
if active is False or (type(active) is str and active != msg.actionid):
|
||||||
|
return
|
||||||
|
|
||||||
|
self.on_speaker_endpoint_info_active = False
|
||||||
|
self.log.debug(" Speaker dev = %s", msg.devicestate)
|
||||||
|
if msg.devicestate == "Not in use":
|
||||||
|
self.invite_speaker()
|
||||||
|
|
||||||
|
def invite_speaker(self):
|
||||||
|
cmd = "channel originate {chan} extension {ext}@{ctx}".format(
|
||||||
|
chan=self.get_speaker_channel(),
|
||||||
|
ext=self.get_extension(),
|
||||||
|
ctx=self.get_context(),
|
||||||
|
)
|
||||||
|
self.log.debug("CMD = %s", cmd)
|
||||||
|
self.manager.send_command(cmd)
|
||||||
|
|
||||||
|
async def kick_speaker(self):
|
||||||
|
return
|
||||||
|
cmd = "confbridge kick {conf} all".format(
|
||||||
|
conf=self.get_conference(),
|
||||||
|
)
|
||||||
|
self.log.debug("CMD = %s", cmd)
|
||||||
|
ret = await self.manager.send_command(cmd)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
async def run_app(Cls, manager):
|
||||||
|
global APP
|
||||||
|
APP = Cls(manager)
|
||||||
|
await APP.run()
|
||||||
|
|
||||||
|
|
||||||
|
async def init(manager):
|
||||||
|
manager.register_event("*", on_tutto)
|
||||||
|
for event in [
|
||||||
|
"FullyBooted",
|
||||||
|
"ConfbridgeStart",
|
||||||
|
"ConfbridgeJoin",
|
||||||
|
"ConfbridgeLeave",
|
||||||
|
"ConfbridgeEnd",
|
||||||
|
"TestEvent",
|
||||||
|
"Hangup",
|
||||||
|
"Newstate", # XXX: solo se lo stato che cambia e' quello dello speaker
|
||||||
|
]:
|
||||||
|
manager.register_event(event, refresh)
|
||||||
|
pass
|
||||||
|
|
||||||
|
await run_app(ConfbridgeInviter, manager)
|
||||||
|
|
||||||
|
|
||||||
|
async def on_shutdown(m):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
logging.getLogger("panoramisk.manager").setLevel(logging.WARNING)
|
||||||
|
manager = Manager(
|
||||||
|
loop=LOOP,
|
||||||
|
host="127.0.0.1",
|
||||||
|
port=5038,
|
||||||
|
ssl=False,
|
||||||
|
encoding="utf8",
|
||||||
|
username="admin",
|
||||||
|
secret="secret123password",
|
||||||
|
ping_delay=10, # Delay after start
|
||||||
|
ping_interval=10, # Periodically ping AMI (dead or alive)
|
||||||
|
reconnect_timeout=2, # Timeout reconnect if connection lost
|
||||||
|
)
|
||||||
|
manager.connect(run_forever=True, on_startup=init, on_shutdown=on_shutdown)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
Loading…
Reference in a new issue