summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--flake.nix1
-rw-r--r--src/pollyanna/arbitrary.py48
-rw-r--r--src/pollyanna/pollyanna.py34
-rw-r--r--src/setup.py2
4 files changed, 77 insertions, 8 deletions
diff --git a/flake.nix b/flake.nix
index 553f483..fbbf696 100644
--- a/flake.nix
+++ b/flake.nix
@@ -35,6 +35,7 @@
           gst-plugins-base
           gst-plugins-good
         ]) ++ (with pkgs.python3Packages; [
+          more-itertools
           requests
           pygobject3
         ]);
diff --git a/src/pollyanna/arbitrary.py b/src/pollyanna/arbitrary.py
new file mode 100644
index 0000000..fbaba4c
--- /dev/null
+++ b/src/pollyanna/arbitrary.py
@@ -0,0 +1,48 @@
+from more_itertools import peekable
+import re
+
+
+class ArbitraryParser:
+    """Matches command strings with %s in them denoting arbitrary phrases"""
+
+    def __init__(self):
+        self.all_words = []
+        self.all_words.append("amazing")
+        self.all_words.append("this")
+        self.all_words.append("is")
+        self.all_words.append("a")
+        self.all_words.append("test")
+        self.all_words.append("of")
+        self.all_words.append("pollyana")
+        self.all_words.append("voice")
+        self.all_words.append("typing")
+        self.all_words.append("fuck")
+        self.all_words.append("yeah")
+
+    def match(self, command, text_line):
+        """
+        Determine whether a line of input text matches a command that has %s
+        in it. Commands without %s may be passed, and will never match.
+
+        Returns None if no match, or a list of the words that matched the %s
+        if one was found.
+
+        Only a single %s is supported and it must be at the end of the
+        command.
+        """
+
+        command_words = re.split(r'[,\s-]+', command.strip())
+        text_words = re.split(r'[,\s-]+', text_line.strip())
+
+        text_iter = peekable(text_words)
+        for word in command_words:
+            is_text_end = text_iter.peek(default=None) == None
+            if word == '%s':
+                if not is_text_end:
+                    return list(text_iter)
+                else:
+                    return None
+            elif is_text_end or word != next(text_iter):
+                return None
+
+        return None
diff --git a/src/pollyanna/pollyanna.py b/src/pollyanna/pollyanna.py
index 295a1e5..b1ad38d 100644
--- a/src/pollyanna/pollyanna.py
+++ b/src/pollyanna/pollyanna.py
@@ -9,6 +9,7 @@ import os.path
 import subprocess
 from gi.repository import GObject, GLib
 
+from pollyanna.arbitrary import ArbitraryParser
 from pollyanna.recognizer import Recognizer
 from pollyanna.util import *
 from pollyanna.numbers import NumberParser
@@ -26,8 +27,9 @@ class Pollyanna:
         self.options = vars(self.config.options)
         self.commands = self.options['commands']
 
-        # Create number parser for later use
+        # Create parsers for later use
         self.number_parser = NumberParser()
+        self.arbitrary_parser = ArbitraryParser()
 
         # Create a hasher
         self.hasher = Hasher(self.config)
@@ -88,10 +90,13 @@ class Pollyanna:
         with open(self.config.strings_file, 'w') as strings:
             # Add command words to the corpus
             for voice_cmd in sorted(self.commands.keys()):
-                strings.write(voice_cmd.strip().replace('%d', '') + "\n")
+                strings.write(re.sub(r'%[ds]', '', voice_cmd.strip()) + "\n")
             # Add number words to the corpus
             for word in self.number_parser.number_words:
                 strings.write(word + " ")
+            # Add dictionary words for arbitrary substrings to the corpus
+            for word in self.arbitrary_parser.all_words:
+                strings.write(word + " ")
             strings.write("\n")
 
     def log_history(self, text):
@@ -139,11 +144,26 @@ class Pollyanna:
             self.run_command(cmd)
             self.log_history(text)
         else:
-            # Run the invalid_sentence_command if it's set
-            if self.options['invalid_sentence_command']:
-                subprocess.call(self.options['invalid_sentence_command'],
-                                shell=True)
-            print("no matching command {0}".format(t))
+            is_matched = False
+            for command in self.commands:
+                match_result = self.arbitrary_parser.match(command, t)
+                if match_result != None:
+                  is_matched = True
+                  break
+            if is_matched:
+                cmd = self.commands[command]
+                cmd = cmd.format(' '.join(match_result))
+                # Should we be passing words?
+                if self.options['pass_words']:
+                    cmd += " " + t
+                self.run_command(cmd)
+                self.log_history(text)
+            else:
+                # Run the invalid_sentence_command if it's set
+                if self.options['invalid_sentence_command']:
+                    subprocess.call(self.options['invalid_sentence_command'],
+                                    shell=True)
+                print("no matching command {0}".format(t))
         # If there is a UI and we are not continuous listen
         if self.ui:
             if not self.continuous_listen:
diff --git a/src/setup.py b/src/setup.py
index 347e5cf..d778f06 100644
--- a/src/setup.py
+++ b/src/setup.py
@@ -25,7 +25,7 @@ setup(
         "Programming Language :: Python :: 3.5",
         "Topic :: Home Automation"
     ],
-    install_requires=["requests"],
+    install_requires=["more-itertools", "requests"],
     data_files = [
         ("/usr/share/pollyanna",
          ["data/icon.png", "data/icon_small.png",