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

Integrate googles and wikis search systems, fix bugs

This commit is contained in:
dQz6tMwk8rJqvDR
2020-07-31 01:43:27 +03:00
parent fffe061d23
commit 87354aab8f
6 changed files with 152 additions and 72 deletions

View File

@ -3,6 +3,7 @@
# command - object (class instance) # command - object (class instance)
# Command.list - list of all commands # Command.list - list of all commands
# Command.find() - recognize command from a string, return command object # Command.find() - recognize command from a string, return command object
# must return dict like {'cmd': cmd, 'params': params}
# this - object (class instance) pointer # this - object (class instance) pointer
# abstract this.start() - required method for all commands # abstract this.start() - required method for all commands
# abstract this.confirm() - Return True/False (User responce) # abstract this.confirm() - Return True/False (User responce)
@ -33,11 +34,13 @@ class RThread(Thread):
return self._return return self._return
class Command(ABC): class Command(ABC):
_list = [] # list of all commands _list = [] # list of all commands
_specials = []
_patterns = { _patterns = {
'word': '[A-Za-zА-ЯЁа-яё0-9]+', 'word': '([A-Za-zА-ЯЁа-яё0-9])+',
'quest' : '(кто|что|как|какой|какая|какое|где|зачем|почему|сколько|чей|куда|когда)',
} }
_regex = { _regex = {
# stars * # stars *
'([A-Za-zА-ЯЁа-яё0-9\(\)\[\]\{\}]+)\*([A-Za-zА-ЯЁа-яё0-9\(\)\[\]\{\}]+)': r'\\b\1.*\2\\b', # 'te*xt' '([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'\\b.*\1', # '*text'
@ -45,16 +48,17 @@ class Command(ABC):
'(^|\s)\*($|\s)': r'.*', # '*' ' * ' '(^|\s)\*($|\s)': r'.*', # '*' ' * '
# one of the list (a|b|c) # one of the list (a|b|c)
'\(((?:.*\|)*.*)\)': r'(?:\1)', '\(((?:.*\|)*.*)\)': r'(?:\1)',
# 0 or 1 the of list [a|b|c] # 0 or 1 the of list [abc]
'\[((?:.*\|?)*?.*?)\]': r'(?:\1)??', '\[((?:.*\|?)*?.*?)\]': r'(?:\1)??',
# one or more of the list, without order {a|b|c} # one or more of the list, without order {a|b|c}
'\{((?:.*\|?)*?.*?)\}': r'(?:\1)+?', '\{((?:.*\|?)*?.*?)\}': r'(?:\1)+?',
} }
def __init__(this, name, keywords, patterns): # initialisation of new command def __init__(this, name, keywords = {}, patterns = [], special = False): # initialisation of new command
this._name = name this._name = name
this._keywords = keywords this._keywords = keywords
this._patterns = patterns this._patterns = patterns
Command.append(this) if special: Command.addSpecial(this)
else: Command.append(this)
def __str__(this): def __str__(this):
str = f'{this.__class__.__name__}.{this.getName()}:\n' str = f'{this.__class__.__name__}.{this.getName()}:\n'
@ -104,7 +108,7 @@ class Command(ABC):
# abstract # abstract
@abstractmethod @abstractmethod
def start(this, string): # main method def start(this, params): # main method
pass pass
@abstractmethod @abstractmethod
@ -116,33 +120,66 @@ class Command(ABC):
def getList(): def getList():
return Command._list return Command._list
def getRegexDict():
return Command._regex
def getPatternsDict():
return Command._patterns
def getSpecialsList():
return Command._specials
def getSpecial(string):
return Command._specials.get(string)
def addSpecial(obj):
Command._specials.append(obj)
@staticmethod @staticmethod
def append(obj): def append(obj):
Command._list.append(obj) Command._list.append(obj)
@staticmethod
def setSearch(obj):
Command._search = obj
@staticmethod @staticmethod
def ratio(string, word): def ratio(string, word):
return ( fuzz.WRatio(string, word) + fuzz.ratio(string, word) ) / 2 return ( fuzz.WRatio(string, word) + fuzz.ratio(string, word) ) / 2
@staticmethod
def compilePattern(pattern):
# transform patterns to regexp
for ptrn, regex in Command.getRegexDict().items():
pattern = re.sub(re.compile(ptrn), regex, pattern)
# find links like $Pattern
link = re.search(re.compile('\$[A-Za-zА-ЯЁа-яё0-9]+'), pattern)
if link: pattern = re.sub('\\'+link[0], f'(?P<{link[0][1:]}>{Command.compilePattern( Command.getPatternsDict()[ link[0][1:] ] )})', pattern)
# return compiled regexp
return pattern
@staticmethod @staticmethod
def find(string): def find(string):
string = string.lower() string = string.lower()
chances = {} chances = {}
list = Command.getList() list = Command.getList()
# calculate chances of every command
for i, obj in enumerate( list ): for i, obj in enumerate( list ):
chances[i] = 0 chances[i] = 0
k = 1 / ( sum( [int(w)*len(kw) for w, kw in obj.getKeywords().items()] ) or 1 ) k = 1 / ( sum( [int(w)*len(kw) for w, kw in obj.getKeywords().items()] ) or 1 )
for weight, kws in obj.getKeywords().items(): for weight, kws in obj.getKeywords().items():
for kw in kws: for kw in kws:
chances[i] += Command.ratio(string, kw) * weight * k chances[i] += Command.ratio(string, kw) * weight * k
# find command with the biggest chance
if( sum( chances.values() ) ): if( sum( chances.values() ) ):
top = max( chances.values() ) / sum( chances.values() ) * 100 top = max( chances.values() ) / sum( chances.values() ) * 100
else: else: # if all chances is 0
return { return {
'cmd': list[0], 'cmd': list[0],
'params': None, 'params': {},
} }
#if( max( chances.values() ) < 800 or top < 80): return list[0] #if( max( chances.values() ) < 800 or top < 50): return list[0]
# find top command obj
for i, chance in chances.items(): for i, chance in chances.items():
if chance == max( chances.values() ): if chance == max( chances.values() ):
return { return {
@ -154,28 +191,34 @@ class Command(ABC):
def reg_find(string): def reg_find(string):
string = string.lower() string = string.lower()
list = Command.getList() list = Command.getList()
if not string: return{
'cmd': list[0], #dialog mode
'params': {},
}
# find command obj by pattern
for obj in list: for obj in list:
for pattern in obj.getPatterns(): for pattern in obj.getPatterns():
# replace patterns if match := re.search(re.compile(Command.compilePattern(pattern)), string):
for ptrn, regex in Command._regex.items(): return {
pattern = re.sub(re.compile(ptrn), regex, pattern) 'cmd': obj,
# links $Pattern 'params': match.groupdict(),
link = re.search(re.compile('\$[A-Za-zА-ЯЁа-яё0-9\(\)\[\]\{\}]+'), pattern) }
if link: pattern = re.sub('\\'+link[0], f'(?P<{link[0][1:]}>{Command._patterns[link[0][1:]]})', pattern) # if command is search query
# find for obj in Command.getSpecialsList():
match = re.search(re.compile(pattern), string) for pattern in obj.getPatterns():
if(match): return { if match := re.search(re.compile(Command.compilePattern(pattern)), string): return {
'cmd': obj, 'cmd': obj,
'params': match.groupdict(), 'params': {**match.groupdict(), 'string': string},
} }
# return dialog bot if command not found
return { return {
'cmd': list[0], 'cmd': list[0], #dialog mode
'params': None, 'params': {},
} }
@staticmethod @staticmethod
def background(answer = '', voice = ''): def background(answer = '', voice = ''):
def decorator(cmd): def decorator(cmd): #wrapper of wrapper (decorator of decorator)
def wrapper(text): def wrapper(text):
finish_event = Event() finish_event = Event()
thread = RThread(target=cmd, args=(text, finish_event)) thread = RThread(target=cmd, args=(text, finish_event))

1
Command/__init__.py Normal file
View File

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

77
Search.py Normal file
View File

@ -0,0 +1,77 @@
import requests
import json
from bs4 import BeautifulSoup as BS
import wikipedia as wiki
from Command import Command
import re
class Search(Command):
def confirm(this, string): return True
def googleDictionary(this, word):
responce = requests.get(f'https://api.dictionaryapi.dev/api/v2/entries/ru/{word}')
if responce.status_code == 200:
responce = json.loads(responce.content)
text = ''
r = responce[0]
definition = r['meanings'][0]['definitions'][0]
short = r['word'].lower().capitalize() + ' (' + ( r['meanings'][0]['partOfSpeech'].capitalize() if r['meanings'][0]['partOfSpeech'] != 'undefined' else 'Разговорный' ) + ') — ' + definition['definition'].lower().capitalize() + ( '. Синонимы: ' + ', '.join(definition['synonyms']) if definition['synonyms'] else '')
short = short.replace(word[0].lower()+'.', word.lower())
short = short.replace(word[0].upper()+'.', word.capitalize())
short = short.replace('-н.', '-нибудь')
short = short.replace('потр.', 'потребляется')
short = short.replace('знач.', 'значении')
for r in responce:
text += '\n' + r['word'].lower().capitalize() + ' (' + (r['meanings'][0]['partOfSpeech'].capitalize() if r['meanings'][0]['partOfSpeech'] != 'undefined' else 'Разговорный') + ')\n'
for definition in r['meanings'][0]['definitions']:
text += '\t' + definition['definition'].lower().capitalize()
if example := definition.get('example'):
text += '\n\tНапример: ' + example
if synonyms := definition['synonyms']:
text += '\n\tСинонимы: ' + ', '.join(synonyms) + '.'
if antonyms := definition['antonyms']:
text += '\n\tАнтонимы: ' + ', '.join(antonyms) + '.'
text += '\n\n'
return {
'text': text,
'short': short,
}
return {}
def wikipedia(this, word):
wiki.set_lang("ru")
article = wiki.summary(word, sentences=5)
try: return article[:article.find('\n\n')][:600]
except: return ''
def googleSearch(this, word):
responce = requests.get(f'https://www.google.ru/search?&q={word}&lr=lang_ru&lang=ru')
page = BS(responce.content, 'html.parser')
info = page.select('div.BNeawe>div>div.BNeawe')
return info[0].get_text() if info else ''
def start(this, params):
query = params['string']
if 'вики' in query:
query = query.replace('википедия', '').replace('вики', '').strip()
try: search = this.googleSearch(query)
except: search = ''
try: wiki = this.wikipedia(query) if not 'Википедия' in search else ''
except: wiki = ''
try: gdict = this.googleDictionary(query)
except: gdict = []
voice = search or (gdict['short'] if gdict else '') or wiki
text = (f'Google Search:\n\t{search}' if search else '') + (f'\n\nWikipedia:\n\t{wiki}' if wiki else '') + ('\n\nDictionary:'+gdict['text'] if gdict else '')
else:
try: search = this.googleSearch(query)
except: search = ''
voice = text = search
return {
'type': 'simple',
'text': text,
'voice': voice,
}
google = Search('Search', {}, ['* вики* *','фильм *','* это','$quest *','{посчитай|сколько будет|корень из} *',], special=True)

View File

@ -21,7 +21,7 @@ from .SmallTalk import *
import datetime, time import datetime, time
import math import math
################################################################################ ################################################################################
def method(text): def method(params):
return { return {
'type': 'simple', 'type': 'simple',
'text': 'Я не понимаю', 'text': 'Я не понимаю',
@ -33,7 +33,7 @@ patterns = []
void = SmallTalk('Undefined', keywords, patterns) void = SmallTalk('Undefined', keywords, patterns)
void.setStart(method) void.setStart(method)
################################################################################ ################################################################################
def method(text): def method(params):
now = datetime.datetime.now() now = datetime.datetime.now()
hours = now.hour%12 if now.hour else 12 hours = now.hour%12 if now.hour else 12
minutes = now.minute minutes = now.minute
@ -112,13 +112,13 @@ keywords = {
5: ['текущее', 'сейчас', 'время'], 5: ['текущее', 'сейчас', 'время'],
1: ['сколько'] 1: ['сколько']
} }
patterns = ['* который * час *', '* скольк* * (врем|час)* *', '* врем* *'] patterns = ['* который * час *', '* скольк* * (врем|час)* *']
ctime = SmallTalk('Current Time', keywords, patterns) ctime = SmallTalk('Current Time', keywords, patterns)
ctime.setStart(method) ctime.setStart(method)
################################################################################ ################################################################################
# Only for tests # Only for tests
@SmallTalk.background(answer = 'Запуск фонового процесса', voice = 'Запускаю фоновый процесс') @SmallTalk.background(answer = 'Запуск фонового процесса', voice = 'Запускаю фоновый процесс')
def method(text, finish_event): def method(params, finish_event):
time.sleep(10) time.sleep(10)
finish_event.set() finish_event.set()
return { return {

View File

@ -33,7 +33,7 @@ while True: # main loop
if online := set(config.names) & set(text.split(' ')): if online := set(config.names) & set(text.split(' ')):
voids = 0 voids = 0
cmd, params = Command.reg_find(text).values() cmd, params = Command.reg_find(text).values()
responce = cmd.start(text) responce = cmd.start(params)
memory.insert(0, { memory.insert(0, {
'text': text, 'text': text,
'cmd': cmd, 'cmd': cmd,

File diff suppressed because one or more lines are too long