1
0
mirror of https://github.com/MarkParker5/STARK.git synced 2025-07-12 22:50:22 +02:00

Partially new flow. Remove Callback, create CommandsManager, create Core

This commit is contained in:
MarkParker5
2021-10-24 22:26:09 +03:00
parent a44580b2dc
commit 7a2cfaa59b
42 changed files with 287 additions and 390 deletions

2
.gitignore vendored
View File

@ -14,3 +14,5 @@ __pycache__
/logs.txt /logs.txt
audio/ audio/
.idea

View File

@ -0,0 +1,34 @@
from typing import Callable
from abc import ABC
from .RThread import RThread, Event
from .CommandsManager import CommandsManager
from .. import Pattern
class Command(ABC):
name: str
patterns: list[Pattern]
start: Callable
def __init__(self, name, keywords = {}, patterns = []): # initialisation of new command
self._name = name #TODO: change name to path
self._patterns = patterns
CommandsManager().append(self)
def setStart(self, function): # define start (required)
self.start = function
@property
def name(self):
return self._name
@property
def patterns(self):
return self._patterns
@classmethod
def new(cls, *args, **kwargs):
def creator(func):
cmd: Command = cls(*args, **kwargs)
cmd.setStart(func)
return func
return creator

View File

@ -0,0 +1,60 @@
from typing import Type
from . import Command
from ..Pattern import ACObject
class SearchResult:
command: Command
parameters: dict[str, ACObject]
def __init__(self, command: Command, parameters: dict[str, ACObject] = {}):
self.command = command
self.parameters = parameters
class CommandsManager:
allCommands: list[Command] = []
def __new__(cls): # Singleton
if not hasattr(cls, 'instance'):
cls.instance = super().__new__(cls)
return cls.instance
def search(self, string) -> list[SearchResult]: # find command by pattern
string = string.lower()
results: list[SearchResult] = []
acstring = ACString(string)
# find command obj by pattern
for command in self.allCommands:
for pattern in command.patterns:
if groupdict := pattern.match(string):
parameters: dict[str: ACObject] = {'string': acstring,}
for key, value in groupdict.items():
name, typeName = key.split(':')
ACType: Type[ACObject] = CommandsManager.classFromString(typeName)
parameters[name] = ACType(value)
results.append(SearchResult(command, parameters))
if results: return results
else: return [SearchResult(Command.QA, {'string': acstring,}),]
def append(self, obj): # add new command to list
self.allCommands.append(obj)
def getCommand(name): # TODO: quick search
for command in self.allCommands:
if command.name == name: return command
@staticmethod
def classFromString(className: str) -> ACObject:
return getattr(sys.modules[__name__], className)
@staticmethod
def background(answer = '', voice = ''): # make background cmd
def decorator(func):
def wrapper(text):
finishEvent = Event()
thread = RThread(target=func, args=(text, finish_event))
thread.start()
return Response(voice = voice, text = answer, thread = ThreadData(thread, finishEvent) )
return wrapper
return decorator

View File

@ -0,0 +1,15 @@
from typing import Optional
from .Command import Command
from .ThreadData import ThreadData
class Response:
voice: str
text: str
callback: Optional[Command]
thread: Optional[ThreadData]
def __init__(self, voice, text, callback = None, thread = None):
self.voice = voice
self.text = text
self.callback = callback
self.thread = thread

View File

@ -0,0 +1,9 @@
from .RThread import RThread, Event
class ThreadData:
thread: RThread
finishEvent: Event
def __init__(thread: RThread, finishEvent: Event):
self.thread = thread
self.finishEvent = finishEvent

View File

@ -0,0 +1,3 @@
from .Command import *
from .Response import *
from .CommandsManager import CommandsManager, SearchResult

View File

@ -0,0 +1,16 @@
from abc import ABC, abstractmethod
from typing import Any
from .. import Pattern
class classproperty(property):
def __get__(self, cls, owner):
return classmethod(self.fget).__get__(None, owner)()
class ACObject(ABC):
pattern: Pattern # static getonly
value: Any
@classproperty
@abstractmethod
def pattern() -> Pattern:
return Pattern('')

View File

@ -0,0 +1 @@
from .ACObject import ACObject

View File

@ -0,0 +1,36 @@
from typing import Type, Optional
import re
from .expressions import expressions
from .ACObjects import *
class Pattern:
origin: str
compiled: str #getonly
def __init__(self, origin: str):
self.origin = origin
@property
def compiled(self) -> str: # transform pattern to classic regex with named groups
pattern: str = self.origin
# transform patterns to regexp
for ptrn, regex in expressions.items():
pattern = re.sub(re.compile(ptrn), regex, pattern)
# find and transform arguments like $name:Type
argumentRegex = re.compile(r'\$[:word:]:[:word:]')
while match := re.search(argumentRegex, pattern)[0]:
arg: str = match[1:]
argName, argTypeName = arg.split(':')
argType: Type[ACObject] = classFromString(argTypeName)
pattern = re.sub('\\'+link[0], f'(?P<{arg}>{argType.pattern.compiled})', pattern)
return re.compile(pattern)
def match(self, string: str) -> Optional[dict[str, str]]:
if match := re.search(self.compiled, string):
return match.groupdict()
return None

View File

@ -0,0 +1,2 @@
from .Pattern import Pattern
from .ACObjects import *

View File

@ -0,0 +1,13 @@
expressions = {
# stars *
r'([A-Za-zА-ЯЁа-яё0-9\(\)\[\]\{\}]+)\*([A-Za-zА-ЯЁа-яё0-9\(\)\[\]\{\}]+)': r'\\b\1.*\2\\b', # 'te*xt'
r'\*([A-Za-zА-ЯЁа-яё0-9\(\)\[\]\{\}]+)': r'\\b.*\1', # '*text'
r'([A-Za-zА-ЯЁа-яё0-9\(\)\[\]\{\}]+)\*': r'\1.*\\b', # 'text*'
r'(^|\s)\*($|\s)': r'.*', # '*' ' * '
# one of the list (a|b|c)
r'\(((?:.*\|)*.*)\)': r'(?:\1)',
# 0 or 1 the of list [abc]
r'\[((?:.*\|?)*?.*?)\]': r'(?:\1)??',
# one or more of the list, without order {a|b|c}
r'\{((?:.*\|?)*?.*?)\}': r'(?:\1)+?',
}

2
ArchieCore/__init__.py Normal file
View File

@ -0,0 +1,2 @@
from .Pattern import *
from .Command import *

View File

@ -2,8 +2,8 @@ import time
import requests import requests
import json as JSON import json as JSON
from Controls.Control import Control from ..Control import Control
from Features.Command import Command from ArchieCore import Command
from Features import * from Features import *
class RemoteControl(Control): class RemoteControl(Control):

View File

@ -3,9 +3,9 @@
import time import time
import os import os
import config import config
from Features import Command from ArchieCore import Command
from General import Text2Speech from General import Text2Speech
from Controls.Control import Control from ..Control import Control
from .MyTeleBot import MyTeleBot from .MyTeleBot import MyTeleBot
class TelegramBot(Control): class TelegramBot(Control):

View File

@ -1,8 +1,8 @@
#!/usr/local/bin/python3.8 #!/usr/local/bin/python3.8
import os import os
from Controls.Control import Control from ..Control import Control
from General import SpeechRecognition, Text2Speech from General import SpeechRecognition, Text2Speech
from Features.Command import Command from ArchieCore import CommandsManager
import config import config
class VoiceAssistant(Control): class VoiceAssistant(Control):
@ -29,6 +29,7 @@ class VoiceAssistant(Control):
print('\nYou: ', end='') print('\nYou: ', end='')
speech = self.listener.listen() speech = self.listener.listen()
print(speech.get('text') or '', end='') print(speech.get('text') or '', end='')
while True: while True:
if speech['status'] == 'error': if speech['status'] == 'error':
break break
@ -36,16 +37,19 @@ class VoiceAssistant(Control):
self.voids += 1 self.voids += 1
break break
text = speech['text'] text = speech['text']
cmd, params = Command.reg_find(text).values()
try: response = cmd.start(params) for result in CommandsManager().search(text):
except: break try: response = result.command.start(result.parameters)
self.reply(response) except: break
self.check_threads()
self.report() self.reply(response)
if response.callback: self.check_threads()
speech = recognize(response.callback, {}) self.report()
else:
break if response.callback:
speech = recognize(response.callback, {})
else:
break
def recognize(self, callback, params): def recognize(self, callback, params):
print('\nYou: ', end='') print('\nYou: ', end='')
@ -54,6 +58,7 @@ class VoiceAssistant(Control):
return speech return speech
text = speech['text'] text = speech['text']
print(text, end='') print(text, end='')
while True: while True:
self.check_threads() self.check_threads()
if not callback: break if not callback: break

View File

@ -1,32 +0,0 @@
from .Command import Command
from .Response import Response
import re
class Callback:
def __init__(self, patterns, quiet = False, once = True):
self.patterns = patterns
self.quiet = quiet
self.once = once
def setStart(self, function):
self.start = function
def start(self, params):
pass
def answer(self, string):
for pattern in self.patterns:
if match := re.search(re.compile(Command.compilePattern(pattern)), string):
return self.start({**match.groupdict(), 'string':string})
return None
@staticmethod
def background(answer = '', voice = ''):
def decorator(cmd):
def wrapper(text):
finish_event = Event()
thread = RThread(target=cmd, args=(text, finish_event))
thread.start()
return Response(voice = voice, text = answer, thread = {'thread': thread, 'finish_event': finish_event} )
return wrapper
return decorator

View File

@ -1,245 +0,0 @@
# Abstract class Command
#
# Command - parent of all command classes
# command - object (class instance)
# Command.list - list of all commands
# Command.find() - recognize command from a string by fuzzywuzzy, return command object
# must return dict like {'cmd': cmd, 'params': params}
# Command.reg_find() - recognize command from a string with regexe patterns, return command object
# must return dict like {'cmd': cmd, 'params': params}
# self - object (class instance) pointer (self)
# abstract self.start() - required method for all commands
# self.keywords - dictionary of arrays keywords
# like {
# (int)weight : ['word1', 'word2', 'word3'],
# (int)weight1 : ['word3', 'word4'],
# (int)weight2 : ['word5', 'word6', 'word7', 'word8', 'word9'],
# }
# self.patterns - list of command patterns
# self.subpatterns - list of subpaterns (context patterns)
# like ['* который * час *', '* скольк* * (врем|час)* *']
# Command._entities - linked patterns $Pattern
# Command.regex - regex patterns dict for better syntax
#
#
#
from abc import ABC, abstractmethod
import re
from .RThread import RThread, Event
from .synonyms import synonyms
class Command(ABC):
_list = [] # list of all commands
_entities = {
'word': lambda: r'\b[A-Za-zА-ЯЁа-яё0-9\-]+\b',
'text': lambda: r'[A-Za-zА-ЯЁа-яё0-9\- ]+',
'num': lambda: r'[0-9]+',
'quest' : lambda: Command.compilePattern('(кто|что|как|какой|какая|какое|где|зачем|почему|сколько|чей|куда|когда)'),
'repeat': lambda: Command.compilePattern('* ((повтор*)|(еще раз)|(еще*раз)*) *'),
'true': lambda: Command.compilePattern('('+'|'.join(synonyms.get('да'))+')'),
'false': lambda: Command.compilePattern('('+'|'.join(synonyms.get('нет'))+')'),
'bool': lambda: Command.compilePattern('($true|$false)')
}
_regex = {
# stars *
'([A-Za-zА-ЯЁа-яё0-9\(\)\[\]\{\}]+)\*([A-Za-zА-ЯЁа-яё0-9\(\)\[\]\{\}]+)': r'\\b\1.*\2\\b', # 'te*xt'
'\*([A-Za-zА-ЯЁа-яё0-9\(\)\[\]\{\}]+)': r'\\b.*\1', # '*text'
'([A-Za-zА-ЯЁа-яё0-9\(\)\[\]\{\}]+)\*': r'\1.*\\b', # 'text*'
'(^|\s)\*($|\s)': r'.*', # '*' ' * '
# one of the list (a|b|c)
'\(((?:.*\|)*.*)\)': r'(?:\1)',
# 0 or 1 the of list [abc]
'\[((?:.*\|?)*?.*?)\]': r'(?:\1)??',
# one or more of the list, without order {a|b|c}
'\{((?:.*\|?)*?.*?)\}': r'(?:\1)+?',
}
def __init__(self, name, keywords = {}, patterns = [], subPatterns = []): # initialisation of new command
self._name = name
self._keywords = keywords
self._patterns = patterns
self._subPatterns = subPatterns
Command.append(self)
def __str__(self):
str = f'{self.__class__.__name__}.{self.getName()}:\n'
for key, value in self._keywords.items():
str += f'\t{key}:\t{value}\n'
return str
######################################################################################
# CONTROL KEYWORDS #
######################################################################################
def getKeyword(self, string): # return position of the keyword
for weight, array in self._keywords.items():
index = 0
for word in array:
if string == word:
return (weight, index)
index += 1
return None # if not found
def removeKeyword(self, string):
position = self.getKeyword(string)
if(position): del self._keywords[ position[0] ][ position[1] ]
def addKeyword(self, weight, string): # add new keywords to end of the list
if self.getKeyword(string): return
if( self._keywords.get(weight) ): self._keywords[weight].append(string)
else: self._keywords[weight] = [string]
def changeKeyword(self, weight, name): # set new weight to keyword (find by name)
self.removeKeyword(name)
self.addKeyword(weight, name)
def checkContext(self, string): # return cmd if the string matches the cmd context
for pattern in self.getSubPatterns():
if match := re.search(re.compile(Command.compilePattern(pattern)), string):
return {
'cmd': self,
'params': {**match.groupdict(), 'string':string}, # return parans+initial text
}
raise Exception("Not Found") # raise exception if context not found
######################################################################################
# SETTERS #
######################################################################################
def setStart(self, function): # define start (required)
self.start = function
######################################################################################
# GETTERS #
######################################################################################
def getName(self):
return self._name
def getKeywords(self):
return self._keywords
def getPatterns(self):
return self._patterns
def getSubPatterns(self):
return self._subPatterns
######################################################################################
# STATIC METHODS #
######################################################################################
@staticmethod
def getList():
return Command._list # all commands
@staticmethod
def getRegexDict():
return Command._regex # all standart patterns
@staticmethod
def getEntity(key):
entity = Command._entities.get(key) # all linked $Pattern s
return entity()
@staticmethod
def append(obj): # add new command to list
Command._list.append(obj)
@staticmethod
def getCommand(name): # get command by name
for obj in Command.getList():
if obj.getName() == name: return obj
@staticmethod
def isRepeat(string): # return True if command is repeat-cmd
if re.search(re.compile(Command.getEntity('repeat')), string): return True
return False
@staticmethod
def ratio(string, word): # get average distance of string and pattern
return ( fuzz.WRatio(string, word) + fuzz.ratio(string, word) ) / 2
@staticmethod
def compilePattern(pattern): # transform my syntax to standart regexp
# transform patterns to regexp
for ptrn, regex in Command.getRegexDict().items():
pattern = re.sub(re.compile(ptrn), regex, pattern)
# find and replace links like $Pattern
while link := re.search(re.compile('\$[a-z]+'), pattern):
pattern = re.sub('\\'+link[0], f'(?P<{link[0][1:]}>{Command.getEntity( link[0][1:] )})', pattern)
# return compiled regexp
return pattern
'''
@staticmethod
def find(string): # find command by fuzzywuzzy
string = string.lower()
chances = {}
list = Command.getList()
# calculate chances of every command
for i, obj in enumerate( list ):
chances[i] = 0
k = 1 / ( sum( [int(w)*len(kw) for w, kw in obj.getKeywords().items()] ) or 1 )
for weight, kws in obj.getKeywords().items():
for kw in kws:
chances[i] += Command.ratio(string, kw) * weight * k
# find command with the biggest chance
if( sum( chances.values() ) ):
top = max( chances.values() ) / sum( chances.values() ) * 100
else: # if all chances is 0
return {
'cmd': Command.QA,
'params': {'string':string,},
}
#if( max( chances.values() ) < 800 or top < 50): return list[0]
# find top command obj
for i, chance in chances.items():
if chance == max( chances.values() ):
return {
'cmd': Command.QA, #dialog mode
'params': {'string':string,},
}
'''
@staticmethod
def reg_find(string): # find comman by pattern
string = string.lower()
list = Command.getList()
if not string: return {
'cmd': Command.getCommand('Hello'),
'params': {'string':string,},
}
# find command obj by pattern
for obj in list:
for pattern in obj.getPatterns():
if match := re.search(re.compile(Command.compilePattern(pattern)), string):
if params := match.groupdict():
if params.get('true'): params['bool'] = True
if params.get('false'): params['bool'] = False
return {
'cmd': obj,
'params': {**params, 'string':string,},
}
# return Question-Answer system if command not found
return {
'cmd': Command.QA,
'params': {'string':string,},
}
@staticmethod #TODO: pass Response instead of answer and voice; create ThreadData struct
def background(answer = '', voice = ''): # make background cmd
def decorator(func): #wrapper of wrapper (decorator of decorator)
def wrapper(text):
finish_event = Event()
thread = RThread(target=func, args=(text, finish_event))
thread.start()
return Response(voice = voice, text = answer, thread = {'thread': thread, 'finish_event': finish_event} )
return wrapper
return decorator
@classmethod
def new(cls, *args, **kwargs):
def creator(func):
cmd: Command = cls(*args, **kwargs)
cmd.setStart(func)
return func
return creator

View File

@ -1,6 +0,0 @@
class Response:
def __init__(self, voice, text, callback = None, thread = None):
self.voice: str = voice
self.text: str = text
self.callback: Callback = callback
self.thread = thread

View File

@ -1,3 +0,0 @@
from .Command import *
from .Callback import *
from .Response import *

View File

@ -1,4 +1,4 @@
from ..Command import Command from ArchieCore import Command
class Media(Command): class Media(Command):
def start(self, string): # main method def start(self, string): # main method

View File

@ -2,7 +2,7 @@ from .Media import *
import requests import requests
from bs4 import BeautifulSoup as BS from bs4 import BeautifulSoup as BS
import os import os
from ..Command import Callback, Response from ArchieCore import Response
################################################################################ ################################################################################
def findPage(name): def findPage(name):
query = name + ' site:kinogo.by' query = name + ' site:kinogo.by'
@ -128,7 +128,7 @@ kinogo_film_cb = Callback(['$text',])
kinogo_film_cb.setStart(start_film) kinogo_film_cb.setStart(start_film)
patterns = ['* включ* фильм $text', '* включ* фильм*'] patterns = ['* включ* фильм $text', '* включ* фильм*']
kinogo_film = Media('KinogoFilm', {}, patterns) kinogo_film = Media('KinogoFilm', patterns)
kinogo_film.setStart(film) kinogo_film.setStart(film)
@ -136,5 +136,5 @@ kinogo_serial_cb = Callback(['$text',])
kinogo_serial_cb.setStart(start_serial) kinogo_serial_cb.setStart(start_serial)
patterns = ['* включ* сериал $text', '* включ* сериал*'] patterns = ['* включ* сериал $text', '* включ* сериал*']
kinogo_serial = Media('KinogoSerial', {}, patterns) kinogo_serial = Media('KinogoSerial', patterns)
kinogo_serial.setStart(serial) kinogo_serial.setStart(serial)

View File

@ -1,5 +1,5 @@
from bs4 import BeautifulSoup as BS from bs4 import BeautifulSoup as BS
from ..Command import Command, Response from ArchieCore import Command, Response
import wikipedia as wiki import wikipedia as wiki
import requests import requests
import random import random
@ -71,4 +71,4 @@ class QA(Command):
voice = text = search or random.choice(['Не совсем понимаю, о чём вы.', 'Вот эта последняя фраза мне не ясна.', 'А вот это не совсем понятно.', 'Можете сказать то же самое другими словами?', 'Вот сейчас я совсем вас не понимаю.', 'Попробуйте выразить свою мысль по-другому',]) voice = text = search or random.choice(['Не совсем понимаю, о чём вы.', 'Вот эта последняя фраза мне не ясна.', 'А вот это не совсем понятно.', 'Можете сказать то же самое другими словами?', 'Вот сейчас я совсем вас не понимаю.', 'Попробуйте выразить свою мысль по-другому',])
return Response(text = text, voice = voice) return Response(text = text, voice = voice)
Command.QA = QA('QA', {}, []) Command.QA = QA('QA', [])

View File

@ -1,4 +1,4 @@
from ..Command import Command # import parent class from ArchieCore import Command # import parent class
import os import os
class Raspi(Command): class Raspi(Command):

View File

@ -1,6 +1,6 @@
from .Raspi import * from .Raspi import *
import os import os
from ..Command import Callback, Response from ArchieCore import CommandsManager, Callback, Response
import config import config
################################################################################ ################################################################################
def reboot(params): def reboot(params):
@ -11,7 +11,7 @@ def reboot(params):
reboot_cb = Callback(['$bool',]) reboot_cb = Callback(['$bool',])
reboot_cb.setStart(reboot) reboot_cb.setStart(reboot)
@Raspi.background(answer = 'Проверяю обновления...', voice = 'Проверяю обновления') @CommandsManager.background(answer = 'Проверяю обновления...', voice = 'Проверяю обновления')
def method(params, finish_event): def method(params, finish_event):
os.system('git -C '+config.path+' remote update') os.system('git -C '+config.path+' remote update')
if not 'git pull' in os.popen('git -C '+config.path+' status -uno').read(): if not 'git pull' in os.popen('git -C '+config.path+' status -uno').read():
@ -24,5 +24,5 @@ def method(params, finish_event):
return Response(text = text, voice = voice, callback = reboot_cb) return Response(text = text, voice = voice, callback = reboot_cb)
patterns = ['* обновись *', '* можешь обновиться *', '* обнови себя *', '* скачай обновлени* *', '* провер* обновлени* *'] patterns = ['* обновись *', '* можешь обновиться *', '* обнови себя *', '* скачай обновлени* *', '* провер* обновлени* *']
gitpull = Raspi('git pull archie.git', [], patterns) gitpull = Raspi('git pull archie.git', patterns)
gitpull.setStart(method) gitpull.setStart(method)

View File

@ -1,5 +1,5 @@
from .Raspi import * from .Raspi import *
from ..Command import Response from ArchieCore import Response
################################################################################ ################################################################################
def method(params): def method(params):
Raspi.hdmi_cec('on 0') Raspi.hdmi_cec('on 0')
@ -7,9 +7,8 @@ def method(params):
voice = text = '' voice = text = ''
return Response(text = text, voice = voice) return Response(text = text, voice = voice)
keywords = {}
patterns = ['* включи* (телевизор|экран) *'] patterns = ['* включи* (телевизор|экран) *']
tv_on = Raspi('tv on', keywords, patterns) tv_on = Raspi('tv on', patterns)
tv_on.setStart(method) tv_on.setStart(method)
################################################################################ ################################################################################
def method(params): def method(params):
@ -17,9 +16,8 @@ def method(params):
voice = text = '' voice = text = ''
return Response(text = text, voice = voice) return Response(text = text, voice = voice)
keywords = {}
patterns = ['* (выключи|отключи)* (телевизор|экран) *'] patterns = ['* (выключи|отключи)* (телевизор|экран) *']
tv_off = Raspi('tv off', keywords, patterns) tv_off = Raspi('tv off', patterns)
tv_off.setStart(method) tv_off.setStart(method)
################################################################################ ################################################################################
def method(params): def method(params):
@ -28,9 +26,8 @@ def method(params):
voice = text = '' voice = text = ''
return Response(text = text, voice = voice) return Response(text = text, voice = voice)
keywords = {}
patterns = ['* (выведи|вывести|покажи|открой|показать|открыть) * с (провода|hdmi|кабеля|порта) * $num *'] patterns = ['* (выведи|вывести|покажи|открой|показать|открыть) * с (провода|hdmi|кабеля|порта) * $num *']
tv_hdmi = Raspi('tv hdmi source', keywords, patterns) tv_hdmi = Raspi('tv hdmi source', patterns)
tv_hdmi.setStart(method) tv_hdmi.setStart(method)
################################################################################ ################################################################################
def method(params): def method(params):
@ -38,9 +35,8 @@ def method(params):
voice = text = '' voice = text = ''
return Response(text = text, voice = voice) return Response(text = text, voice = voice)
keywords = {}
patterns = ['* (выведи|вывести|покажи|открой|показать|открыть) * с (ноута|ноутбука|планшета|провода|hdmi)'] patterns = ['* (выведи|вывести|покажи|открой|показать|открыть) * с (ноута|ноутбука|планшета|провода|hdmi)']
tv_hdmi = Raspi('tv hdmi source', keywords, patterns) tv_hdmi = Raspi('tv hdmi source', patterns)
tv_hdmi.setStart(method) tv_hdmi.setStart(method)
################################################################################ ################################################################################
def method(params): def method(params):
@ -48,7 +44,6 @@ def method(params):
voice = text = '' voice = text = ''
return Response(text = text, voice = voice) return Response(text = text, voice = voice)
keywords = {}
patterns = ['* (верни|вернуть|включи*|покажи|показать) [основн|нормальн|стандартн|привычн]* (телевизор|экран|картинк|изображение) *'] patterns = ['* (верни|вернуть|включи*|покажи|показать) [основн|нормальн|стандартн|привычн]* (телевизор|экран|картинк|изображение) *']
tv_rpi = Raspi('tv rpi source', keywords, patterns) tv_rpi = Raspi('tv rpi source', patterns)
tv_rpi.setStart(method) tv_rpi.setStart(method)

View File

@ -3,7 +3,7 @@
# Module for speaking with voice assistent # Module for speaking with voice assistent
# See class Command # See class Command
from ..Command import Command # import parent class from ArchieCore import Command # import parent class
class SmallTalk(Command): class SmallTalk(Command):
def start(self, string): # main method def start(self, string): # main method

View File

@ -3,7 +3,7 @@ import requests
from bs4 import BeautifulSoup as BS from bs4 import BeautifulSoup as BS
import math import math
from .SmallTalk import SmallTalk from .SmallTalk import SmallTalk
from ..Command import Response from ArchieCore import Response
################################################################################ ################################################################################
def method(params): def method(params):
if city := params.get('text'): if city := params.get('text'):
@ -88,12 +88,12 @@ def method(params):
return Response(text = text, voice = voice) return Response(text = text, voice = voice)
keywords = { # keywords = {
10: ['который час', 'сколько времени'], # 10: ['который час', 'сколько времени'],
5: ['текущее', 'сейчас', 'время'], # 5: ['текущее', 'сейчас', 'время'],
1: ['сколько'] # 1: ['сколько']
} # }
patterns = ['* который * час в $text', '* скольк* * (врем|час)* * в $text', '* время в $text', '* который * час *', '* скольк* * (врем|час)* *'] patterns = ['* который * час в $text', '* скольк* * (врем|час)* * в $text', '* время в $text', '* который * час *', '* скольк* * (врем|час)* *']
subpatterns = ['а (сейчас|в $text)'] # subpatterns = ['а (сейчас|в $text)']
ctime = SmallTalk('Current Time', keywords, patterns, subpatterns) ctime = SmallTalk('Current Time', patterns)
ctime.setStart(method) ctime.setStart(method)

View File

@ -1,5 +1,5 @@
from .SmallTalk import * from .SmallTalk import *
from ..Command import Response from ArchieCore import Response
################################################################################ ################################################################################
@SmallTalk.new('Hello', patterns = ['* привет* *',]) @SmallTalk.new('Hello', patterns = ['* привет* *',])

View File

@ -5,7 +5,7 @@ import spidev
import time import time
import json as JSON import json as JSON
from threading import Thread from threading import Thread
from ..Command import Command from ArchieCore import Command
GPIO.setmode(GPIO.BCM) GPIO.setmode(GPIO.BCM)

View File

@ -1,5 +1,5 @@
from .SmartHome import * from .SmartHome import *
from ..Command import Response, Command from ArchieCore import Response, Command
import Text2Speech import Text2Speech
import os import os
################################################################################ ################################################################################
@ -19,7 +19,6 @@ def method(params):
Text2Speech.Engine().generate(voice).speak() Text2Speech.Engine().generate(voice).speak()
return Response(text = text, voice = voice) return Response(text = text, voice = voice)
keywords = {}
patterns = [] patterns = []
alarmclock = SmartHome('alarmclock', keywords, patterns) alarmclock = SmartHome('alarmclock', patterns)
alarmclock.setStart(method) alarmclock.setStart(method)

View File

@ -1,5 +1,5 @@
from .SmartHome import * from .SmartHome import *
from ..Command import Response from ArchieCore import Response
################################################################################ ################################################################################
def method(params): def method(params):
@ -10,9 +10,8 @@ def method(params):
voice = text = '' voice = text = ''
return Response(text = text, voice = voice) return Response(text = text, voice = voice)
keywords = {}
patterns = ['* (включ|выключ)* свет *'] patterns = ['* (включ|выключ)* свет *']
main_light = SmartHome('main_light', keywords, patterns) main_light = SmartHome('main_light', patterns)
main_light.setStart(method) main_light.setStart(method)
################################################################################ ################################################################################
@ -26,9 +25,8 @@ def method(params):
voice = text = '' voice = text = ''
return Response(text = text, voice = voice) return Response(text = text, voice = voice)
keywords = {}
patterns = ['* включи* подсветку *'] patterns = ['* включи* подсветку *']
light_on = SmartHome('led_on', keywords, patterns) light_on = SmartHome('led_on', patterns)
light_on.setStart(method) light_on.setStart(method)
################################################################################ ################################################################################
@ -41,9 +39,8 @@ def method(params):
voice = text = '' voice = text = ''
return Response(text = text, voice = voice) return Response(text = text, voice = voice)
keywords = {}
patterns = ['* выключи* подсветку *'] patterns = ['* выключи* подсветку *']
led_off = SmartHome('led_off', keywords, patterns) led_off = SmartHome('led_off', patterns)
led_off.setStart(method) led_off.setStart(method)
################################################################################ ################################################################################
@ -56,9 +53,8 @@ def method(params):
voice = text = '' voice = text = ''
return Response(text = text, voice = voice) return Response(text = text, voice = voice)
keywords = {}
patterns = [] patterns = []
led_hello = SmartHome('led_hello', keywords, patterns) led_hello = SmartHome('led_hello', patterns)
led_hello.setStart(method) led_hello.setStart(method)
################################################################################ ################################################################################

View File

@ -1,5 +1,5 @@
from .SmartHome import * from .SmartHome import *
from ..Command import Response from ArchieCore import Response
################################################################################ ################################################################################
def method(params): def method(params):
@ -10,7 +10,6 @@ def method(params):
voice = text = '' voice = text = ''
return Response(text = text, voice = voice) return Response(text = text, voice = voice)
keywords = {}
patterns = ['* (включ|выключ)* свет *'] patterns = ['* (включ|выключ)* свет *']
main_light = SmartHome('main_light', keywords, patterns) main_light = SmartHome('main_light', patterns)
main_light.setStart(method) main_light.setStart(method)

View File

@ -10,9 +10,8 @@ def method(params):
voice = text = '' voice = text = ''
return Response(text = text, voice = voice) return Response(text = text, voice = voice)
keywords = {}
patterns = ['* (открыть|открой) (окно|окна) *', '* (подними|поднять) (шторы|роллеты) *'] patterns = ['* (открыть|открой) (окно|окна) *', '* (подними|поднять) (шторы|роллеты) *']
window_open = SmartHome('window_open', keywords, patterns) window_open = SmartHome('window_open', patterns)
window_open.setStart(method) window_open.setStart(method)
################################################################################ ################################################################################
@ -25,7 +24,6 @@ def method(params):
voice = text = '' voice = text = ''
return Response(text = text, voice = voice) return Response(text = text, voice = voice)
keywords = {}
patterns = ['* (закрыть|закрой) (окно|окна) *', '* (опусти|опустить) (шторы|роллеты) *'] patterns = ['* (закрыть|закрой) (окно|окна) *', '* (опусти|опустить) (шторы|роллеты) *']
window_close = SmartHome('window_close', keywords, patterns) window_close = SmartHome('window_close', patterns)
window_close.setStart(method) window_close.setStart(method)

View File

@ -1,4 +1,4 @@
from ..Command import Command, Response # import parent class from ArchieCore import Command, Response # import parent class
import urllib.request import urllib.request
import xlrd import xlrd
import xlwt import xlwt

View File

@ -33,9 +33,8 @@ def formatDay(lessons):
def nextLessonFunc(params): def nextLessonFunc(params):
return formatLesson(Zieit.getNextLesson(Zieit.lessonsStartTime)) return formatLesson(Zieit.getNextLesson(Zieit.lessonsStartTime))
keywords = {}
patterns = ['* следующ* (предмет|урок|пара)'] patterns = ['* следующ* (предмет|урок|пара)']
nextLesson = Zieit('Next Lesson', keywords, patterns) nextLesson = Zieit('Next Lesson', patterns)
nextLesson.setStart(nextLessonFunc) nextLesson.setStart(nextLessonFunc)
################################################################################ ################################################################################
@ -43,9 +42,8 @@ nextLesson.setStart(nextLessonFunc)
def currentLessonFunc(params): def currentLessonFunc(params):
return formatLesson(Zieit.getNextLesson(Zieit.lessonsEndTime)) return formatLesson(Zieit.getNextLesson(Zieit.lessonsEndTime))
keywords = {}
patterns = ['* (текущ*|сейчас) (предмет|урок|пара)'] patterns = ['* (текущ*|сейчас) (предмет|урок|пара)']
currentLesson = Zieit('Current Lesson', keywords, patterns) currentLesson = Zieit('Current Lesson', patterns)
currentLesson.setStart(currentLessonFunc) currentLesson.setStart(currentLessonFunc)
################################################################################ ################################################################################
@ -57,10 +55,8 @@ def todaysSheduleFunc(params):
text = voice = 'Сегодня пар нет' text = voice = 'Сегодня пар нет'
return Response(text = text, voice = voice) return Response(text = text, voice = voice)
keywords = {}
patterns = ['* сегодня (предметы|уроки|пары|расписание)', '* (предметы|уроки|пары|расписание) * сегодня *'] patterns = ['* сегодня (предметы|уроки|пары|расписание)', '* (предметы|уроки|пары|расписание) * сегодня *']
todaysShedule = Zieit('Todays Shedule', keywords, patterns) todaysShedule = Zieit('Todays Shedule', patterns)
todaysShedule.setStart(todaysSheduleFunc) todaysShedule.setStart(todaysSheduleFunc)
################################################################################ ################################################################################
@ -72,7 +68,6 @@ def tomorrowsSheduleFunc(params):
text = voice = 'Завтра пар нет' text = voice = 'Завтра пар нет'
return Response(text = text, voice = voice) return Response(text = text, voice = voice)
keywords = {}
patterns = ['* завтра (предметы|уроки|пары|расписание)', '* (предметы|уроки|пары|расписание) * завтра *'] patterns = ['* завтра (предметы|уроки|пары|расписание)', '* (предметы|уроки|пары|расписание) * завтра *']
tomorrowsShedule = Zieit('Todays Shedule', keywords, patterns) tomorrowsShedule = Zieit('Todays Shedule', patterns)
tomorrowsShedule.setStart(tomorrowsSheduleFunc) tomorrowsShedule.setStart(tomorrowsSheduleFunc)

View File

@ -1,6 +1,3 @@
from .Command import Command
from .Command import Response
from .Media import Media from .Media import Media
from .QA.QA import QA from .QA.QA import QA
from .SmallTalk import SmallTalk from .SmallTalk import SmallTalk
@ -8,4 +5,4 @@ from .Raspi import Raspi
from .Zieit import Zieit from .Zieit import Zieit
try: from .SmartHome import SmartHome try: from .SmartHome import SmartHome
except: print('cannot import module named "SmartHome" from Features/Smarthome\n') except: pass

View File

@ -1,5 +1,6 @@
from google.cloud import texttospeech from google.cloud import texttospeech
import os import os
os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = 'hide'
from pygame import mixer from pygame import mixer
from time import sleep from time import sleep
import mmap import mmap

View File

@ -1,2 +1,27 @@
# A.R.C.H.I.E. # A.R.C.H.I.E.
Voice assistant, smart home hub, media center and smart tv ##Voice assistant, smart home hub, media center and smart tv
###Project structure:
- ####ArchieCore - Core, base classes
- Command
- CommandsManager
- SearchResult
- Response
- ThreadData
- Pattern
- ACObject and subclasses
- ####Controls - Responsible for user interaction
- Control(ABC)
- VoiceAssistant
- TelegramBot
- RemoteControl
- Django
- ####Features - Possibilities, set of functions
- ####General - For helper classes
- ####Raspberry - Control system and hardware
###Root files:
- **start.py** - entry point
- **config.example.py** - file for settings. Copy as config.py and type own paraneters
- **helper.py** - use in terminal for creating new modules, commands, features, controls, etc.
- **dependences.txt** - list of all dependences (Python version and libraries)

View File

@ -1,3 +1,4 @@
Python 3.10
pip install speech_recognition pip install speech_recognition
pip install google-cloud-texttospeech pip install google-cloud-texttospeech
pip install pygame pip install pygame
@ -7,4 +8,4 @@ pip install xlrd
pip install xlwt pip install xlwt
pip install xlutils pip install xlutils
pip install pyaudio # if instalation fails, try install from .whl (https://www.lfd.uci.edu/~gohlke/pythonlibs/#pyaudio) pip install pyaudio # if instalation fails, try install from .whl (https://www.lfd.uci.edu/~gohlke/pythonlibs/#pyaudio)
pip install pip install PyTelegramBotApi pip install pip install PyTelegramBotApi

View File

@ -1,21 +0,0 @@
#!/usr/local/bin/python3.8
import os
import config
os.system('git pull')
modules = {
'Voice Assistant': 'voice_assistant',
'Telegram bot': 'telegram_bot',
}
for name, module in modules.items():
try:
print(f'launching the {name}')
os.system(f'lxterminal --command="python3.8 {config.path}/{module}.py & read"')
except:
print(f'[error]\t{name} launch failed')
print('Running server...')
os.system(f'lxterminal --command="python3.8 {config.path}/Controls/Django/manage.py runserver 192.168.0.129:8000 & read"')
os.system(f'lxterminal --command="vlc"')