-
Notifications
You must be signed in to change notification settings - Fork 0
/
intents_processor.py
184 lines (140 loc) · 5.12 KB
/
intents_processor.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
import re
import utils
from logger import logger
from speechsynth import speech
from settings import Settings, Intents, load_intents
"""
This module allows to process and executes commands from natural language text
"""
LAST_EXECUTED_COMMAND = None
WAIT_ENTIRE_INPUT = False
def normalize_args(arg): # normalize arg content according to settings.literals
if not arg:
return False
for nl, lit in Intents.literals.items():
arg = arg.replace(nl, lit)
if arg.isdigit():
return int(arg)
return arg
def match_command(cmd_list, input_sentence):
"""
Try to match keyword in a cmd_list
"""
found_cmd, arg = '', None
for cmd in cmd_list:
# if is and exactly match
if cmd == input_sentence:
return cmd, None
# else try match command includes with args (ex: run the {n} program)
match = re.findall(f"^{ cmd.replace('{n}', '(.+)') }$", input_sentence)
if match and len(cmd) > len(found_cmd):
found_cmd = cmd
arg = match[0]
return found_cmd, arg
def find_match_command(sentence):
"""
Iterate over command list and find the best match
"""
for cmd_key, value in Intents.all_commands.items():
cmd_list = [cmd_key]
if synms := value.get('alternative'): # synonyms availables
if type(synms) is list:
cmd_list.extend(synms)
else:
logger.warning(f"Alternatives for '{cmd_key}' is not a list!")
matched_cmd, extracted_arg = match_command(cmd_list, sentence)
if matched_cmd:
value['args'] = normalize_args(extracted_arg)
return value
return False
def list_to_or_regx_pattern(words_list: list):
"""
Return a word list to OR style list
ex: # ['ok google', 'ahora'] -> (ok google|ahora.+)
"""
return f"({'|'.join([w for w in words_list])}*)"
def find_into_str(string: str, word_list: list):
return re.findall(f"{ list_to_or_regx_pattern(word_list)}\s(.+)", string)
def match_and_filter_hotword(string: str):
"""
Detect and filter match hotword into a sentence
also OR regex allow to use hotowords with more thatn one word
"""
hotword = find_into_str(string, Intents.hotwords)
return False if not hotword else hotword[0][1]
def reload_intents():
logger.info("Reloading settings")
load_intents()
logger.info("Settings reloaded")
def get_context(): # check context based on win title
win_title = utils.get_win_title()
logger.info(f"Actual window title { win_title }")
for context, apps in Intents.context_list.items():
if win_title.endswith(tuple(apps)):
return context
return 'UNKNOW'
def intent(entry, check_hotword=True) -> bool:
"""
Check word and try match a command
"""
global LAST_EXECUTED_COMMAND, WAIT_ENTIRE_INPUT
command = entry.strip()
# check and filter hotword
if check_hotword:
filter_sentence = match_and_filter_hotword(command)
if not filter_sentence:
return False
command = filter_sentence
logger.info("Hotword detected")
# special reload words
if command in Intents.reload_words:
logger.info("Reloading 'settings' module.")
reload_intents()
return True
# find command
cmd_info = find_match_command(command)
if not cmd_info:
logger.warning(f"Command '{command}' not found")
return False
logger.info(f"Command {command} found - content: {cmd_info}")
# set lazy status (wait a resolutive input, for ex when the user is writting something)
if cmd_info.get('wait'):
WAIT_ENTIRE_INPUT = True
else:
WAIT_ENTIRE_INPUT = False
# check context
if cmd_context := cmd_info.get('context'):
actual_context = get_context()
if actual_context != cmd_context:
logger.error(f"Wrong context: { cmd_context }")
logger.error(f" Actual is: { actual_context }")
return False
# supported functions
functions = {
'run_process': utils.run_process,
'press': utils.press_keys,
'ctrl_num': utils.ctrl_num,
'write': utils.press_write
}
for function_name, function in functions.items():
if to_execute := cmd_info.get(function_name):
# check args
if arg_value := cmd_info.get("args"):
# arg is present into instance
if to_execute == '{}':
function(arg_value)
# arg is extract from dict
else:
function(to_execute, arg_value)
else:
# if the function has a value ({ run_process: 'chrome.exe' })
function(to_execute)
LAST_EXECUTED_COMMAND = entry
if to_speech := cmd_info.get('voice') and Settings.voice:
speech(to_speech)
return True
def repeat_last_command():
logger.info(f"Repeating last command { LAST_EXECUTED_COMMAND }")
return intent(LAST_EXECUTED_COMMAND)
if __name__ == '__main__':
execute = intent("ctrl dos", check_hotword=False)