コード例 #1
0
ファイル: menu.py プロジェクト: daasara/riba
class CollectDigits(Interaction):
    """Collects some number of digits (e.g. an extension) from user"""
    soundFile = common.StringLocaleProperty(
        "soundFile",
        """File (name) for the pre-recorded blurb""",
    )
    textPrompt = common.StringProperty(
        "textPrompt",
        """Textual prompt describing the option""",
    )
    readBack = common.BooleanProperty(
        "readBack",
        """Whether to read the entered value back to the user""",
        defaultValue=False,
    )
    minDigits = common.IntegerProperty(
        "minDigits",
        """Minimum number of digits to collect (only restricted if specified)""",
    )
    maxDigits = common.IntegerProperty(
        "maxDigits",
        """Maximum number of digits to collect (only restricted if specified)""",
    )
    runnerClass = CollectDigitsRunner
    tellInvalid = common.IntegerProperty(
        "tellInvalid",
        """Whether to tell the user that their selection is unrecognised""",
        defaultValue=True,
    )
コード例 #2
0
ファイル: frontend.py プロジェクト: vitimm144/starpy
class Question(propertied.Propertied):
    survey = weak.WeakProperty(
        "survey",
        """Our survey object""",
    )
    questionId = common.IntegerProperty(
        "questionId",
        """Unique identifier for our question""",
        defaultFunction=lambda prop, client: client.survey.newQuestionId(),
    )

    def recordQuestion(self, agi, number=None):
        """Record a question (number)"""
        return agi.recordFile(
            '%s.%s' % (self.survey.surveyId, self.questionId),
            'gsm',
            '#*',
            timeout=60,
            beep=True,
            silence=5,
        ).addCallback(self.onRecorded,
                      agi=agi).addErrback(self.onRecordAborted, agi=agi)

    def onRecorded(self, result, agi):
        """Handle recording of the question"""
        pass
コード例 #3
0
ファイル: utilapplication.py プロジェクト: daasara/riba
class AMISpecifier(propertied.Propertied):
    """Manager interface setup/specifier"""
    username = common.StringLocaleProperty(
        "username",
        """Login username for the manager interface""",
    )
    secret = common.StringLocaleProperty(
        "secret",
        """Login secret for the manager interface""",
    )
    password = secret
    server = common.StringLocaleProperty(
        "server",
        """Server IP address to which to connect""",
        defaultValue='127.0.0.1',
    )
    port = common.IntegerProperty(
        "port",
        """Server IP port to which to connect""",
        defaultValue=5038,
    )
    timeout = common.FloatProperty(
        "timeout",
        """Timeout in seconds for an AMI connection timeout""",
        defaultValue=5.0,
    )

    def login(self):
        """Login to the specified manager via the AMI"""
        theManager = manager.AMIFactory(self.username, self.secret)
        return theManager.login(self.server, self.port, timeout=self.timeout)
コード例 #4
0
ファイル: menu.py プロジェクト: daasara/riba
class Interaction(propertied.Propertied):
    """Base class for user-interaction operations"""
    ALL_DIGITS = '0123456789*#'
    timeout = common.FloatProperty(
        "timeout",
        """Duration to wait for response before repeating message""",
        defaultValue=5,
    )
    maxRepetitions = common.IntegerProperty(
        "maxRepetitions",
        """Maximum number of times to play before failure""",
        defaultValue=5,
    )
    onSuccess = basic.BasicProperty(
        "onSuccess",
        """Optional callback for success with signature method( result, runner )""",
    )
    onFailure = basic.BasicProperty(
        "onFailure",
        """Optional callback for failure with signature method( result, runner )""",
    )
    runnerClass = None

    def __call__(self, agi, *args, **named):
        """Initiate AGI-based interaction with the user"""
        return self.runnerClass(model=self, agi=agi)(*args, **named)
コード例 #5
0
ファイル: menu.py プロジェクト: russellb/starpy
class NumberPrompt(Prompt):
    """Prompt that reads a number as a number"""
    value = common.IntegerProperty(
        "value", """Integer numeral to read""",
    )
    def read(self, agi, escapeDigits):
        """Read the audio prompt to the user"""
        return agi.sayNumber(self.value, escapeDigits)
コード例 #6
0
ファイル: priexhaustionbare.py プロジェクト: vitimm144/starpy
class ChannelTracker(propertied.Propertied):
    """Track open channels on the Asterisk server"""
    channels = common.DictionaryProperty(
        "channels",
        """Set of open channels on the system""",
    )
    thresholdCount = common.IntegerProperty(
        "thresholdCount",
        """Storage of threshold below which we don't warn user""",
        defaultValue=20,
    )

    def main(self):
        """Main operation for the channel-tracking demo"""
        APPLICATION.amiSpecifier.login().addCallback(self.onAMIConnect)

    def onAMIConnect(self, ami):
        ami.status().addCallback(self.onStatus, ami=ami)
        ami.registerEvent('Hangup', self.onChannelHangup)
        ami.registerEvent('Newchannel', self.onChannelNew)

    def onStatus(self, events, ami=None):
        """Integrate the current status into our set of channels"""
        log.debug("""Initial channel status retrieved""")
        for event in events:
            self.onChannelNew(ami, event)

    def onChannelNew(self, ami, event):
        """Handle creation of a new channel"""
        log.debug("""Start on channel %s""", event)
        opening = event['uniqueid'] not in self.channels
        self.channels[event['uniqueid']] = event
        if opening:
            self.onChannelChange(ami, event, opening=opening)

    def onChannelHangup(self, ami, event):
        """Handle hangup of an existing channel"""
        try:
            del self.channels[event['uniqueid']]
        except KeyError as err:
            log.warn("""Hangup on unknown channel %s""", event)
        else:
            log.debug("""Hangup on channel %s""", event)
        self.onChannelChange(ami, event, opening=False)

    def onChannelChange(self, ami, event, opening=False):
        """Channel count has changed, do something useful like enforcing limits"""
        if opening and len(self.channels) > self.thresholdCount:
            log.warn("""Current channel count: %s""", len(self.channels))
        else:
            log.info("""Current channel count: %s""", len(self.channels))
コード例 #7
0
ファイル: menu.py プロジェクト: daasara/riba
class Menu(Interaction):
    """IVR-based menu, returns options selected by the user and keypresses
	
	The Menu holds a collection of Option instances along with a prompt 
	which presents those options to the user.  The menu will attempt to 
	collect the user's selected option up to maxRepetitions times, playing 
	the prompt each time.
	
	If tellInvalid is true, will allow any character being pressed to stop
	the playback, and will tell the user if the pressed character is not 
	recognised.  Otherwise will simply ignore a pressed character which isn't
	part of an Option object's 'option' property.
	
	The menu will chain into callable Options, so that SubMenu and ExitOn can
	be used to produce effects such as multi-level menus with options to 
	return to the parent menu level.
	
	Returns [(option,char(pressedKey))...] for each level of menu explored
	"""
    INVALID_OPTION_FILE = 'pm-invalid-option'
    prompt = common.ListProperty(
        "prompt",
        """(Set of) prompts to run, can be Prompt instances or filenames
		
		Used by the PromptRunner to produce prompt selections
		""",
    )
    textPrompt = common.StringProperty(
        "textPrompt",
        """Textual prompt describing the option""",
    )
    options = common.ListProperty(
        "options",
        """Set of options the user may select""",
    )
    tellInvalid = common.IntegerProperty(
        "tellInvalid",
        """Whether to tell the user that their selection is unrecognised""",
        defaultValue=True,
    )
    runnerClass = MenuRunner
コード例 #8
0
ファイル: storage.py プロジェクト: eshikvtumane/basicproperty
class WithProps(object):
    currentState = common.IntegerProperty(
        "currentState",
        """The current state of this instance""",
        defaultValue=3,
    )
    someSource = common.DictionaryProperty(
        "someSource",
        """Source for properties in state 4""",
    )

    someProp = StateBasedProperty(
        "someProp",
        """A state-aware generic property""",
        defaultFunction=lambda prop, client: client.__class__.__name__,
        setDefaultOnGet=0,
    )
    someOtherProp = DictStateBasedProperty(
        "someOtherProp",
        """A state-aware dictionary property (with automatic default""",
    )
コード例 #9
0
ファイル: utilapplication.py プロジェクト: daasara/riba
class AGISpecifier(propertied.Propertied):
    """Specifier of where we send the user to connect to our AGI"""
    port = common.IntegerProperty(
        "port",
        """IP port on which to listen""",
        defaultValue=4573,
    )
    interface = common.StringLocaleProperty(
        "interface",
        """IP interface on which to listen (local only by default)""",
        defaultValue='127.0.0.1',
    )
    context = common.StringLocaleProperty(
        "context",
        """Asterisk context to which to connect incoming calls""",
        defaultValue='survey',
    )

    def run(self, mainFunction):
        """Start up the AGI server with the given mainFunction"""
        f = fastagi.FastAGIFactory(mainFunction)
        return reactor.listenTCP(self.port, f, 50, self.interface)
コード例 #10
0
class Simple(propertied.Propertied):
    count = common.IntegerProperty(
        "count",
        """Count some value for us""",
        defaultValue=0,
    )
    names = common.StringsProperty(
        "names",
        """Some names as a list of strings""",
    )
    mapping = common.DictionaryProperty(
        "mapping",
        """Mapping from name to number""",
        defaultValue=[
            ('tim', 3),
            ('tom', 4),
            ('bryan', 5),
        ],
    )

    def __repr__(self):
        className = self.__class__.__name__

        def clean(value):
            value = value.splitlines()[0]
            if len(value) > 30:
                value = value[:27] + '...'
            return value

        props = ", ".join([
            '%s=%s' % (prop.name, repr(prop.__get__(self)))
            for prop in self.getProperties() if hasattr(self, prop.name)
        ])
        return '<%(className)s %(props)s>' % locals()

    __str__ = __repr__
コード例 #11
0
ファイル: menu.py プロジェクト: russellb/starpy
class Runner(propertied.Propertied):
    """User's interaction with a given Interaction-type"""
    agi = basic.BasicProperty(
        "agi", """The AGI instance we use to communicate with the user""",
    )
    def defaultFinalDF(prop, client):
        """Produce the default finalDF with onSuccess/onFailure support"""
        df = defer.Deferred()
        model = client.model
        if hasattr(model, 'onSuccess'):
            log.debug('register onSuccess: %s', model.onSuccess)
            df.addCallback(model.onSuccess, runner=client)
        if hasattr(model, 'onFailure'):
            log.debug('register onFailure: %s', model.onFailure)
            df.addErrback(model.onFailure, runner=client)
        return df
    finalDF = basic.BasicProperty(
        "finalDF", """Final deferred we will callback/errback on success/failure""",
        defaultFunction = defaultFinalDF,
    )
    del defaultFinalDF

    alreadyRepeated = common.IntegerProperty(
        "alreadyRepeated", """Number of times we've repeated the message...""",
        defaultValue = 0,
    )
    model = basic.BasicProperty(
        "model", """The data-model that we are presenting to the user (e.g. Menu)""",
    )

    def returnResult(self, result):
        """Return result of deferred to our original caller"""
        log.debug('returnResult: %s %s', self.model,result)
        if not self.finalDF.called:
            self.finalDF.debug = True
            self.finalDF.callback(result)
        else:
            log.debug('finalDF already called, ignoring %s', result)
        return result

    def returnError(self, reason):
        """Return failure of deferred to our original caller"""
        log.debug('returnError: %s', self.model)
        if not isinstance(reason.value, error.MenuExit):
            log.warn("""Failure during menu: %s""", reason.getTraceback())
        if not self.finalDF.called:
            self.finalDF.debug = True
            self.finalDF.errback(reason)
        else:
            log.debug('finalDF already called, ignoring %s', reason.getTraceback())

    def promptAsRunner(self, prompt):
        """Take set of prompt-compatible objects and produce a PromptRunner for them"""
        realPrompt = []
        for p in prompt:
            if isinstance(p, (str, unicode)):
                p = AudioPrompt(p)
            elif isinstance(p, int):
                p = NumberPrompt(p)
            elif not isinstance(p, Prompt):
                raise TypeError( """Unknown prompt element type on %r: %s"""%(
                    p, p.__class__,
                ))
            realPrompt.append(p)
        return PromptRunner(
            elements = realPrompt,
            escapeDigits = self.escapeDigits,
            agi = self.agi,
            timeout = self.model.timeout,
        )
コード例 #12
0
ファイル: frontend.py プロジェクト: vitimm144/starpy
class Survey(propertied.Propertied):
    """Models a single survey to be completed"""
    surveyId = common.IntegerProperty(
        "surveyId",
        """Unique identifier for this survey""",
    )
    owner = basic.BasicProperty(
        "owner",
        """Owner's phone number to which to connect""",
    )
    questions = common.ListProperty(
        "questions",
        """Set of questions which make up the survey""",
    )
    YOU_CURRENTLY_HAVE = 'vm-youhave'
    QUESTIONS_IN_YOUR_SURVEY = 'vm-messages'
    QUESTION_IN_YOUR_SURVEY = 'vm-message'
    TO_LISTEN_TO_SURVEY_QUESTION = 'to-listen-to-it'
    TO_RECORD_A_NEW_SURVEY_QUESTION = 'to-rerecord-it'
    TO_FINISH_SURVEY_SETUP = 'vm-helpexit'

    def setupSurvey(self, agi):
        """AGI application to allow the user to set up the survey

        Screen 1:
            You have # questions.
            To listen to a question, press the number of the question.
            To record a new question, press pound.
            To finish setup, press star.
        """
        seq = fastagi.InSequence()
        seq.append(agi.wait, 2)
        base = """You currently have %s question%s.
        To listen to a question press the number of the question.
        To record a new question, press pound.
        To finish survey setup, press star.
        """ % (
            len(self.questions),
            ['', 's'][len(self.questions) == 1],
        )
        if len(base) != 1:
            base += 's'
        base = " ".join(base.split())
        seq.append(agi.execute, 'Festival', base)
        seq.append(agi.finish, )
        return seq()
        seq.append(agi.streamFile, self.YOU_CURRENTLY_HAVE)
        seq.append(agi.sayNumber, len(self.questions))
        if len(self.questions) == 1:
            seq.append(agi.streamFile, self.QUESTION_IN_YOUR_SURVEY)
        else:
            seq.append(agi.streamFile, self.QUESTIONS_IN_YOUR_SURVEY)
        seq.append(agi.streamFile, self.TO_LISTEN_TO_SURVEY_QUESTION)
        seq.append(agi.streamFile, self.TO_RECORD_A_NEW_SURVEY_QUESTION)
        seq.append(agi.streamFile, self.TO_FINISH_SURVEY_SETUP)
        seq.append(agi.finish, )
        return seq()

    def newQuestionId(self):
        """Return a new, unique, question id"""
        bad = True
        while bad:
            bad = False
            id = random.randint(0, sys.maxint)
            for question in self.questions:
                if id == question.__dict__.get('questionId'):
                    bad = True
        return id
コード例 #13
0
class ChannelTracker(propertied.Propertied):
    """Track open channels on the Asterisk server"""
    channels = common.DictionaryProperty(
        "channels",
        """Set of open channels on the system""",
    )
    thresholdCount = common.IntegerProperty(
        "thresholdCount",
        """Storage of threshold below which we don't warn user""",
        defaultValue=20,
    )

    def main(self):
        """Main operation for the channel-tracking demo"""
        amiDF = APPLICATION.amiSpecifier.login().addCallback(self.onAMIConnect)
        # XXX do something useful on failure to login...
    def onAMIConnect(self, ami):
        """Register for AMI events"""
        # XXX should do an initial query to populate channels...
        # XXX should handle asterisk reboots (at the moment the AMI
        # interface will just stop generating events), not a practical
        # problem at the moment, but should have a periodic check to be sure
        # the interface is still up, and if not, should close and restart
        log.debug('onAMIConnect')
        ami.status().addCallback(self.onStatus, ami=ami)
        ami.registerEvent('Hangup', self.onChannelHangup)
        ami.registerEvent('Newchannel', self.onChannelNew)

    def interestingEvent(self, event, ami=None):
        """Decide whether this channel event is interesting 
		
		Real-world application would want to take only Zap channels, or only
		channels from a given context, or whatever other filter you want in 
		order to capture *just* the scarce resource (such as PRI lines).
		
		Keep in mind that an "interesting" event must show up as interesting 
		for *both* Newchannel and Hangup events or you will leak 
		references/channels or have unknown channels hanging up.
		"""
        return True

    def onStatus(self, events, ami=None):
        """Integrate the current status into our set of channels"""
        log.debug("""Initial channel status retrieved""")
        for event in events:
            self.onChannelNew(ami, event)

    def onChannelNew(self, ami, event):
        """Handle creation of a new channel"""
        log.debug("""Start on channel %s""", event)
        if self.interestingEvent(event, ami):
            opening = not self.channels.has_key(event['uniqueid'])
            self.channels[event['uniqueid']] = event
            if opening:
                self.onChannelChange(ami, event, opening=opening)

    def onChannelHangup(self, ami, event):
        """Handle hangup of an existing channel"""
        if self.interestingEvent(event, ami):
            try:
                del self.channels[event['uniqueid']]
            except KeyError, err:
                log.warn("""Hangup on unknown channel %s""", event)
            else:
                log.debug("""Hangup on channel %s""", event)
            self.onChannelChange(ami, event, opening=False)