class DependentMultiService(MultiService): """ I am a MultiService that starts services in insert order, and stops them in the reverse order. The service starts and stops are chained, so be very careful about services that may fail to start or stop. """ def __init__(self, serviceName, serviceParent=None): MultiService.__init__(self, serviceName, serviceParent) self.services = OrderedDict(self.services) def _finishStartService(self, res): return ApplicationService.startService(self) or defer.succeed(None) def _rollbackStartedServices(self, failure, service): v = self.services.values() startedServices = v[:v.index(service)] startedServices.reverse() for svc in startedServices: svc.stopService() return failure def startService(self): """ Start all of my Services. I return a Deferred that will callback (with no useful result) when all services are started. In the event of a failure, all of the successful services will be stopped (without chained behavior) and I will errback with the first unsuccessful service's failure. """ def startServiceDeferred(res, service): return service.startService() or defer.succeed(None) d = defer.succeed(None) for svc in self.services.values(): d.addCallbacks(startServiceDeferred, callbackArgs=(svc,), errback=self._rollbackStartedServices, errbackArgs=(svc,)) return d.addCallback(self._finishStartService) def _emergencyStopService(self, failure, service): v = self.services.values() runningServices = v[v.index(service):] runningServices.reverse() for svc in runningServices: svc.stopService() self._finishStopService() return failure def _finishStopService(self, res): return ApplicationService.stopService(self) or defer.succeed(None) def stopService(self): """ Stop all of my Services. I return a Deferred that will callback (with no useful result) when all services are stopped. In the event of a failure, the running services will be stopped (without chained behavior) and I will errback with the first unsuccessful service's failure. """ def stopServiceDeferred(res, service): return service.stopService() or defer.succeed(None) v = self.services.values() v.reverse() d = defer.succeed(None) for svc in v: d.addCallbacks(stopServiceDeferred, callbackArgs=(svc,), errback=self._emergencyStopService, errbackArgs=(svc,)) return d.addCallback(self._finishStopService)
class VoiceApp(StateMachine): _inbound = None def __init__(self, defer, appl, cookie, **kwargs): self.__cookie = cookie self.__appl = appl self.__legs = OrderedDict() self.__dict__.update(kwargs) self.__currentDTMFKey = None self.__collectedDTMFKeys = '' self.__dtmfSingleMode = True super(VoiceApp, self).__init__(defer, **kwargs) def getDefaultLeg(self): if self.__legs: return self.__legs.values()[0] def getLeg(self, cookie): return self.__legs.get(cookie) def setLeg(self, leg, cookie): self.__legs[cookie] = leg #self.leg.hijackLeg(self) def va_selectDefaultFormat(self, ptlist, callcookie): return self.getLeg(callcookie).selectDefaultFormat(ptlist) def va_incomingRTP(self, packet, callcookie): leg = self.getLeg(callcookie) if leg is None: log.msg('no leg for cookie %s for incoming RTP'%(callcookie,), system='doug') return else: return leg.leg_incomingRTP(packet) def va_outgoingRTP(self, sample, cookie=None): if cookie is None: cookie = self.__cookie self.__appl.outgoingRTP(cookie, sample) def va_start(self, args): self._start(callstart=0, args=args) def va_callstart(self, inboundLeg, args=None): if args is None: args = {} if inboundLeg is not None: self.__legs[inboundLeg.getCookie()] = inboundLeg if self._inbound is None: self._inbound = inboundLeg ce = CallStartedEvent(inboundLeg) ce.args = args self._triggerEvent(ce) def va_callanswered(self, leg=None): if leg is None: leg = self._inbound self._triggerEvent(CallAnsweredEvent(leg)) def va_callrejected(self, leg=None): if leg is None: leg = self._inbound try: del self.__legs[leg] except KeyError: log.msg("can't find leg %s, current legs: %r"%( leg, self.__legs.keys()), system='doug') self._triggerEvent(CallRejectedEvent(leg)) def _clear_legs(self): from shtoom.util import stack #print self, "clearing running legs %r"%(self.__legs.items())#,stack(8) for name, leg in self.__legs.items(): leg._stopAudio() del self.__legs[name] _cleanup = _clear_legs def va_abort(self): self.mediaStop() self._clear_legs() self._triggerEvent(CallEndedEvent(None)) def mediaPlay(self, playlist, leg=None): if leg is None: leg = self.getDefaultLeg() leg.mediaPlay(playlist) def mediaRecord(self, dest, leg=None): if leg is None: leg = self.getDefaultLeg() leg.mediaRecord(dest) def mediaStop(self, leg=None): if leg is None: leg = self.getDefaultLeg() if leg is not None: leg.mediaStop() def mediaStopRecording(self, leg=None): if leg is None: leg = self.getDefaultLeg() if leg is not None: leg.mediaStopRecording() def setTimer(self, delay): return Timer(self, delay) def isPlaying(self, leg=None): if leg is None: leg = self.getDefaultLeg() return leg.isPlaying() def isRecording(self, leg=None): if leg is None: leg = self.getDefaultLeg() return leg.isRecording() def dtmfMode(self, single=False, inband=False, timeout=0, leg=None): if leg is None: leg = self.getDefaultLeg() leg.dtmfMode(single, inband, timeout) def placeCall(self, toURI, fromURI=None): from shtoom.doug.leg import Leg nleg = Leg(cookie=None, dialog=None, voiceapp=self) self.__appl.placeCall(self.__cookie, nleg, toURI, fromURI) def va_hangupCall(self, cookie): self.__appl.dropCall(cookie) def connectLegs(self, leg1, leg2=None): from shtoom.doug.leg import Bridge if leg2 is None: leg2 = self.getDefaultLeg() if leg1 is leg2: raise ValueError, "can't join %r to itself!"%(leg1) else: b = Bridge(leg1, leg2) return b def sendDTMF(self, digits, cookie=None, duration=0.1, delay=0.05): "Send a string of DTMF keystrokes" for n,key in enumerate(digits): if key not in ',01234567890#*': raise ValueError, key if key == ',': # pause continue n = float(n) # just in case if cookie is None: cookie = self.__cookie i = 0.2 reactor.callLater(i+n*(duration+delay), lambda k=key: self.__appl.startDTMF(cookie, k)) reactor.callLater(i+n*(duration+delay)+duration, lambda k=key: self.__appl.stopDTMF(cookie, k)) def _inboundDTMFKeyPress(self, dtmf): if self.__dtmfSingleMode: self._triggerEvent(DTMFReceivedEvent(dtmf, self)) else: self.__collectedDTMFKeys += dtmf if dtmf in ('#', '*'): dtmf, self.__collectedDTMFKeys = self.__collectedDTMFKeys, '' self._triggerEvent(DTMFReceivedEvent(dtmf, self)) def va_startDTMFevent(self, dtmf, cookie=None): c = self.__currentDTMFKey if dtmf: if c is not dtmf: self.va_stopDTMFevent(c) self.__currentDTMFKey = dtmf self._inboundDTMFKeyPress(dtmf) else: # repeat pass def va_stopDTMFevent(self, dtmf, cookie=None): # For now, I only care about dtmf start events if dtmf == self.__currentDTMFKey: self.__currentDTMFKey = None
class DependentMultiService(MultiService): """ I am a MultiService that starts services in insert order, and stops them in the reverse order. The service starts and stops are chained, so be very careful about services that may fail to start or stop. """ def __init__(self, serviceName, serviceParent=None): MultiService.__init__(self, serviceName, serviceParent) # Ensure order self.services = OrderedDict(self.services) def _finishStartService(self, res): return ApplicationService.startService(self) or defer.succeed(None) def _rollbackStartedServices(self, failure, service): v = self.services.values() startedServices = v[:v.index(service)] startedServices.reverse() # Warning: On failure, service stops are not # chained. However, they will stop in the proper order. for svc in startedServices: svc.stopService() return failure def startService(self): """ Start all of my Services. I return a Deferred that will callback (with no useful result) when all services are started. In the event of a failure, all of the successful services will be stopped (without chained behavior) and I will errback with the first unsuccessful service's failure. """ def startServiceDeferred(res, service): return service.startService() or defer.succeed(None) d = defer.succeed(None) for svc in self.services.values(): d.addCallbacks(startServiceDeferred, callbackArgs=(svc, ), errback=self._rollbackStartedServices, errbackArgs=(svc, )) return d.addCallback(self._finishStartService) def _emergencyStopService(self, failure, service): v = self.services.values() runningServices = v[v.index(service):] runningServices.reverse() for svc in runningServices: svc.stopService() # It is probably desirable that the service collection be # marked as stopped, even though errors have occurred. self._finishStopService() return failure def _finishStopService(self, res): return ApplicationService.stopService(self) or defer.succeed(None) def stopService(self): """ Stop all of my Services. I return a Deferred that will callback (with no useful result) when all services are stopped. In the event of a failure, the running services will be stopped (without chained behavior) and I will errback with the first unsuccessful service's failure. """ def stopServiceDeferred(res, service): return service.stopService() or defer.succeed(None) v = self.services.values() v.reverse() d = defer.succeed(None) for svc in v: d.addCallbacks(stopServiceDeferred, callbackArgs=(svc, ), errback=self._emergencyStopService, errbackArgs=(svc, )) return d.addCallback(self._finishStopService)
class VoiceApp(StateMachine): _inbound = None def __init__(self, defer, appl, cookie, **kwargs): self.__cookie = cookie self.__appl = appl self.__legs = OrderedDict() self.__dict__.update(kwargs) self.__currentDTMFKey = None self.__collectedDTMFKeys = '' self.__dtmfSingleMode = True super(VoiceApp, self).__init__(defer, **kwargs) def getDefaultLeg(self): if self.__legs: return self.__legs.values()[0] def getLeg(self, cookie): return self.__legs.get(cookie) def setLeg(self, leg, cookie): self.__legs[cookie] = leg #self.leg.hijackLeg(self) def va_selectDefaultFormat(self, ptlist, callcookie): return self.getLeg(callcookie).selectDefaultFormat(ptlist) def va_incomingRTP(self, packet, callcookie): leg = self.getLeg(callcookie) if leg is None: log.msg('no leg for cookie %s for incoming RTP' % (callcookie, ), system='doug') return else: return leg.leg_incomingRTP(packet) def va_outgoingRTP(self, sample, cookie=None): if cookie is None: cookie = self.__cookie self.__appl.outgoingRTP(cookie, sample) def va_start(self, args): self._start(callstart=0, args=args) def va_callstart(self, inboundLeg, args=None): if args is None: args = {} if inboundLeg is not None: self.__legs[inboundLeg.getCookie()] = inboundLeg if self._inbound is None: self._inbound = inboundLeg ce = CallStartedEvent(inboundLeg) ce.args = args self._triggerEvent(ce) def va_callanswered(self, leg=None): if leg is None: leg = self._inbound self._triggerEvent(CallAnsweredEvent(leg)) def va_callrejected(self, leg=None): if leg is None: leg = self._inbound try: del self.__legs[leg] except KeyError: log.msg("can't find leg %s, current legs: %r" % (leg, self.__legs.keys()), system='doug') self._triggerEvent(CallRejectedEvent(leg)) def _clear_legs(self): from shtoom.util import stack #print self, "clearing running legs %r"%(self.__legs.items())#,stack(8) for name, leg in self.__legs.items(): leg._stopAudio() del self.__legs[name] _cleanup = _clear_legs def va_abort(self): self.mediaStop() self._clear_legs() self._triggerEvent(CallEndedEvent(None)) def mediaPlay(self, playlist, leg=None): if leg is None: leg = self.getDefaultLeg() leg.mediaPlay(playlist) def mediaRecord(self, dest, leg=None): if leg is None: leg = self.getDefaultLeg() leg.mediaRecord(dest) def mediaStop(self, leg=None): if leg is None: leg = self.getDefaultLeg() if leg is not None: leg.mediaStop() def mediaStopRecording(self, leg=None): if leg is None: leg = self.getDefaultLeg() if leg is not None: leg.mediaStopRecording() def setTimer(self, delay): return Timer(self, delay) def isPlaying(self, leg=None): if leg is None: leg = self.getDefaultLeg() return leg.isPlaying() def isRecording(self, leg=None): if leg is None: leg = self.getDefaultLeg() return leg.isRecording() def dtmfMode(self, single=False, inband=False, timeout=0, leg=None): if leg is None: leg = self.getDefaultLeg() leg.dtmfMode(single, inband, timeout) def placeCall(self, toURI, fromURI=None): from shtoom.doug.leg import Leg nleg = Leg(cookie=None, dialog=None, voiceapp=self) self.__appl.placeCall(self.__cookie, nleg, toURI, fromURI) def va_hangupCall(self, cookie): self.__appl.dropCall(cookie) def connectLegs(self, leg1, leg2=None): from shtoom.doug.leg import Bridge if leg2 is None: leg2 = self.getDefaultLeg() if leg1 is leg2: raise ValueError, "can't join %r to itself!" % (leg1) else: b = Bridge(leg1, leg2) return b def sendDTMF(self, digits, cookie=None, duration=0.1, delay=0.05): "Send a string of DTMF keystrokes" for n, key in enumerate(digits): if key not in ',01234567890#*': raise ValueError, key if key == ',': # pause continue n = float(n) # just in case if cookie is None: cookie = self.__cookie i = 0.2 reactor.callLater(i + n * (duration + delay), lambda k=key: self.__appl.startDTMF(cookie, k)) reactor.callLater(i + n * (duration + delay) + duration, lambda k=key: self.__appl.stopDTMF(cookie, k)) def _inboundDTMFKeyPress(self, dtmf): if self.__dtmfSingleMode: self._triggerEvent(DTMFReceivedEvent(dtmf, self)) else: self.__collectedDTMFKeys += dtmf if dtmf in ('#', '*'): dtmf, self.__collectedDTMFKeys = self.__collectedDTMFKeys, '' self._triggerEvent(DTMFReceivedEvent(dtmf, self)) def va_startDTMFevent(self, dtmf, cookie=None): c = self.__currentDTMFKey if dtmf: if c is not dtmf: self.va_stopDTMFevent(c) self.__currentDTMFKey = dtmf self._inboundDTMFKeyPress(dtmf) else: # repeat pass def va_stopDTMFevent(self, dtmf, cookie=None): # For now, I only care about dtmf start events if dtmf == self.__currentDTMFKey: self.__currentDTMFKey = None