Browse Source

mixer is called when someone joins the bridge

boyska 2 years ago
parent
commit
e5eb477475
1 changed files with 196 additions and 0 deletions
  1. 196 0
      transfer_back/mixer.py

+ 196 - 0
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()