Compare commits
2 Commits
e2fd9b7daa
...
9be47df527
Author | SHA1 | Date | |
---|---|---|---|
9be47df527 | |||
d67ffbced2 |
2
Pipfile
2
Pipfile
@ -14,4 +14,4 @@ nltk = "*"
|
|||||||
spacy = "*"
|
spacy = "*"
|
||||||
|
|
||||||
[requires]
|
[requires]
|
||||||
python_version = "3.9"
|
python_version = "3.8"
|
||||||
|
17
TODO.md
17
TODO.md
@ -1,26 +1,33 @@
|
|||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
## 1. Main modulo
|
## Main modulo
|
||||||
1. Workers para
|
1. Workers para
|
||||||
1. [x] Revisar Email
|
1. [x] Revisar Email
|
||||||
1. [ ] Revisar WhatsApp
|
1. [ ] Revisar WhatsApp
|
||||||
1. [ ] Procesar Texto
|
1. [ ] Procesar Texto
|
||||||
|
|
||||||
## 2. Modulo de Revision de Email
|
## Modulo de Revision de Email
|
||||||
Para revisar si hay mails nuevos, validando que sea de las personas registradas.
|
Para revisar si hay mails nuevos, validando que sea de las personas registradas.
|
||||||
Si no está registrada se avisa a administrador para saber que hacer.
|
Si no está registrada se avisa a administrador para saber que hacer.
|
||||||
Limpieza de Inbox y Spam.
|
Limpieza de Inbox y Spam.
|
||||||
- [x] Revisar lista de emails
|
- [x] Revisar lista de emails
|
||||||
- [x] Revisar si fue revisado
|
- [x] Revisar si fue revisado
|
||||||
- [x] Revisar si corresponde a un "Jefe"
|
- [x] Revisar si corresponde a un "Jefe"
|
||||||
|
- [x] Confirmar con cerebro si es un comando
|
||||||
|
|
||||||
## 3. Modulo de WhatsApp
|
## Modulo de WhatsApp
|
||||||
Respuestas a mensajes.
|
Respuestas a mensajes.
|
||||||
|
|
||||||
## 4. Modulo de Recordatorio
|
## AI para procesar textos
|
||||||
|
Spacy permite procesar texto
|
||||||
|
Hay que poder "educar" el modelo
|
||||||
|
Hay que poder agregar datos
|
||||||
|
Hay que poder agregar los correos y mensajes que no se conocen a listado "desconocido"
|
||||||
|
|
||||||
|
## Modulo de Recordatorio
|
||||||
Crear recordatorios y mandar correo o WhatsApp en el momento del recordatorio.
|
Crear recordatorios y mandar correo o WhatsApp en el momento del recordatorio.
|
||||||
|
|
||||||
## 5. Modulo de Registro de Eventos
|
## Modulo de Registro de Eventos
|
||||||
Ir llevando un registro de eventos.
|
Ir llevando un registro de eventos.
|
||||||
|
|
||||||
### Otros
|
### Otros
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
"port": 993,
|
"port": 993,
|
||||||
"user": {
|
"user": {
|
||||||
"name": "secretary@incoviba.cl",
|
"name": "secretary@incoviba.cl",
|
||||||
"password": "quzshqzyfcnydevp"
|
"password": "vgvjuwzwizktdpka"
|
||||||
},
|
},
|
||||||
"ssl": true
|
"ssl": true
|
||||||
},
|
},
|
||||||
@ -13,7 +13,7 @@
|
|||||||
"port": 495,
|
"port": 495,
|
||||||
"user": {
|
"user": {
|
||||||
"name": "secretary@incoviba.cl",
|
"name": "secretary@incoviba.cl",
|
||||||
"password": "quzshqzyfcnydevp"
|
"password": "vgvjuwzwizktdpka"
|
||||||
},
|
},
|
||||||
"ssl": true
|
"ssl": true
|
||||||
},
|
},
|
||||||
|
@ -1,12 +1,29 @@
|
|||||||
import os
|
import os
|
||||||
import spacy
|
import spacy
|
||||||
|
from src.instrucciones import Instrucciones
|
||||||
|
|
||||||
|
|
||||||
class Brain:
|
class Brain:
|
||||||
def __init__(self, data_folder):
|
def __init__(self, data_folder):
|
||||||
|
self.folder = data_folder
|
||||||
self.filename = os.path.join(data_folder, 'brain.json')
|
self.filename = os.path.join(data_folder, 'brain.json')
|
||||||
self.nlp = spacy.load('es_core_news_sm')
|
self.nlp = None
|
||||||
|
|
||||||
|
self.load_nlp(data_folder)
|
||||||
|
|
||||||
|
def load_nlp(self, data_folder):
|
||||||
|
folder = os.path.join(data_folder, 'model')
|
||||||
|
self.nlp = spacy.load(folder)
|
||||||
|
|
||||||
|
def save_model(self):
|
||||||
|
folder = os.path.join(self.folder, 'model')
|
||||||
|
self.nlp.to_disk(folder)
|
||||||
|
|
||||||
def get_command(self, phrase):
|
def get_command(self, phrase):
|
||||||
|
doc = self.nlp(phrase)
|
||||||
|
command = max(doc.cats, key=doc.cats.get)
|
||||||
|
return command
|
||||||
|
|
||||||
|
def get_response(self, command, phrase):
|
||||||
doc = self.nlp(phrase)
|
doc = self.nlp(phrase)
|
||||||
return doc
|
return doc
|
||||||
|
15
src/brain/build_model.py
Normal file
15
src/brain/build_model.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import os
|
||||||
|
import spacy
|
||||||
|
from src.instrucciones import Instrucciones
|
||||||
|
|
||||||
|
|
||||||
|
def load_model(commands):
|
||||||
|
nlp = spacy.load('es_core_news_sm')
|
||||||
|
if 'textcat' not in nlp.pipe_names:
|
||||||
|
textcat = nlp.create_pipe('textcat')
|
||||||
|
nlp.add_pipe(textcat)
|
||||||
|
textcat.add_label('test')
|
||||||
|
for c in commands.instrucciones:
|
||||||
|
textcat.add_label(c.instruccion)
|
||||||
|
optimizer = nlp.begin_training()
|
||||||
|
return nlp
|
@ -1,8 +1,9 @@
|
|||||||
class Message:
|
class Message:
|
||||||
def __init__(self, mtype, sender, datetime, text, original):
|
def __init__(self, mtype, sender, datetime, text, original, subject):
|
||||||
self.type = mtype
|
self.type = mtype
|
||||||
self.sender = sender
|
self.sender = sender
|
||||||
self.datetime = datetime
|
self.datetime = datetime
|
||||||
self.text = text
|
self.text = text
|
||||||
self.checked = False
|
self.checked = False
|
||||||
self.original = original
|
self.original = original
|
||||||
|
self.subject = subject
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import email
|
from email.parser import BytesParser
|
||||||
|
from email.policy import default as DefaultPolicy
|
||||||
|
|
||||||
|
|
||||||
class Email:
|
class Email:
|
||||||
@ -10,8 +11,7 @@ class Email:
|
|||||||
status, raw_data = imap.uid('fetch', self.uid, '(RFC822)')
|
status, raw_data = imap.uid('fetch', self.uid, '(RFC822)')
|
||||||
if status != 'OK':
|
if status != 'OK':
|
||||||
raise Exception('Could not recover message {0}'.format(self.uid))
|
raise Exception('Could not recover message {0}'.format(self.uid))
|
||||||
|
self.message = BytesParser(policy=DefaultPolicy).parsebytes(text=raw_data[0][1])
|
||||||
self.message = email.message_from_bytes(raw_data[0][1])
|
|
||||||
|
|
||||||
def delete(self, imap):
|
def delete(self, imap):
|
||||||
status, result = imap.uid('STORE', self.uid, '+FLAGS', '(\\Deleted)')
|
status, result = imap.uid('STORE', self.uid, '+FLAGS', '(\\Deleted)')
|
||||||
|
@ -95,9 +95,7 @@ class Email(Thread):
|
|||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
self.start_workers()
|
self.start_workers()
|
||||||
worker = Thread(target=exit_thread, args=(self.params['events']['stop'], self.params['logging']))
|
worker = Thread(target=exit_thread, args=(self.params['events']['stop'], self.params['logging']))
|
||||||
self.add_worker(worker)
|
|
||||||
worker.start()
|
worker.start()
|
||||||
self.worker_status.append(True)
|
|
||||||
|
|
||||||
while not self.params['events']['stop'].is_set():
|
while not self.params['events']['stop'].is_set():
|
||||||
if not self.check_workers():
|
if not self.check_workers():
|
||||||
@ -106,3 +104,4 @@ class Email(Thread):
|
|||||||
self.params['logging'].log('Waiting for workers', type(self))
|
self.params['logging'].log('Waiting for workers', type(self))
|
||||||
self.params['events']['log_stop'].set()
|
self.params['events']['log_stop'].set()
|
||||||
self.join_workers()
|
self.join_workers()
|
||||||
|
worker.join()
|
||||||
|
@ -9,6 +9,7 @@ import email.utils
|
|||||||
from src.communication import Message
|
from src.communication import Message
|
||||||
import json
|
import json
|
||||||
from src.functions import dump_queue
|
from src.functions import dump_queue
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
|
||||||
class Obtenedor(Worker):
|
class Obtenedor(Worker):
|
||||||
@ -70,7 +71,7 @@ class Obtenedor(Worker):
|
|||||||
for part in email_part.get_payload():
|
for part in email_part.get_payload():
|
||||||
output += self.build_message(part)
|
output += self.build_message(part)
|
||||||
else:
|
else:
|
||||||
html = email_part.get_payload(decode=True)
|
html = email_part.get_payload(decode=True).decode('utf-8')
|
||||||
bs = BeautifulSoup(html, 'html.parser')
|
bs = BeautifulSoup(html, 'html.parser')
|
||||||
if bs.body:
|
if bs.body:
|
||||||
html = bs.body.get_text()
|
html = bs.body.get_text()
|
||||||
@ -94,8 +95,9 @@ class Obtenedor(Worker):
|
|||||||
if self.is_revisado(em.uid):
|
if self.is_revisado(em.uid):
|
||||||
continue
|
continue
|
||||||
sender = em.message['from']
|
sender = em.message['from']
|
||||||
text = ' '.join([em.message['subject'] + '.'] + self.build_message(em.message))
|
# text = ' '.join([em.message['subject'] + '.'] + self.build_message(em.message))
|
||||||
msg = Message('email', text=text, original=em, sender=sender,
|
text = self.build_message(em.message)
|
||||||
|
msg = Message('email', text=text, original=em, sender=sender, subject=str(em.message['subject']),
|
||||||
datetime=email.utils.parsedate_to_datetime(em.message['Date']))
|
datetime=email.utils.parsedate_to_datetime(em.message['Date']))
|
||||||
self.queue.put(msg)
|
self.queue.put(msg)
|
||||||
self.add_revisado(em.uid)
|
self.add_revisado(em.uid)
|
||||||
@ -132,7 +134,7 @@ class Validador(Worker):
|
|||||||
return self.bosses.is_boss(sender)
|
return self.bosses.is_boss(sender)
|
||||||
|
|
||||||
def validar_instrucciones(self, message):
|
def validar_instrucciones(self, message):
|
||||||
return self.instrucciones.is_valid(message.original.message['subject'])
|
return self.instrucciones.is_valid(message.subject)
|
||||||
|
|
||||||
def validar(self, message):
|
def validar(self, message):
|
||||||
if self.validar_bosses(message.sender):
|
if self.validar_bosses(message.sender):
|
||||||
@ -245,7 +247,7 @@ class Borrador(Worker):
|
|||||||
return
|
return
|
||||||
|
|
||||||
for uid in self.borrar:
|
for uid in self.borrar:
|
||||||
print(uid)
|
print('Borrar ', uid)
|
||||||
# status, ids = imap.uid('store', uid, '+FLAGS', b'\\Deleted')
|
# status, ids = imap.uid('store', uid, '+FLAGS', b'\\Deleted')
|
||||||
# if status != 'OK':
|
# if status != 'OK':
|
||||||
# continue
|
# continue
|
||||||
@ -281,6 +283,9 @@ class Procesador(Worker):
|
|||||||
self.frec = configs.get('supervisor.wait')
|
self.frec = configs.get('supervisor.wait')
|
||||||
self.brain = params['brain']
|
self.brain = params['brain']
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
self.brain.save_model()
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
self.start_turn()
|
self.start_turn()
|
||||||
while not self.stop.is_set():
|
while not self.stop.is_set():
|
||||||
@ -288,7 +293,10 @@ class Procesador(Worker):
|
|||||||
em = self.queue.get(timeout=self.frec)
|
em = self.queue.get(timeout=self.frec)
|
||||||
except queue.Empty:
|
except queue.Empty:
|
||||||
continue
|
continue
|
||||||
print(em.text)
|
command = self.brain.get_command(em.subject)
|
||||||
command = self.brain.get_command(em.text)
|
pprint((em.subject, command))
|
||||||
[print(ent) for ent in command.ents]
|
for t in em.text:
|
||||||
|
contenido = self.brain.get_response(command, t)
|
||||||
|
|
||||||
|
self.cleanup()
|
||||||
self.end_turn()
|
self.end_turn()
|
||||||
|
@ -10,14 +10,15 @@ class Command:
|
|||||||
class Commands:
|
class Commands:
|
||||||
def __init__(self, data_folder):
|
def __init__(self, data_folder):
|
||||||
self.filename = os.path.join(data_folder, 'commands.json')
|
self.filename = os.path.join(data_folder, 'commands.json')
|
||||||
data = []
|
|
||||||
try:
|
|
||||||
with open(self.filename, 'r') as f:
|
|
||||||
data = json.load(f)
|
|
||||||
except FileNotFoundError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
self.commands = []
|
self.commands = []
|
||||||
|
self.load_commands(self.filename)
|
||||||
|
|
||||||
|
def load_commands(self, filename):
|
||||||
|
data = []
|
||||||
|
if os.path.isfile(filename):
|
||||||
|
with open(filename, 'r') as f:
|
||||||
|
data = json.load(f)
|
||||||
for c in data:
|
for c in data:
|
||||||
cmd = Command()
|
cmd = Command()
|
||||||
cmd.command = c
|
cmd.command = c
|
||||||
@ -36,7 +37,6 @@ class Commands:
|
|||||||
class Instruccion:
|
class Instruccion:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.instruccion = ''
|
self.instruccion = ''
|
||||||
self.aliases = []
|
|
||||||
self.command = None
|
self.command = None
|
||||||
self.params = {}
|
self.params = {}
|
||||||
|
|
||||||
@ -44,22 +44,20 @@ class Instruccion:
|
|||||||
class Instrucciones:
|
class Instrucciones:
|
||||||
def __init__(self, data_folder):
|
def __init__(self, data_folder):
|
||||||
self.filename = os.path.join(data_folder, 'instrucciones.json')
|
self.filename = os.path.join(data_folder, 'instrucciones.json')
|
||||||
data = []
|
|
||||||
try:
|
|
||||||
with open(self.filename, 'r') as f:
|
|
||||||
data = json.load(f)
|
|
||||||
except FileNotFoundError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
self.commands = Commands(data_folder)
|
self.commands = Commands(data_folder)
|
||||||
|
|
||||||
self.instrucciones = []
|
self.instrucciones = []
|
||||||
|
self.load_instrucciones(self.filename)
|
||||||
|
self.idx = 0
|
||||||
|
|
||||||
|
def load_instrucciones(self, filename):
|
||||||
|
data = []
|
||||||
|
if os.path.isfile(filename):
|
||||||
|
with open(filename, 'r') as f:
|
||||||
|
data = json.load(f)
|
||||||
for d in data:
|
for d in data:
|
||||||
i = Instruccion()
|
i = Instruccion()
|
||||||
i.instruccion = d['name']
|
i.instruccion = d['name']
|
||||||
if 'aliases' in d:
|
|
||||||
for a in d['aliases']:
|
|
||||||
i.aliases.append(a)
|
|
||||||
if 'params' in d:
|
if 'params' in d:
|
||||||
for param, val in d['params'].items():
|
for param, val in d['params'].items():
|
||||||
i.params[param] = val
|
i.params[param] = val
|
||||||
@ -73,8 +71,6 @@ class Instrucciones:
|
|||||||
for i, ins in enumerate(self.instrucciones):
|
for i, ins in enumerate(self.instrucciones):
|
||||||
if instruccion == ins.instruccion:
|
if instruccion == ins.instruccion:
|
||||||
return i
|
return i
|
||||||
if instruccion in ins.aliases:
|
|
||||||
return i
|
|
||||||
|
|
||||||
def find(self, instruccion):
|
def find(self, instruccion):
|
||||||
if not self.is_valid(instruccion):
|
if not self.is_valid(instruccion):
|
||||||
@ -85,23 +81,16 @@ class Instrucciones:
|
|||||||
for i in self.instrucciones:
|
for i in self.instrucciones:
|
||||||
if instruccion == i.instruccion:
|
if instruccion == i.instruccion:
|
||||||
return True
|
return True
|
||||||
if instruccion in i.aliases:
|
|
||||||
return True
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def add(self, instruccion, aliases: list = None):
|
def add(self, instruccion, aliases: list = None):
|
||||||
if self.is_valid(instruccion):
|
if self.is_valid(instruccion):
|
||||||
if aliases is not None:
|
|
||||||
i = self.get(instruccion)
|
|
||||||
self.instrucciones[i].aliases = aliases
|
|
||||||
return
|
return
|
||||||
ins = Instruccion()
|
ins = Instruccion()
|
||||||
ins.instruccion = instruccion
|
ins.instruccion = instruccion
|
||||||
if aliases is not None:
|
|
||||||
ins.aliases = aliases
|
|
||||||
self.instrucciones.append(ins)
|
self.instrucciones.append(ins)
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
data = [{'instruccion': i.instruccion, 'aliases': i.aliases} for i in self.instrucciones]
|
data = [{'instruccion': i.instruccion, 'params': i.params.items()} for i in self.instrucciones]
|
||||||
with open(self.filename, 'w') as f:
|
with open(self.filename, 'w') as f:
|
||||||
json.dump(data, f, indent=4)
|
json.dump(data, f, indent=4)
|
||||||
|
Reference in New Issue
Block a user