|
@@ -5,16 +5,18 @@
|
5
|
5
|
# Copyright 2013 Jezra
|
6
|
6
|
# Copyright 2015 Clayton G. Hobbs
|
7
|
7
|
|
|
8
|
+from __future__ import print_function
|
8
|
9
|
import sys
|
9
|
10
|
import signal
|
10
|
|
-from gi.repository import GObject
|
|
11
|
+import hashlib
|
11
|
12
|
import os.path
|
12
|
13
|
import subprocess
|
13
|
14
|
from optparse import OptionParser
|
|
15
|
+from gi.repository import GObject
|
14
|
16
|
try:
|
15
|
17
|
import yaml
|
16
|
18
|
except:
|
17
|
|
- print "YAML is not supported. ~/.config/blather/options.yaml will not function"
|
|
19
|
+ print("YAML is not supported; unable to use config file")
|
18
|
20
|
|
19
|
21
|
from recognizer import Recognizer
|
20
|
22
|
|
|
@@ -25,8 +27,9 @@ command_file = os.path.join(conf_dir, "commands.conf")
|
25
|
27
|
strings_file = os.path.join(conf_dir, "sentences.corpus")
|
26
|
28
|
history_file = os.path.join(conf_dir, "blather.history")
|
27
|
29
|
opt_file = os.path.join(conf_dir, "options.yaml")
|
28
|
|
-lang_file = os.path.join(lang_dir,'lm')
|
29
|
|
-dic_file = os.path.join(lang_dir,'dic')
|
|
30
|
+hash_file = os.path.join(conf_dir, "hash.yaml")
|
|
31
|
+lang_file = os.path.join(lang_dir, 'lm')
|
|
32
|
+dic_file = os.path.join(lang_dir, 'dic')
|
30
|
33
|
# Make the lang_dir if it doesn't exist
|
31
|
34
|
if not os.path.exists(lang_dir):
|
32
|
35
|
os.makedirs(lang_dir)
|
|
@@ -34,7 +37,6 @@ if not os.path.exists(lang_dir):
|
34
|
37
|
class Blather:
|
35
|
38
|
|
36
|
39
|
def __init__(self, opts):
|
37
|
|
- # Import the recognizer so Gst doesn't clobber our -h
|
38
|
40
|
self.ui = None
|
39
|
41
|
self.options = {}
|
40
|
42
|
ui_continuous_listen = False
|
|
@@ -48,8 +50,8 @@ class Blather:
|
48
|
50
|
# Load the options file
|
49
|
51
|
self.load_options()
|
50
|
52
|
|
51
|
|
- # Merge the opts
|
52
|
|
- for k,v in opts.__dict__.items():
|
|
53
|
+ # Merge the options with the ones provided by command-line arguments
|
|
54
|
+ for k, v in opts.__dict__.items():
|
53
|
55
|
if (not k in self.options) or opts.override:
|
54
|
56
|
self.options[k] = v
|
55
|
57
|
|
|
@@ -59,7 +61,7 @@ class Blather:
|
59
|
61
|
elif self.options['interface'] == "gt":
|
60
|
62
|
from gtktrayui import UI
|
61
|
63
|
else:
|
62
|
|
- print "no GUI defined"
|
|
64
|
+ print("no GUI defined")
|
63
|
65
|
sys.exit()
|
64
|
66
|
|
65
|
67
|
self.ui = UI(args, self.options['continuous'])
|
|
@@ -76,47 +78,51 @@ class Blather:
|
76
|
78
|
if self.options['history']:
|
77
|
79
|
self.history = []
|
78
|
80
|
|
|
81
|
+ # Update the language if necessary
|
|
82
|
+ self.update_language()
|
|
83
|
+
|
79
|
84
|
# Create the recognizer
|
80
|
85
|
try:
|
81
|
86
|
self.recognizer = Recognizer(lang_file, dic_file, self.options['microphone'])
|
82
|
|
- except Exception, e:
|
83
|
|
- #no recognizer? bummer
|
84
|
|
- print 'error making recognizer'
|
|
87
|
+ except Exception as e:
|
|
88
|
+ # No recognizer? bummer
|
|
89
|
+ print('error making recognizer')
|
85
|
90
|
sys.exit()
|
86
|
91
|
|
87
|
92
|
self.recognizer.connect('finished', self.recognizer_finished)
|
88
|
93
|
|
89
|
|
- print "Using Options: ", self.options
|
|
94
|
+ print("Using Options: ", self.options)
|
90
|
95
|
|
91
|
96
|
def read_commands(self):
|
92
|
97
|
# Read the commands file
|
93
|
98
|
file_lines = open(command_file)
|
94
|
99
|
strings = open(strings_file, "w")
|
95
|
100
|
for line in file_lines:
|
96
|
|
- print line
|
|
101
|
+ print(line)
|
97
|
102
|
# Trim the white spaces
|
98
|
103
|
line = line.strip()
|
99
|
104
|
# If the line has length and the first char isn't a hash
|
100
|
105
|
if len(line) and line[0]!="#":
|
101
|
106
|
# This is a parsible line
|
102
|
|
- (key,value) = line.split(":",1)
|
103
|
|
- print key, value
|
|
107
|
+ (key, value) = line.split(":", 1)
|
|
108
|
+ print(key, value)
|
104
|
109
|
self.commands[key.strip().lower()] = value.strip()
|
105
|
110
|
strings.write( key.strip()+"\n")
|
106
|
111
|
# Close the strings file
|
107
|
112
|
strings.close()
|
108
|
113
|
|
109
|
114
|
def load_options(self):
|
|
115
|
+ """If possible, load options from the options.yaml file"""
|
110
|
116
|
# Is there an opt file?
|
111
|
117
|
try:
|
112
|
118
|
opt_fh = open(opt_file)
|
113
|
119
|
text = opt_fh.read()
|
114
|
120
|
self.options = yaml.load(text)
|
115
|
121
|
except:
|
|
122
|
+ # Do nothing if the options file cannot be loaded
|
116
|
123
|
pass
|
117
|
124
|
|
118
|
|
-
|
119
|
|
- def log_history(self,text):
|
|
125
|
+ def log_history(self, text):
|
120
|
126
|
if self.options['history']:
|
121
|
127
|
self.history.append(text)
|
122
|
128
|
if len(self.history) > self.options['history']:
|
|
@@ -130,9 +136,45 @@ class Blather:
|
130
|
136
|
# Close the file
|
131
|
137
|
hfile.close()
|
132
|
138
|
|
|
139
|
+ def update_language(self):
|
|
140
|
+ """Update the language if its hash has changed"""
|
|
141
|
+ try:
|
|
142
|
+ # Load the stored hash from the hash file
|
|
143
|
+ try:
|
|
144
|
+ with open(hash_file, 'r') as f:
|
|
145
|
+ text = f.read()
|
|
146
|
+ hashes = yaml.load(text)
|
|
147
|
+ stored_hash = hashes['language']
|
|
148
|
+ except (IOError, KeyError, TypeError):
|
|
149
|
+ # No stored hash
|
|
150
|
+ stored_hash = ''
|
|
151
|
+
|
|
152
|
+ # Calculate the hash the language file has right now
|
|
153
|
+ hasher = hashlib.sha256()
|
|
154
|
+ with open(strings_file, 'rb') as sfile:
|
|
155
|
+ buf = sfile.read()
|
|
156
|
+ hasher.update(buf)
|
|
157
|
+ new_hash = hasher.hexdigest()
|
|
158
|
+
|
|
159
|
+ # If the hashes differ
|
|
160
|
+ if stored_hash != new_hash:
|
|
161
|
+ # Update the language
|
|
162
|
+ # FIXME: Do this with Python, not Bash
|
|
163
|
+ self.run_command('./language_updater.sh')
|
|
164
|
+ # Store the new hash
|
|
165
|
+ new_hashes = {'language': new_hash}
|
|
166
|
+ with open(hash_file, 'w') as f:
|
|
167
|
+ f.write(yaml.dump(new_hashes))
|
|
168
|
+ except Exception as e:
|
|
169
|
+ # Do nothing if the hash file cannot be loaded
|
|
170
|
+ # FIXME: This is kind of bad; maybe YAML should be mandatory.
|
|
171
|
+ print('error updating language')
|
|
172
|
+ print(e)
|
|
173
|
+ pass
|
|
174
|
+
|
133
|
175
|
def run_command(self, cmd):
|
134
|
|
- '''Print the command, then run it'''
|
135
|
|
- print cmd
|
|
176
|
+ """Print the command, then run it"""
|
|
177
|
+ print(cmd)
|
136
|
178
|
subprocess.call(cmd, shell=True)
|
137
|
179
|
|
138
|
180
|
def recognizer_finished(self, recognizer, text):
|
|
@@ -154,7 +196,7 @@ class Blather:
|
154
|
196
|
# Run the invalid_sentence_command if there is an invalid sentence command
|
155
|
197
|
if self.options['invalid_sentence_command']:
|
156
|
198
|
subprocess.call(self.options['invalid_sentence_command'], shell=True)
|
157
|
|
- print "no matching command %s" % t
|
|
199
|
+ print("no matching command {0}".format(t))
|
158
|
200
|
# If there is a UI and we are not continuous listen
|
159
|
201
|
if self.ui:
|
160
|
202
|
if not self.continuous_listen:
|
|
@@ -173,7 +215,7 @@ class Blather:
|
173
|
215
|
sys.exit()
|
174
|
216
|
|
175
|
217
|
def process_command(self, UI, command):
|
176
|
|
- print command
|
|
218
|
+ print(command)
|
177
|
219
|
if command == "listen":
|
178
|
220
|
self.recognizer.listen()
|
179
|
221
|
elif command == "stop":
|
|
@@ -187,9 +229,9 @@ class Blather:
|
187
|
229
|
elif command == "quit":
|
188
|
230
|
self.quit()
|
189
|
231
|
|
190
|
|
- def load_resource(self,string):
|
|
232
|
+ def load_resource(self, string):
|
191
|
233
|
local_data = os.path.join(os.path.dirname(__file__), 'data')
|
192
|
|
- paths = ["/usr/share/blather/","/usr/local/share/blather", local_data]
|
|
234
|
+ paths = ["/usr/share/blather/", "/usr/local/share/blather", local_data]
|
193
|
235
|
for path in paths:
|
194
|
236
|
resource = os.path.join(path, string)
|
195
|
237
|
if os.path.exists( resource ):
|
|
@@ -247,7 +289,7 @@ if __name__ == "__main__":
|
247
|
289
|
try:
|
248
|
290
|
main_loop.run()
|
249
|
291
|
except:
|
250
|
|
- print "time to quit"
|
|
292
|
+ print("time to quit")
|
251
|
293
|
main_loop.quit()
|
252
|
294
|
sys.exit()
|
253
|
295
|
|