Este repositorio es un servicio web de Flask en Python3 cuyo propósito es mandar mensajes de todo tipo de formatos a todo tipo de plataformas que tengan una API para ese propósito.
Implementa una base de datos de mensajes a enviar, usa la MAC address del host que pide mandar mensajes para diferenciar las consultas y permitir la posibilidad de que el mensaje venga encriptado.
## Instalación
Los requerimientos del programa son tener Python3 y pip3, para luego instalar los módulos de Flask, sqlite3 y python-arptable. Si no quiere hacerlo manualmente hay un archivo [install.sh](/install.sh).
Los requerimientos del programa son tener Python3 y pip3, para luego instalar los módulos de Flask, sqlite3 y python-arptable. Si no quiere hacerlo manualmente hay un archivo [install.sh](/install.sh)(desactualizado).
## Preparación
...
...
@@ -15,6 +17,7 @@ Para correr el servicio en el servidor simplemente hay que escribir en la consol
python3 deploy.py
```
## Uso
Para enviar mensajes se puede usar dos formas, la forma encriptada o la forma sin encripción, ambas son similares.
...
...
@@ -28,13 +31,13 @@ Llame a esta ruta para conseguir la llave pública del servidor en caso de quere
Este link es usado para subir los datos a enviar, pero obligatoriamente debe estar en formato de archivo. Opcionalmente puede estar encriptado, en cuyo caso debe mandar una llave en formato de archivo, el procedimiento es el siguiente:
- Crear una llave simétrica AES
- Encriptar el archivo con la llave simétrica
- Encriptar los archivos con la llave simétrica
- Encriptar la llave simétrica con la llave pública dada por el servidor
- Enviar el archivo encriptado con el nombre 'data' y la llave encriptada en formato de archivo con el nombre 'key' (data=@data.file, key=@key.file)
- Enviar los archivos encriptados con cualquier nombre y la llave encriptada en formato de archivo con el nombre 'key' (somename=@data.file, key=@key.file)
Si no se envía una llave se presume que el archivo no está encriptado.
/data devuelve un string de 16 caracteres que corresponde al mensaje, se usa en el siguiente paso.
/data devuelve un id que corresponde al mensaje, se usa en el siguiente paso.
Aclaración: el Content-Type debe ser multipart/form-data
...
...
@@ -42,24 +45,38 @@ Aclaración: el Content-Type debe ser multipart/form-data
Llamando a este link se envían los parámetros de envío del mensaje pasado en data, y se procede a poner en la cola el mensaje. (POST)
- Bajo 'id' se envía el string del paso anterior
- Bajo 'id' se envía el id devuelto en el paso anterior
- Bajo 'serv' se envía el servicio a usar, por ahora solo hay 'wpp1' correspondiente a WhatsApp
- Bajo 'dest' se envía el número de destino, el formato puede variar dependiendo del servicio y dicho formato será documentado, el de 'wpp1' es el formato internacional sin el '+' y sin espacios ni guiones
- Bajo 'type' se envía el tipo de dato a enviar, por ahora solo se permite 'text', los nombres serán documentados.
- Bajo 'type' se envía un json { archivo : tipo }, donde a cada archivo se le asigna un tipo
En caso de que no exista el servicio o este no admita el tipo, o que el id no exista, se devolverá un mensaje de error con esa información. Caso contrario, se devolverá el id de base de datos del mensaje para futura consulta.
En caso de que no exista el servicio o este no admita el tipo, o que el id no exista, se devolverá un mensaje de error con esa información. Caso contrario, se devolverá el string 'queued' desmotrando que todo salió bien y que el mensaje está en cola.
### myserver.com/cons
Por este link se consulta el estado de los mensajes que se quieren enviar, mandando el id bajo 'id' (POST). Se devuelve 'queued' o 'delivered'. Si fue enviado el mensaje se archiva y no puede ser consultado nuevamente.
Por este link se consulta el estado de los mensajes que se quieren enviar, mandando el id bajo 'id' (POST). Se devuelve 'preprocess', 'queued' o 'delivered'. Si fue enviado el mensaje se archiva y no puede ser consultado nuevamente. 'preprocess' significa que el mensaje no recibió parámetros. 'queued' que no todo fue enviado. 'delivered' que fueron enviados todos los archivos.
### A saber
Los mensajes a los que no se les provea parámetros serán eliminados después de 24 hs. Lo mismo para mensajes que no sean consultados por 24 hs.
## Servicios
WhatsApp 1 - ['wpp1'](/Wpp1.md)
Mail - ['mail'](/Mail.md)
Los tipos que aceptan y la forma de usarlos se encuentran en los links.
## Contribuir
Para contribuir con el proyecto se debe popular el archivo `services.py`
# Contribuir
Para contribuir con el proyecto, mayormente, se deberá popular el archivo `services.py`.
La forma de hacer esto consta de lo siguiente:
- Para servicios existentes se pueden agregar tipos de datos, en cuyo caso deberán ser incluidos en la clase `Datatypes` con su respectivo chequeo de validez en `validate` y se deberá agregar, si se necesita, una nueva restricción de formato (o funcionalidad) para todos los servicios.
- Para agregar nuevos servicios se debe hacer una nueva clase que derive de `ServiceBase` e implementar las funciones de dicha clase abstracta. Luego agregar el servicio a `Services`con su validación y expandir `serviceFactory(serv)` para que pueda devolver una instancia de la nueva clase
- Para agregar nuevos servicios se debe hacer una nueva clase que derive de `ServiceBase` e implementar las funciones de dicha clase abstracta. Luego agregar el servicio a `Services`y expandir `serviceFactory(serv)` para que pueda devolver una instancia de la nueva clase.
En el método
```
...
...
@@ -70,7 +87,7 @@ En el método
data es un dictionario que contiene los datos del pedido guardados en la base de datos
- En 'id' está el id del pedido, que coincide con el nombre del archivo a mandar
- En 'dir' está el nombre del directorio bajo el cual se encuentra el archivo a enviar
- En 'path' está el nombre del directorio bajo el cual se encuentran los archivos a enviar
- En 'dest' se encuentra el destinatario
- En 'type' se encuentra el formato de lo que queremos mandar
- En 'state' se encuentra el estado del mensaje, que siempre será 'queued' y es completamente irrelevante a la operación
...
...
@@ -87,10 +104,44 @@ El método
toma un string correspondiente al formato de los datos y retorna un booleano correspondiente a si el servicio soporta o no el tipo de dato. Es una buena práctica usar `Datatypes.validate()` para chequear que el formato existe.
## Servicios
## Archivos
En `deploy.py` se encuentra el servicio Flask, esta parte se tocará muy poco. En `process.py` se encuentran las aplicaciones de base de datos orientadas a lo que el servicio web necesita. En `database.py` se encuentran las interacciones genéricas con la base de datos. En `services.py` se encuentran las clases que representan los servicios de mensajería. En `enums.py` se encuentran las clases estáticas que facilitan la estructura que maneja el servicio.
## Errores
Si se envía un servicio inexistente, llegará un mensaje de error y el los parámetros serán cancelados. Lo mismo si, dado un servicio, los tipos de los archivos no están soportados o no existen.
Si se envía un tipo de un archivo que no existe se cancelará todo y se retornará el mensaje de error. Lo mismo con el id, aunque sin eso nada se puede hacer, puesto que no se pueden localizar los archivos.
Si en una consulta se envía un id incorrecto se responderá apropiadamente, pero no se va a romper nada.
Errores menos comunes incluyen:
- Intentar usar una tabla de base de datos que no existe
- En DBconnection.update(), usar un comparador no válido
- La data falla los chequeos de validez
## Tests
Hay un archivo que hace los [tests](/test_flask.py) automáticamente si se corre con `python3`. Tarda un minuto para permitir que se envíen todos los mensajes, hay que tener cuidado de que si hace falta que tarde más hay que cambiar la constante de tiempo.
Para agregar más tests, por cada uno debe existir una función y agregar un thread en main.
## Mejoras no implementadas
1. Añadir mas información a los mensajes de mail.
2. Test de mensajes de error.
3. Añadir estado "partially delivered" para mensajes enviados a medias.
4. Cambiar id de /data a string (posiblemente el nombre de la carpeta)*.
5. Posibilidad de usar un emisor de mensajes diferente.
WhatsApp 1 - ['wpp1'](/Wpp1)
*Esto, si bien es un golpe duro en performance, porque la búsqueda O(LogN) en base de datos se vuelve O(N), es un incremento sustancial en seguridad, puesto que nadie accidentalmente puede enviar parámetros a mensajes que no son suyos o consultar el estado de un mensaje enviado ajeno y borrarlo, volviéndolo inconsultable.
#### próximamente...
### Soluciones que pensé
Telegram - telegram
\ No newline at end of file
1. Cambiar la tabla "type" a "info" (o simplemente agregar como tabla separada) y que el json acepte parámetros de todo tipo específicos de cada servicio.
4. Remover por completo la noción de id numérico y que el comparador para los updates se vuelva el path
5. Agregar una tbla "sender" y que tenga "default" o algún otro que se prefiera.