Selaa lähdekoodia

Port Kaylee and shell plugin to new plugin API

The new shell plugin is a lot cleaner, only determining how the command
matches once and parsing numbers zero or one times.

Kaylee uses the new API, but there are still some problems.  Most
notably, Handler objects have no sorting methods yet, so behavior will
be unpredictable when multiple plugins are loaded.
Clara Hobbs 6 vuotta sitten
vanhempi
commit
fd6dbad6b6
2 muutettua tiedostoa jossa 49 lisäystä ja 51 poistoa
  1. 22
    19
      kayleevc/kaylee.py
  2. 27
    32
      kayleevc/plugins/shell.py

+ 22
- 19
kayleevc/kaylee.py Näytä tiedosto

@@ -38,7 +38,6 @@ class Kaylee:
38 38
         for plugin in self.config.plugins.keys():
39 39
             pmod = importlib.import_module(plugin, 'kayleevc.plugins')
40 40
             pobj = pmod.Plugin(self.config, plugin)
41
-            pobj.connect('tts', self.plugin_tts)
42 41
             self.plugins.append(pobj)
43 42
 
44 43
         # Create a hasher
@@ -139,18 +138,18 @@ class Kaylee:
139 138
     def recognizer_finished(self, recognizer, text):
140 139
         confidence_heap = queue.PriorityQueue()
141 140
         min_confidence = self.options['minimum_confidence']
142
-        # Add plugins to the heap
141
+        # Add handlers to the heap
143 142
         for index, plugin in enumerate(self.plugins):
144
-            # Get plugin confidence
145
-            confidence = plugin.confidence(text)
146
-            # Clamp confidence to [0, 1]
147
-            confidence = min(max(confidence, 0), 1)
148
-            # If the plugin meets minimum confidence
149
-            if ((min_confidence is not None and confidence >= min_confidence)
150
-                or (min_confidence is None and confidence > 0)):
151
-                # Add a triple to the heap, so plugins are sorted first by
152
-                # confidence, then by index to break ties
153
-                confidence_heap.put_nowait((1 - confidence, index, plugin))
143
+            # Get a handler
144
+            handler = plugin.get_handler(text)
145
+            # If the handler meets minimum confidence
146
+            if ((handler is not None)
147
+                and ((min_confidence is None)
148
+                     or (min_confidence is not None
149
+                         and handler.confidence >= min_confidence))):
150
+                # Add a triple to the heap, so handlers are sorted first by
151
+                # their confidence, then by index to break ties
152
+                confidence_heap.put_nowait((handler, index, plugin))
154 153
 
155 154
         # Run valid or invalid sentence command
156 155
         if confidence_heap.empty():
@@ -158,20 +157,24 @@ class Kaylee:
158 157
         else:
159 158
             self._valid_cmd()
160 159
 
161
-        # Give the command to the plugins that want it
160
+        # Give the command to the handlers that want it
162 161
         while True:
163 162
             try:
164
-                plugin = confidence_heap.get_nowait()[2]
165
-            except queue.Empty:
166
-                break
167
-            # If the plugin successfully handled the command
168
-            if plugin.handle(text):
163
+                # Get information about the handler
164
+                info = confidence_heap.get_nowait()
165
+                handler = info[0]
166
+                plugin = info[2]
167
+                # Handle the command
168
+                handler(self.plugin_tts)
169 169
                 self._log_history(plugin, text)
170
+            except queue.Empty:
170 171
                 break
172
+                # If an unknown error occurs, ignore it and keep trying
173
+                # FIXME not a good API decision
171 174
 
172 175
         self._stop_ui(text)
173 176
 
174
-    def plugin_tts(self, plugin, text):
177
+    def plugin_tts(self, text):
175 178
         # Stop listening
176 179
         self.recognizer.pause()
177 180
         # Speak

+ 27
- 32
kayleevc/plugins/shell.py Näytä tiedosto

@@ -28,7 +28,7 @@ is::
28 28
 
29 29
 import subprocess
30 30
 
31
-from .pluginbase import PluginBase
31
+from . import PluginBase, Handler
32 32
 from ..numbers import NumberParser
33 33
 
34 34
 
@@ -44,38 +44,33 @@ class Plugin(PluginBase):
44 44
         for voice_cmd in self.commands.keys():
45 45
             self.corpus_strings.add(voice_cmd.strip().replace('%d', ''))
46 46
 
47
-    def _run_command(self, cmd):
48
-        """Print the command, then run it"""
49
-        print(cmd)
50
-        subprocess.call(cmd, shell=True)
51
-
52
-    def confidence(self, text):
47
+    def get_handler(self, text):
53 48
         """Return whether or not the command can be handled"""
54
-        numt, nums = self.number_parser.parse_all_numbers(text)
55 49
         if text in self.commands:
56
-            return 1
57
-        elif numt in self.commands:
58
-            return 1
50
+            # If there's an exact match, return a handler for it
51
+            return ShellHandler(1, self.commands[text])
59 52
         else:
60
-            return 0
61
-
62
-    def handle(self, text):
63
-        """Run the shell command corresponding to a recognized voice command
64
-
65
-        Spoken number(s) may be substituted in the shell command.  The shell
66
-        commands are printed immediately before they are executed.
67
-        """
68
-        numt, nums = self.number_parser.parse_all_numbers(text)
69
-        # Is there a matching command?
70
-        if text in self.commands:
71
-            cmd = self.commands[text]
72
-            self._run_command(cmd)
73
-            return True
74
-        # Is there a matching command with numbers substituted?
75
-        elif numt in self.commands:
76
-            cmd = self.commands[numt]
77
-            cmd = cmd.format(*nums)
78
-            self._run_command(cmd)
79
-            return True
53
+            # Otherwise, try substituting numbers
54
+            numt, nums = self.number_parser.parse_all_numbers(text)
55
+            if numt in self.commands:
56
+                # Return a handler if we have a match
57
+                return ShellHandler(1, self.commands[numt], nums)
58
+            # Return None if that fails too
59
+            return None
60
+
61
+
62
+class ShellHandler(Handler):
63
+    """Handle a voice command that corresponds to a shell command"""
64
+
65
+    def __init__(self, confidence, command, nums=None):
66
+        """Determine the exact command that will be run"""
67
+        super().__init__(confidence)
68
+        if nums is None:
69
+            self.command = command
80 70
         else:
81
-            return False
71
+            self.command = command.format(*nums)
72
+
73
+    def __call__(self, tts):
74
+        """Print the command, then run it"""
75
+        print(self.command)
76
+        subprocess.call(self.command, shell=True)

Loading…
Peruuta
Tallenna