コード例 #1
0
    def evaluate(self, tokens):
        """
        @see Service.evaluate()
        """
        # Get the words, ready to match with
        words = self._words(tokens)

        # This is how it could be phrased
        fixes = ((('define', ), tuple()), (('what', 'is', 'the', 'meaning',
                                            'of'), tuple()), (('what', 'does'),
                                                              ('mean', )))
        match = None
        for (prefix, suffix) in fixes:
            try:
                # Look for the prefix and suffix in the words
                if len(prefix) > 0:
                    (pre_start, pre_end,
                     pre_score) = fuzzy_list_range(words, prefix)
                else:
                    (pre_start, pre_end, pre_score) = (0, 0, 100)
                if len(suffix) > 0:
                    (suf_start, suf_end,
                     suf_score) = fuzzy_list_range(words, suffix)
                else:
                    (suf_start, suf_end, suf_score) = (len(words), len(words),
                                                       100)
                LOG.debug(
                    "%s matches %s with from %d to %d with score %d, "
                    "and %s matches from %d to %d with score %d", prefix,
                    words, pre_start, pre_end, pre_score, suffix, suf_start,
                    suf_end, suf_score)

                # We expect there to be only one word in the middle of the
                # prefix and suffix when we match
                if (pre_start == 0 and pre_end + 1 == suf_start
                        and suf_end == len(words)
                        and (match is None or match[2] < score)):
                    match = (pre_start, pre_end, pre_score, suf_start, suf_end,
                             suf_score)
            except ValueError:
                pass

        # Did we get anything?
        if match is not None:
            # Pull back the values
            (pre_start, pre_end, pre_score, suf_start, suf_end,
             suf_score) = match

            # The belief is the geometric distance of the scores
            belief = sqrt(pre_score * pre_score +
                          suf_score * suf_score) / 100.0

            # The word is the one at pre_end (since it's non-inclusive)
            word = words[pre_end]

            # And give back the handler
            return _DictionaryHandler(self, tokens, belief, word, self._limit)
        else:
            # Nope, we got nothing
            return None
コード例 #2
0
ファイル: chronos.py プロジェクト: iamsrp/dexter
    def evaluate(self, tokens):
        """
        @see Service.evaluate()
        """
        # We use a number of different prefices here since the word "for" has a
        # homonyms of "four" and "set" apparently sounds like "said". Yes, I
        # know I could do a cross product here but...
        words = self._words(tokens)
        phrase = ('set', 'a', 'timer', 'for')
        try:
            (start, end, score) = fuzzy_list_range(words, phrase)
            return _SetTimerHandler(self, tokens, words[end:])
        except Exception as e:
            pass

        # And now for cancelling
        phrase = ('cancel', 'timer')
        try:
            index = fuzzy_list_range(words, prefix)
            return _CancelHandler(self, tokens, words)
        except Exception as e:
            pass

        # Didn't find any of the prefices
        return None
コード例 #3
0
    def evaluate(self, tokens):
        """
        @see Service.evaluate()
        """
        words = self._words(tokens)

        # Look for a direct setting
        prefix = ('set', 'volume', 'to')
        try:
            (start, end, _) = fuzzy_list_range(words, prefix)
            return _SetHandler(self, tokens, ' '.join(words[end:]))
        except:
            # Didn't find a match
            pass

        # For the below we need to up the threshold since:
        #  fuzz.ratio('turn up the volume','turn down the volume') == 84

        # Or a request to raise...
        for prefix in (('raise', 'the', 'volume'), ('raise', 'volume'),
                       ('turn', 'the', 'volume', 'up'), ('turn', 'up', 'the',
                                                         'volume')):
            try:
                (start, end, _) = fuzzy_list_range(words, prefix, threshold=85)
                if start == 0 and end == len(words):
                    return _AdjustHandler(self, tokens, 1)
            except:
                # Didn't find a match
                pass

        # ...or lower the volume
        for prefix in (('lower', 'the', 'volume'), ('lower', 'volume'),
                       ('turn', 'the', 'volume', 'down'), ('turn', 'down',
                                                           'the', 'volume')):
            try:
                (start, end, _) = fuzzy_list_range(words, prefix, threshold=85)
                if start == 0 and end == len(words):
                    return _AdjustHandler(self, tokens, -1)
            except:
                # Didn't find a match
                pass

        # Or to shut up completely
        for phrase in ('mute', 'silence', 'quiet', 'shut up'):
            if fuzz.ratio(' '.join(words), phrase) > 80:
                return _SetHandler(self, tokens, 'zero')

        # Otherwise this was not for us
        return None
コード例 #4
0
    def evaluate(self, tokens):
        """
        @see Service.evaluate()
        """
        # Get the words, ready to match with
        words = self._words(tokens)

        # Look for these prefixes
        prefices = (('spell', ), ('how', 'do', 'you', 'spell'))
        match = None
        for prefix in prefices:
            try:
                # Look for the prefix and suffix in the words
                (start, end, score) = fuzzy_list_range(words, prefix)
                LOG.debug("%s matches %s with from %d to %d with score %d",
                          prefix, words, start, end, score)

                # Get the best one
                if (start == 0 and (match is None or match[2] < score)):
                    match = (start, end, score)
            except ValueError:
                pass

        # Did we get anything?
        if match is not None:
            # Simply give these to the handler
            (start, end, score) = match
            return _SpellingHandler(self, tokens, score / 100.0, words[end:])
        else:
            # Nope, we got nothing
            return None
コード例 #5
0
ファイル: fortune.py プロジェクト: iamsrp/dexter
    def evaluate(self, tokens):
        """
        @see Service.evaluate()
        """
        try:
            # If we match the phrase then pick a fortune!
            (s, e, _) = fuzzy_list_range(self._phrase,
                                         self._words(tokens))
            if s == 0 and e == len(self._phrase):
                fortune = self._pick()
                if not fortune:
                    fortune = "I don't have anything to say today it seems"

                # Now possibly tweak the text of the fortune so it works for
                # reading out loud
                fortune = self._speechify(fortune)

                return _FortuneHandler(self, tokens, fortune)

        except ValueError:
            # No match
            pass

        # Not for us it seems
        return None
コード例 #6
0
    def evaluate(self, tokens):
        """
        @see Service.evaluate()
        """
        # Render to lower-case, for matching purposes.
        words = self._words(tokens)

        # Look for these types of queston
        prefices = (('what', 'is', 'a'), ('what', 'is', 'the'), ('what', 'is'),
                    ('who', 'is', 'the'), ('who', 'is'))
        match = None
        for prefix in prefices:
            try:
                # Look for the prefix in the words
                (start, end, score) = fuzzy_list_range(words, prefix)
                LOG.debug("%s matches %s with from %d to %d with score %d",
                          prefix, words, start, end, score)
                if start == 0 and (match is None or match[2] < score):
                    match = (start, end, score)
            except ValueError:
                pass

        # If we got a good match then use it
        if match:
            (start, end, score) = match
            thing = ' '.join(words[end:]).strip().lower()

            # Let's look to see if Wikipedia returns anything when we search
            # for this thing
            best = None
            try:
                self._notify(Notifier.ACTIVE)
                for result in wikipedia.search(thing):
                    if result is None or len(result) == 0:
                        continue
                    score = fuzz.ratio(thing, result.lower())
                    LOG.debug("'%s' matches '%s' with a score of %d", result,
                              thing, score)
                    if best is None or best[1] < score:
                        best = (result, score)
            except Exception as e:
                LOG.error("Failed to query Wikipedia for '%s': %s" %
                          (thing, e))
            finally:
                self._notify(Notifier.IDLE)

            # Turn the words into a string for the handler
            if best is not None:
                return _Handler(self, tokens, best[1] / 100, best[0])

        # If we got here then it didn't look like a query for us
        return None
コード例 #7
0
ファイル: randomness.py プロジェクト: iamsrp/dexter
    def evaluate(self, tokens):
        """
        @see Service.evaluate()
        """
        # The incoming request
        words = self._words(tokens)

        # Binary random number
        for phrase in ("toss a coin", "flip a coin"):
            try:
                fuzzy_list_range(words, phrase)
                return _CoinTossHandler(self, tokens)
            except ValueError:
                pass

        # A regular die
        for phrase in ("roll a die", "roll a dice"):
            try:
                fuzzy_list_range(words, phrase)
                return _DiceHandler(self, tokens, 6)
            except ValueError:
                pass
            
        # A generic request
        try:
            prefix = ('give', 'me', 'a', 'number', 'between')
            (_, offset, _)  = fuzzy_list_range(words, prefix)
            if len(words) >= offset + 3:
                and_index = words.index('and')
                start     = parse_number(words[offset     :and_index])
                end       = parse_number(words[and_index+1:])
                if start is not None and end is not None:
                    return _RangeHandler(self, tokens, start, end)
        except Exception as e:
            LOG.debug("Failed to handle '%s': %s" % (phrase, e))

        # Not for us
        return None
コード例 #8
0
 def evaluate(self, tokens):
     """
     @see Service.evaluate()
     """
     words = self._words(tokens)
     for (what, handler) in self._HANDLERS:
         for prefix in self._PREFICES:
             phrase = (prefix + what)
             try:
                 (s, e, _) = fuzzy_list_range(words, phrase)
                 if s == 0 and e == len(phrase):
                     return handler(self, tokens)
             except Exception as e:
                 LOG.debug("Failed to handle '%s': %s" %
                           (' '.join(words), e))
     return None
コード例 #9
0
ファイル: bespoke.py プロジェクト: iamsrp/dexter
    def evaluate(self, tokens):
        """
        @see Service.evaluate()
        """
        # The incoming text
        words = self._words(tokens)

        # Look for the match phrases
        for (phrase, reply, is_prefix) in self._phrases:
            try:
                LOG.debug("Looking for %s in %s", phrase, words)
                (start, end, score) = fuzzy_list_range(words, phrase)
                LOG.debug("Matched [%d:%d] and score %d", start, end, score)
                if start == 0 and (not is_prefix or end == len(phrase)):
                    return _BespokeHandler(self, tokens, reply)
            except ValueError as e:
                LOG.debug("No match: %s", e)
コード例 #10
0
ファイル: chronos.py プロジェクト: iamsrp/dexter
    def evaluate(self, tokens):
        """
        @see Service.evaluate()
        """
        # Look to match what we were given on a number of different phrasings
        words = self._words(tokens)
        for want in (('whats', 'the', 'time'), ('what', 'is', 'the', 'time'),
                     ('what', 'time', 'is', 'it')):
            try:
                # Match the different pharses on the input
                (start, end, score) = fuzzy_list_range(words, want)

                # We want to match the full input, since we want to avoid people
                # asking for the time with caveats
                if start == 0 and end == len(words):
                    LOG.info("Matched '%s' on '%s'" % (want, words))
                    return _ClockHandler(self, tokens)
            except ValueError:
                pass

        return None
コード例 #11
0
ファイル: dev.py プロジェクト: iamsrp/dexter
 def evaluate(self, tokens):
     """
     @see Service.evaluate()
     """
     # Look for a match in the phrases
     words   = self._words(tokens)
     matches = []
     best    = None
     for phrase in self._phrases:
         try:
             result = fuzzy_list_range(phrase, words)
             matches.append((phrase, words, result))
             (_, _, score) = result
             if best is None or best < score:
                 best = score
         except ValueError:
             pass
     if len(matches) > 0:
         return _MatchHandler(self, tokens, tuple(matches), best)
     else:
         return None
コード例 #12
0
ファイル: music.py プロジェクト: iamsrp/dexter
    def evaluate(self, tokens):
        """
        @see Service.evaluate()
        """
        # Get stripped text, for matching
        words = [homonize(w) for w in self._words(tokens)]

        # Look for specific control words, doing a fuzzy match for single
        # tokens, but an exact match for the "special" phrases (since they
        # typically come from an unambiguously rendering source).
        if len(words) == 1:
            if (self._matches(words[0], "stop")
                    or self._matches(words[0], "pause")):
                return self._get_stop_handler(tokens)
            elif (self._matches(words[0], "play")
                  or self._matches(words[0], "unpause")):
                return self._get_play_handler(tokens)
        elif words == ['next', 'song']:
            return self._get_next_song_handler(tokens)
        elif words == ['previous', 'song']:
            return self._get_prev_song_handler(tokens)
        elif words == ['play', 'or', 'pause']:
            return self._get_toggle_pause_handler(tokens)

        # Now some potentially fuzzier matches
        for (get_handler, phrases) in (
            (self._get_next_song_handler, (
                ('next', 'song'),
                ('play', 'next', 'song'),
                ('go', 'forward', 'a', 'song'),
                ('move', 'forward', 'a', 'song'),
                ('skip', 'forward', 'a', 'song'),
            )),
            (self._get_prev_song_handler, (
                ('previous', 'song'),
                ('play', 'previous', 'song'),
                ('go', 'back', 'a', 'song'),
                ('go', 'backwards', 'a', 'song'),
                ('move', 'back', 'a', 'song'),
                ('move', 'backwards', 'a', 'song'),
                ('skip', 'back', 'a', 'song'),
                ('skip', 'backwards', 'a', 'song'),
            )),
            (self._get_describe_song_handler, (
                ('identify', 'song'),
                ('whats', 'this', 'song'),
                ('what', 'is', ' this', 'song'),
                ('name', 'this', 'song'),
            )),
        ):
            for phrase in phrases:
                try:
                    (s, e, _) = fuzzy_list_range(words, phrase)
                    if s == 0 and e == len(phrase):
                        return get_handler(tokens)
                except ValueError:
                    pass

        # We didn't match on the stock phrases so move on to trying to see if
        # this is a command to play a song.
        #
        # We expect to have something along the lines of:
        #  Play <song or album> on <platform>
        #  Play <genre> music
        #  Play <song or album> by <artist>
        if len(words) < 3:
            # We can't match on this
            return None

        # See if the first word is "play"
        if not self._matches(words[0], "play"):
            # Nope, probably not ours then
            return None

        # Okay, strip off "play"
        words = words[1:]

        # See if it ends with "on <platform>", if so then we can see if it's for
        # us specificaly.
        platform_match = False
        if self._matches(words[-2], "on"):
            # Handle partial matches on the service name since, for example,
            # "spotify" often gets interpreted as "spotty"
            if fuzz.ratio(words[-1], self._platform.lower()) > 50:
                # This is definitely for us
                platform_match = True

                # Strip off the platfrom now so that we can match other things
                words = words[:-2]
            else:
                # Looks like it's for a different platform
                return None

        # See if we have an artist
        artist = None
        if "by" in words and len(words) >= 3:
            # We see if this matches a know artist. It could by something like
            # "Bye Bye Baby" fooling us.

            # Find the last occurance of "by"; hey Python, why no rindex?!
            by_index = len(words) - list(reversed(words)).index("by") - 1
            artist = words[by_index + 1:]
            if self._match_artist(artist):
                # Okay, strip off the artist
                words = words[:by_index]
            else:
                # No match
                artist = None

        # See if it ends with a genre indicator. Don't do this if we matched an
        # artist (since it could be "Sexy Music" by "Meat Puppets", for example).
        if artist is None and len(words) > 1 and self._matches(
                words[-1], "music"):
            genre = tuple(words[:-1])
            words = []
        else:
            genre = None

        # Anything left should be the song-or-album name now
        if len(words) > 0:
            song_or_album = tuple(words)
            words = []

        # Okay, ready to make the hand-off call to the subclass
        return self._get_handler_for(tokens, platform_match, genre, artist,
                                     song_or_album)