From e5eb47747552fa142e86e9578fc4bdd43c1200d0 Mon Sep 17 00:00:00 2001 From: boyska Date: Tue, 15 Mar 2022 18:08:30 +0100 Subject: [PATCH] mixer is called when someone joins the bridge --- transfer_back/mixer.py | 196 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 transfer_back/mixer.py diff --git a/transfer_back/mixer.py b/transfer_back/mixer.py new file mode 100644 index 0000000..f1f0ae2 --- /dev/null +++ b/transfer_back/mixer.py @@ -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()