[ previous ] [ next ] [ threads ]
 To :  Marco Menardi <mmenaz@m...>
 From :  Maciek Kaminski <maciejka@t...>
 Subject :  Re: [yate] for l-fy and markit: poor man's pbx
 Date :  Mon, 11 Jul 2005 21:39:59 +0200
Marco Menardi napisa³(a):

> Some questions before I try it tonight or tomorrow (so your expert 
> answers can save me hours of discouraging tryies):
> a) where have the C++ code (source and bin) and expecially the python 
> code be located? libyate.py and test.py are under scripts/, the same 
> for poor man pbx?

Put python code into script directory, c++ into modules, beside 
HashTable.cpp which should go into engine and hashtable.h which should 
go into main yate dir.

> b) my previous design with asterisk was that the "business logic" of 
> my PBX (how to handle incoming / outgoing / extensions routing and 
> features (i.e. VM, voice menus, call voice recordings, etc.) or error 
> handling (busy, unavailable, wrong dial)) were "put" in the dialplan. 
> What I mainly missed in asterisk was the Dial() command unable to do 
> "native" assisted transfer/conferences, nor sophisticated DTMF 
> interception.
> How does this pmp (poor man's pbx) fit in this picture? Is something I 
> have to call from extregroute.conf? Any sample? What do I have to put 
> in extmodule.conf?

pbx.py does not use extmodule but rmodule. My rmodule configuration 
looks like this:
[general]
; This section controls the behaviour of the Remote Module

; how much time give to the client to handle message in useconds. -1 for 
no limit
timeout=-1 ;1000000


[tcp]
; port: int: TCP Port to listen on, 0 to disable module
port=5038

; addr: ipaddress: IP address to bind to
addr=127.0.0.1

; header: string: Header string to display on connect
header=YATE (http://YATE.null.ro) ready.

[pipe]
;script=./scripts/record.py

At the moment pbx.py watches for calls to extension called secretary. So 
You should put something like:
^secretary$=iax/kamyk@1...
into regexroute.conf

> What is the role of the "main" python function here?

It starts yaypm message loop.

> c) I've installed python 2.4 and Twisted for 2.4 (I'm using Debian 
> Sid), is it fine?

Should be.

P.S.: I attach newer version that handles hangup. You should also use 
modified version of yaypm module.



#!/usr/bin/python
# -*- coding: iso-8859-2; -*-

"""
 pbx.py

 Copyright (C) 2005 Maciek Kaminski

 Very poor man's pbx.

"""

from yaypm import YateClientProtocolFactory, Message, AbandonedException, examples
from yaypm.flow import sleep, build_start, go, getResult
from twisted.internet import reactor, defer

import sys, logging, yaypm, yaypm.flow

logger = logging.getLogger('pbx')

class TransferedException(Exception):
    pass

def blind_transfer(yate, callid, target):

    end = yate.onmsg("chan.hangup", lambda m : m.attrs["id"] == callid, autoreturn = True)
    
    yield yate.dispatch(Message(name = "call.route", attrs = {"id": callid, "called": target}), until = end)
    route = getResult()

    logger.debug("blind transfering: %s to: %s" % (callid, route.retvalue))

    yield yate.dispatch(Message(name = "call.execute", attrs = {"userData": callid, "id": callid, "callto": route.retvalue}))
    getResult()

    raise TransferedException    

def supervised_transfer(yate, callid, targetid):

    try:
        end = yate.onmsg("chan.hangup", lambda m : m.attrs["id"] in [callid, targetid], autoreturn = True)            
        yield yate.onmsg(
            "chan.dtmf",
            lambda m : m.attrs["id"] == targetid and not m.attrs["text"] in ["*", "#"],
            until = end, autoreturn = True)   
        dtmf = getResult()

        yield yate.dispatch(
            Message(name = "call.route", attrs = {"id": callid, "called": dtmf.attrs["text"]}), until = end)
        route = getResult()

        logger.debug("doing supervised transfer to: %s" % route.retvalue)

        yate.dispatch(
            Message(
                name = "call.transfer",
                attrs = {"userData": targetid, "transferto1": route.retvalue, "transferto2": "moh/default"}))

        yield yate.onwatch("call.answered", lambda m : m.attrs["targetid"] == targetid , end)
        answered = getResult()
    except AbandonedException, e:
        logger.debug("supervised transfer abandoned by: %s" % e.cause.attrs["id"])        
        yate.dispatch(Message(name = "call.drop", attrs = {"id": callid}))
        yate.dispatch(Message(name = "call.drop", attrs = {"id": targetid}))
        raise e
        
    try:
        end = yate.onmsg("chan.hangup", lambda m : m.attrs["id"] == callid, autoreturn = True)         
        
        logger.debug("called: %s" % answered.attrs["id"])

        yield defer.DeferredList(
            [yate.onmsg("chan.dtmf", lambda m : m.attrs["id"] == targetid and m.attrs["text"] in ["*", "#"], end, autoreturn = True),
            yate.onmsg("chan.disconnected", lambda m : m.attrs["id"] == answered.attrs["id"], autoreturn = False)],
            fireOnOneCallback = True)

        msg = getResult()[0]

        if msg.name == "chan.disconnected":
            yield yate.dispatch(Message(name = "call.connect", attrs = {"id1": callid, "id2": answered.attrs["id"]}))
            getResult()
            yate.ret(msg, True)
            go(pbx(yate, callid, answered.attrs["id"]))
            raise TransferedException()
        
        elif msg.name == "chan.dtmf":
            yate.dispatch(Message(name = "call.connect", attrs = {"id1": callid, "id2": targetid}))
            return
        
    except AbandonedException, e:       
        logger.debug("supervised transfer abandoned by: %s" % e.cause.attrs["id"])
        raise e

def pbx(yate, callid, targetid):
    logger.info("pbx %s -> %s started" % (callid, targetid))   
    try:
        end = yate.onmsg("chan.hangup", lambda m : m.attrs["id"] in [callid, targetid], autoreturn = True)
        while True:
            logger.debug("pbx %s -> %s waiting for dtmfs" % (callid, targetid))
            yield yate.onmsg("chan.dtmf", lambda m : m.attrs["id"] == targetid and m.attrs["text"] in ["*", "#"], end, autoreturn = True)
            dtmf = getResult()

            logger.debug("got %s from: %s" % (dtmf.attrs["text"], targetid))

            yield yate.onmsg("chan.dtmf", lambda m : m.attrs["id"] == targetid, end, autoreturn = True)
            dtmf = getResult()

            logger.debug("got %s from: %s" % (dtmf.attrs["text"], targetid))

            if(dtmf.attrs["text"] in ["*", "#"]):
                yield go(supervised_transfer(yate, callid, targetid))
            else:
                yield go(blind_transfer(yate, callid, dtmf.attrs["text"]))
            getResult()
            
    except AbandonedException, e:
        logger.debug("pbx %s -> %s abandoned" % (callid, targetid))
    except TransferedException, e:
        logger.debug("pbx %s -> %s finished after successful transfer" % (callid, targetid))

def start_pbx(yate, callid):

    end = yate.onmsg("chan.hangup", lambda m : m.attrs["id"] == callid, autoreturn = True)
 
    yield yate.onwatch("call.answered", lambda m : m.attrs["targetid"] == callid , end)
    answered = getResult()
    
    go(pbx(yate, callid, answered.attrs["id"]))
        

def main(yate, called):
    yield defer.DeferredList(
        [
         yate.watch("call.execute"),
         yate.watch("call.answered"),
         yate.install("chan.dtmf", 50),
         yate.install("chan.disconnected"),         
         yate.install("chan.hangup")])
    getResult()

    logger.debug("watching for calls to: %s" % called)    

    while True:
        yield yate.onwatch("call.execute", lambda m : m.attrs.has_key("called") and m.attrs["called"] == called)
        execute = getResult()
        logger.debug("starting pbx for call: %s" % execute.attrs["id"])        
        go(start_pbx(yate, execute.attrs["id"]))

if __name__ == '__main__':
       hdlr = logging.StreamHandler()
       #formatter = logging.Formatter()
       formatter = examples.ConsoleFormater('%(name)s %(levelname)s %(message)s')
       hdlr.setFormatter(formatter)

       yaypm.logger.addHandler(hdlr)
       #yaypm.logger.setLevel(logging.DEBUG)
       yaypm.flow.logger_flow.setLevel(logging.DEBUG)       
       yaypm.logger_messages.setLevel(logging.INFO)

       logger.setLevel(logging.DEBUG)
       logger.addHandler(hdlr)

       start = build_start(main, "secretary")

       f = YateClientProtocolFactory(start)       
       reactor.connectTCP("localhost", 5038, f)
       reactor.run()



‹