paint-brush
Cree un segundo cerebro en línea autohospedado como Evernotepor@BlackLight
14,737 lecturas
14,737 lecturas

Cree un segundo cerebro en línea autohospedado como Evernote

por Fabio Manganiello2022/01/06
Read on Terminal Reader
Read this story w/o Javascript

Demasiado Largo; Para Leer

Evernote es una aplicación de "segundo cerebro" que sincroniza notas y enlaces a través de múltiples dispositivos. Obsidian es un producto multidispositivo de aspecto moderno basado en Markdown que almacena de forma transparente sus notas en su propio almacenamiento local y es casi demasiado bueno para ser verdad. Obsidian cobra $ 8 al mes solo por sincronizar contenido en sus propios dispositivos (copiar contenido en su propia nube), y puede publicar su contenido en la nube si desea compartirlo.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail

Coin Mentioned

Mention Thumbnail
featured image - Cree un segundo cerebro en línea autohospedado como Evernote
Fabio Manganiello HackerNoon profile picture


La necesidad de un segundo cerebro en línea

Cuando Evernote lanzó la idea de una libreta en línea como una especie de "segundo cerebro" hace más de una década, resonó mucho con lo que había estado tratando de lograr durante un tiempo. Para entonces, ya tenía toneladas de marcadores, archivos de texto con enlaces para leer más tarde, notas que había tomado en varios dispositivos, bocetos que había tomado en papel físico y borradores de artículos o trabajos en los que estaba trabajando. Todo este contenido solía ser escaso en muchos dispositivos, era doloroso sincronizarlo y luego Evernote llegó como agua en un desierto.


He sido un usuario feliz de Evernote hasta hace unos 5 o 6 años, cuando me di cuenta de que la empresa se había quedado sin ideas y ya no podía comprometerme con sus decisiones. Si se suponía que Evernote era mi segundo cerebro, debería haber sido muy sencillo sincronizarlo con mi sistema de archivos y entre varios dispositivos, pero no fue tan sencillo como parece. Evernote tenía una API primitiva, un recortador web primitivo, ningún cliente de Linux y, a medida que se esforzaba más y más para monetizar su producto, puso más y más funciones detrás de niveles caros. Además, Evernote experimentó pérdidas de datos , brechas de seguridad y controversias de privacidad que, en mi opinión, lo hicieron inadecuado para manejar algo tan preciado como las notas de mi vida y mi trabajo. No podía comprometerme con un producto que me cobraría $5 más al mes solo por tenerlo funcionando en un dispositivo adicional, especialmente cuando el producto en sí no me parecía tan sólido. Si se suponía que Evernote era mi segundo cerebro, entonces debería haber podido llevarlo conmigo a donde quisiera, sin tener que preocuparme por cuántos dispositivos ya lo estaba usando, sin tener que temer cambios futuros o políticas de monetización más agresivas. eso podría haber limitado mi capacidad para usar el producto.


Así que comencé mi viaje como un vagabundo de los servicios de toma de notas y almacenamiento de enlaces. Sí, idealmente, quiero algo que pueda hacer ambas cosas: su cerebro digital consta tanto de las notas que ha tomado como de los enlaces que ha guardado.


Probé muchos de ellos durante los años siguientes (Instapaper, Pocket, Readability, Mercury Reader, SpringPad, Google Keep, OneNote, Dropbox Paper...), pero finalmente no quedé satisfecho con la mayoría de ellos:


  1. En la mayoría de los casos, esos productos caen en la categoría de toma de notas o en la categoría de raspador/protector web, rara vez en ambas.
  2. En la mayoría de los casos, debe pagar una tarifa mensual o anual por algo tan simple como almacenar y sincronizar texto.
  3. Muchos de los productos anteriores carecen de una API para importar/exportar/leer datos mediante programación, o colocan sus API detrás de algunos niveles premium. Esto es imposible para mí: si la empresa que crea el producto se cae, lo último que quiero son mis notas personales, enlaces y marcadores para que se caigan y no sea fácil sacarlos.
  4. La mayoría de esos productos no tienen funciones de sincronización del sistema de archivos local: todo solo funciona en su aplicación.


Mi insatisfacción con los productos en el mercado se alivió un poco cuando descubrí Obsidian . ¿Un producto multidispositivo, de aspecto moderno y basado en Markdown que almacena de forma transparente sus notas en su propio almacenamiento local e incluso proporciona muchos complementos comunitarios? Eso cubre todo lo que quiero; es casi demasiado bueno para ser verdad! Y, de hecho, es demasiado bueno para ser verdad. Obsidian cobra $8 al mes solo por sincronizar contenido entre dispositivos (copiar contenido a su propia nube) y $16 al mes si desea publicar/compartir su contenido. ¡Son precios inaceptablemente altos para algo tan simple como sincronizar y compartir archivos de texto! Este fue el detonante que me motivó a tomar el asunto en mis propias manos, así que se me ocurrió la lista de deseos para mi aplicación ideal de "segundo cerebro":


  1. Debe ser autohospedado. No hay servicios en la nube involucrados: es fácil poner cosas en la nube de otra persona, por lo general es mucho más difícil sacarlas, y los servicios en la nube no son confiables por definición: pueden decidir de un momento a otro que no están ganando suficiente dinero, cobrar más para algunas funciones que está utilizando mientras mantiene sus propios datos más preciados como rehenes. O, peor aún, podrían bajar y llevarse todos sus datos con ellos.
  2. Cada dispositivo debe tener una copia local de mi computadora portátil y debe ser sencillo sincronizar los cambios entre estas copias.
  3. Debería estar basado en Markdown. Markdown es portátil, limpio, fácil de indexar y buscar, se puede convertir fácilmente a HTML si es necesario, pero es mucho menos engorroso de leer y escribir, y es fácil de importar/exportar. Para dar una idea del poder y la flexibilidad subestimados de Markdown, tenga en cuenta que todos los artículos en el blog de Platypush son archivos estáticos de Markdown en un servidor local que se convierten sobre la marcha a HTML antes de ser enviados a su navegador.
  4. Debe poder manejar mis propias notas, así como analizar y convertir a páginas web de Markdown que me gustaría guardar o leer más tarde.
  5. Debe ser fácil agregar y modificar contenido. Ya sea que desee agregar un nuevo enlace desde la sesión de mi navegador en mi computadora portátil, teléfono o tableta, o escribir un texto sobre la marcha desde mi teléfono, o continuar trabajando en un borrador desde otro dispositivo, debería poder hacerlo con sin fricción, como si siempre estuviera trabajando en el mismo dispositivo.
  6. Necesita funcionar sin conexión. Quiero poder trabajar en un artículo de blog mientras estoy en un vuelo sin conexión a Internet, y espero que el contenido se sincronice automáticamente tan pronto como mi dispositivo obtenga una conexión.
  7. Tiene que estar basado en archivos. Estoy harto de formatos personalizados, API arcanas y otras barreras y abstracciones sin sentido entre mi texto y yo. La regla KISS se aplica aquí: si es un archivo de texto y aparece en mi máquina dentro de un directorio normal, expóngalo como un archivo de texto y obtendrá primitivas como leer/crear/modificar/copiar/mover/eliminar gratis.
  8. Necesita encapsular una buena lógica de raspado/análisis web, de modo que cada página web pueda destilarse en un formato Markdown legible y fácilmente exportable.
  9. Debe permitir rutinas automatizadas, por ejemplo, obtener automáticamente contenido nuevo de un feed RSS y descargarlo en un formato legible en el repositorio compartido.


Parece una larga lista de compras, pero en realidad no se necesita mucho para implementarla. Es hora de llegar a la pizarra y diseñar su arquitectura.

Arquitectura de alto nivel

Desde una perspectiva de alto nivel, la arquitectura que estamos tratando de construir se parece a algo como esto:

Arquitectura de alto nivel

El repositorio Git

Básicamente usamos un servidor git como repositorio para nuestras notas y enlaces. Podría ser un repositorio privado en GitHub o Gitlab, o incluso una carpeta estática inicializada como un repositorio git en un servidor accesible a través de SSH. Hay muchas ventajas en elegir un sistema de control de versiones como git como la fuente de verdad para el contenido de su cuaderno:


  1. El seguimiento del historial es gratuito: es fácil realizar un seguimiento de los cambios realizados por diferentes dispositivos, así como retroceder a versiones anteriores; nunca se pierde nada.
  2. Fácil sincronización : la inserción de nuevo contenido en sus notas se puede asignar a un git push , la sincronización de nuevo contenido en otros dispositivos se puede asignar a un git pull .
  3. Interfaces nativas compatibles con Markdown: tanto GitHub como Gitlab proporcionan buenas interfaces nativas para visualizar el contenido de Markdown. Explorar y administrar su cuaderno es tan fácil como explorar un repositorio de git.
  4. Fácil de importar y exportar : exportar su computadora portátil a otro dispositivo es tan simple como ejecutar un git clone .
  5. Flexibilidad de almacenamiento : puede crear el repositorio en una instancia en la nube, en una instancia autohospedada o en cualquier máquina con una interfaz SSH. El repositorio puede estar en cualquier lugar, siempre que sea accesible para los dispositivos que desea usar.


Entonces, el primer requisito para este proyecto es configurar un repositorio git en cualquier fuente que desee usar para el almacenamiento central de su computadora portátil. Tenemos principalmente tres opciones para esto:

Cree un nuevo repositorio en una instancia en la nube de GitHub/Gitlab.

  1. Pros : no tiene que mantener un servidor git, solo tiene que crear un nuevo proyecto y tiene todas las interfaces sofisticadas para administrar archivos y ver el contenido de Markdown.
  2. Contras : en realidad no es 100% autohospedado, ¿no es así? :)

Aloje una instancia de Gitlab usted mismo.

  1. Pros : mucha flexibilidad cuando se trata de hospedaje. Incluso puede ejecutar el servidor en una máquina a la que solo se puede acceder desde el exterior a través de una VPN, lo que brinda algunas características de seguridad agradables y encapsulación de contenido. Además, tiene una interfaz moderna como Gitlab para manejar sus archivos y también puede configurar fácilmente la automatización del repositorio a través de webhooks.
  2. Contras : instalar y ejecutar una instancia de Gitlab es un proceso con su propia curva de aprendizaje. Además, una instancia de Gitlab suele consumir muchos recursos; no la ejecute en una Raspberry Pi si desea que la experiencia del usuario sea fluida.

Inicialice un repositorio vacío en cualquier servidor de acceso público (o accesible a través de VPN) con una interfaz SSH.

Una característica a menudo olvidada de git es que es básicamente un contenedor sobre SSH, por lo tanto, puede crear un repositorio sobre la marcha en cualquier máquina que ejecute un servidor SSH, sin necesidad de un marco web completo encima. Es tan simple como:

 # Server machine $ mkdir -p /home/user/notebook.git $ cd /home/user/notebook.git $ git init --bare # Client machine $ git clone user@remote-machine:/home/user/notebook.git
  1. Pros : la opción más flexible: puede ejecutar el almacenamiento de su computadora portátil en literalmente cualquier cosa que tenga una CPU, una interfaz SSH y git.
  2. Contras : no tendrá una interfaz nativa elegante para administrar sus archivos, ni funciones de automatización de repositorios como acciones o webhooks (disponibles con GitHub y Gitlab, respectivamente).

El servidor web Markdown

Puede ser útil tener un servidor web para acceder a sus notas y enlaces desde cualquier navegador, especialmente si su repositorio no vive en GitHub/Gitlab y, por lo tanto, no tiene una forma nativa de exponer los archivos en la web.


Clone el repositorio del portátil en la máquina en la que desea exponer el servidor web Markdown y luego instale Madness y sus dependencias:


 $ sudo apt install ruby-full $ gem install madness


Tome nota de dónde se instaló el ejecutable madness y cree un nuevo archivo de servicio systemd de usuario en ~/.config/systemd/user/madness.service para administrar el servidor en su carpeta de repositorio:


 [Unit] Description=Serve Markdown content over HTML After=network.target [Service] ExecStart=/home/user/.gem/ruby/version/bin/madness /path/to/the/notebook --port 9999 Restart=always RestartSec=10 [Install] WantedBy=default.target


Vuelva a cargar el demonio systemd e inicie/habilite el servidor:


 $ systemctl --user daemon-reload $ systemctl --user start madness $ systemctl --user enable madness


Si todo salió bien, puede dirigir su navegador a http://host:9999 y debería ver la interfaz de Madness con sus archivos de Markdown.


Captura de pantalla de la interfaz de locura


Puede configurar fácilmente un proxy inverso nginx o un túnel SSH para exponer el servidor fuera de la red local.

El corredor MQTT

Un corredor MQTT es otro ingrediente crucial en esta configuración. Se utiliza para transmitir eventos de forma asíncrona, como una solicitud para agregar una nueva URL o actualizar las copias del repositorio local.


Cualquiera de los corredores MQTT de código abierto que existen debería hacer el trabajo. Yo personalmente uso Mosquitto para la mayoría de mis proyectos, pero RabbitMQ , Aedes o cualquier otro corredor deberían funcionar.


Al igual que el servidor git, también debe instalar el MQTT en una máquina que sea de acceso público o accesible a través de VPN para todos los dispositivos en los que desea usar su computadora portátil. Si opta por una máquina con una dirección IP de acceso público, se recomienda habilitar SSL y la autenticación de nombre de usuario/contraseña en su corredor, para que las personas no autorizadas no puedan conectarse a ella.


Tomando el caso de Mosquitto, la instalación y configuración es bastante sencilla. Instale el paquete mosquitto desde su administrador de paquetes favorito; el proceso de instalación también debe crear un archivo de configuración en /etc/mosquitto/mosquitto.conf . En el caso de una configuración SSL con usuario y contraseña, normalmente configuraría las siguientes opciones:


 # Usually 1883 for non-SSL connections, 8883 for SSL connections port 8883 # SSL/TLS version tls_version tlsv1.2 # Path to the certificate chain cafile /etc/mosquitto/certs/chain.crt # Path to the server certificate certfile /etc/mosquitto/certs/server.crt # Path to the server private key keyfile /etc/mosquitto/certs/server.key # Set to false to disable access without username and password allow_anonymous false # Password file, which contains username:password pairs # You can create and manage a password file by following the # instructions reported here: # https://mosquitto.org/documentation/authentication-methods/ password_file /etc/mosquitto/passwords.txt


Si no necesita encriptación SSL y autenticación en su corredor (lo cual está bien si está ejecutando el corredor en una red privada y accede desde el exterior a través de VPN), solo necesitará configurar la opción de port .


Una vez que haya configurado el agente MQTT, puede iniciarlo y habilitarlo a través de systemd :

 $ sudo systemctl start mosquitto $ sudo systemctl enable mosquitto

Luego puede usar un cliente MQTT como MQTT Explorer para conectarse al corredor y verificar que todo funcione.

La automatización Platypush

Una vez que el repositorio de git y el intermediario MQTT estén en su lugar, es hora de configurar Platypush en una de las máquinas en las que desea mantener su computadora portátil sincronizada, por ejemplo, su computadora portátil.

En este contexto, Platypush se usa para unir las piezas de la automatización de sincronización definiendo las siguientes cadenas de eventos:


  1. Cuando se detecta un cambio en el sistema de archivos en la carpeta donde se clonó el cuaderno (por ejemplo, porque se agregó, eliminó o editó una nota), inicie un temporizador que, por ejemplo, 30 segundos sincroniza los cambios en el repositorio de git (el temporizador se usa para limitar la frecuencia de los eventos de actualización). Luego, envíe un mensaje al tema MQTT notebook/sync para decirles a los otros clientes que deben sincronizar sus copias del repositorio.
  2. Cuando un cliente recibe un mensaje en notebook/sync y el autor es diferente del propio cliente (esto es necesario para evitar "bucles de sincronización"), extraiga los cambios más recientes del repositorio remoto.
  3. Cuando un cliente específico (que estará a cargo de extraer las URL y agregar nuevo contenido remoto) recibe un mensaje en el notebook/save tema con una URL adjunta, el contenido de la página web asociada se analizará y guardará en el cuaderno ( función "Guardar URL").


La misma lógica de automatización se puede configurar en tantos clientes como desee.

El primer paso es instalar el servidor Redis y Platypush en su máquina cliente. Por ejemplo, en un sistema basado en Debian:


 # Install Redis $ sudo apt install redis-server # Start and enable the Redis server $ sudo systemctl start redis-server $ sudo systemctl enable redis-server # Install Platypush $ sudo pip install platypush


Luego deberá crear un archivo de configuración para decirle a Platypush qué servicios desea usar. Nuestro caso de uso requerirá las siguientes integraciones:


  • mqtt ( backend y complemento ), utilizado para suscribirse para sincronizar/guardar temas y enviar mensajes al intermediario.
  • file.monitor backend, utilizado para monitorear los cambios en las carpetas locales.
  • [Opcional] pushbullet o una forma alternativa de enviar notificaciones a otros dispositivos (como telegram , twilio , gotify , mailgun ). Usaremos esto para notificar a otros clientes cuando se agregue contenido nuevo.
  • [Opcional] la integración de http.webpage , que se usa para raspar el contenido de una página web a Markdown o PDF.


Comience creando un archivo config.yaml con sus integraciones:


 # The name of your client device_id: my-client mqtt: host: your-mqtt-server port: 1883 # Uncomment the lines below for SSL/user+password authentication # port: 8883 # username: user # password: pass # tls_cafile: ~/path/to/ssl.crt # tls_version: tlsv1.2 # Specify the topics you want to subscribe here backend.mqtt: listeners: - topics: - notebook/sync # The configuration for the file monitor follows. # This logic triggers FileSystemEvents whenever a change # happens on the specified folder. We can use these events # to build our sync logic backend.file.monitor: paths: # Path to the folder where you have cloned the notebook # git repo on your client - path: /path/to/the/notebook recursive: true # Ignore changes on non-content sub-folders, such as .git or # other configuration/cache folders ignore_directories: - .git - .obsidian


Luego genere un nuevo entorno virtual Platypush desde el archivo de configuración:


 $ platyvenv build -c config.yaml


Una vez que se haya ejecutado el comando, debería informar una línea como la siguiente:


 Platypush virtual environment prepared under /home/user/.local/share/platypush/venv/my-client


Llamemos a esta ruta $PREFIX . Cree una estructura para almacenar sus scripts en $PREFIX/etc/platypush (una copia del archivo config.yaml ya debería estar allí en este punto). La estructura se verá así:


 $PREFIX -> etc -> platypush -> config.yaml # Configuration file -> scripts # Scripts folder -> __init__.py # Empty file -> notebook.py # Logic for notebook synchronization


Procedamos con la definición de la lógica central en notebook.py :


 import logging import os import re from threading import RLock, Timer from platypush.config import Config from platypush.event.hook import hook from platypush.message.event.file import FileSystemEvent from platypush.message.event.mqtt import MQTTMessageEvent from platypush.procedure import procedure from platypush.utils import run logger = logging.getLogger('notebook') repo_path = '/path/to/your/git/repo' sync_timer = None sync_timer_lock = RLock() def should_sync_notebook(event: MQTTMessageEvent) -> bool: """ Only synchronize the notebook if a sync request came from a source other than ourselves - this is required to prevent "sync loops", where a client receives its own sync message and broadcasts sync requests again and again. """ return Config.get('device_id') != event.msg.get('origin') def cancel_sync_timer(): """ Utility function to cancel a pending synchronization timer. """ global sync_timer with sync_timer_lock: if sync_timer: sync_timer.cancel() sync_timer = None def reset_sync_timer(path: str, seconds=15): """ Utility function to start a synchronization timer. """ global sync_timer with sync_timer_lock: cancel_sync_timer() sync_timer = Timer(seconds, sync_notebook, (path,)) sync_timer.start() @hook(MQTTMessageEvent, topic='notebook/sync') def on_notebook_remote_update(event, **_): """ This hook is triggered when a message is received on the notebook/sync MQTT topic. It triggers a sync between the local and remote copies of the repository. """ if not should_sync_notebook(event): return sync_notebook(repo_path) @hook(FileSystemEvent) def on_notebook_local_update(event, **_): """ This hook is triggered when a change (ie file/directory create/update/delete) is performed on the folder where the repository is cloned. It starts a timer to synchronize the local and remote repository copies. """ if not event.path.startswith(repo_path): return logger.info(f'Synchronizing repo path {repo_path}') reset_sync_timer(repo_path) @procedure def sync_notebook(path: str, **_): """ This function holds the main synchronization logic. It is declared through the @procedure decorator, so you can also programmatically call it from your requests through eg `procedure.notebook.sync_notebook`. """ # The timer lock ensures that only one thread at the time can # synchronize the notebook with sync_timer_lock: # Cancel any previously awaiting timer cancel_sync_timer() logger.info(f'Synchronizing notebook - path: {path}') cwd = os.getcwd() os.chdir(path) has_stashed_changes = False try: # Check if the local copy of the repo has changes git_status = run('shell.exec', 'git status --porcelain').strip() if git_status: logger.info('The local copy has changes: synchronizing them to the repo') # If we have modified/deleted files then we stash the local changes # before pulling the remote changes to prevent conflicts has_modifications = any(re.match(r'^\s*[MD]\s+', line) for line in git_status.split('\n')) if has_modifications: logger.info(run('shell.exec', 'git stash', ignore_errors=True)) has_stashed_changes = True # Pull the latest changes from the repo logger.info(run('shell.exec', 'git pull --rebase')) if has_modifications: # Un-stash the local changes logger.info(run('shell.exec', 'git stash pop')) # Add, commit and push the local changes has_stashed_changes = False device_id = Config.get('device_id') logger.info(run('shell.exec', 'git add .')) logger.info(run('shell.exec', f'git commit -a -m "Automatic sync triggered by {device_id}"')) logger.info(run('shell.exec', 'git push origin main')) # Notify other clients by pushing a message to the notebook/sync topic # having this client ID as the origin. As an alternative, if you are using # Gitlab to host your repo, you can also configure a webhook that is called # upon push events and sends the same message to notebook/sync. run('mqtt.publish', topic='notebook/sync', msg={'origin': Config.get('device_id')}) else: # If we have no local changes, just pull the remote changes logger.info(run('shell.exec', 'git pull')) except Exception as e: if has_stashed_changes: logger.info(run('shell.exec', 'git stash pop')) # In case of errors, retry in 5 minutes reset_sync_timer(path, seconds=300) raise e finally: os.chdir(cwd) logger.info('Notebook synchronized')


Ahora puede iniciar el entorno recién configurado:


 $ platyvenv start my-client


O cree un servicio de usuario systemd para él en ~/.config/systemd/user/platypush-notebook.service :


 $ cat <<EOF > ~/.config/systemd/user/platypush-notebook.service [Unit] Description=Platypush notebook automation After=network.target [Service] ExecStart=/path/to/platyvenv start my-client ExecStop=/path/to/platyvenv stop my-client Restart=always RestartSec=10 [Install] WantedBy=default.target EOF $ systemctl --user daemon-reload $ systemctl --user start platypush-notebook $ systemctl --user enable platypush-notebook


Mientras se ejecuta el servicio, intente crear un nuevo archivo Markdown en la copia local del repositorio supervisado. En unos segundos, la automatización debería activarse y el nuevo archivo debería enviarse automáticamente al repositorio. Si está ejecutando el código en varios hosts, estos también deberían obtener las actualizaciones en segundos. También puede ejecutar una instancia en el mismo servidor que ejecuta Madness para sincronizar su copia del repositorio, y su instancia web permanecerá sincronizada con cualquier actualización. ¡Felicitaciones, ha configurado una red distribuida para sincronizar sus notas!

Configuración de Android

Probablemente desee una forma de acceder a su computadora portátil también en su teléfono y tableta y mantener la copia en sus dispositivos móviles sincronizada automáticamente con el servidor.


Afortunadamente, es posible instalar y ejecutar Platypush en Android a través de Termux , y la lógica que ha configurado en sus computadoras portátiles y servidores también debería funcionar sin problemas en Android. Termux le permite ejecutar un entorno Linux en modo usuario sin necesidad de rootear su dispositivo.


Primero, instale la aplicación Termux en su dispositivo Android. Opcionalmente, también puede instalar las siguientes aplicaciones:


  • Termux:API : para acceder mediante programación a funciones de Android (p. ej., mensajes de texto SMS, cámara, GPS, nivel de batería, etc.) desde sus scripts.
  • Termux:Boot : para iniciar servicios como Redis y Platypush en el momento del arranque sin tener que abrir primero la aplicación Termux (recomendado).
  • Termux:Widget : para agregar scripts (por ejemplo, para iniciar manualmente Platypush o sincronizar el portátil) en la pantalla de inicio.
  • Termux:GUI : para agregar soporte para elementos visuales (como diálogos y widgets para compartir contenido) a sus scripts.


Después de instalar Termux, abra una nueva sesión, actualice los paquetes, instale termux-services (para soporte de servicios) y habilite el acceso SSH (generalmente es más útil escribir comandos en un teclado físico que en la pantalla de un teléfono):


 $ pkg update $ pkg install termux-services openssh # Start and enable the SSH service $ sv up sshd $ sv-enable sshd # Set a user password $ passwd


Un servicio habilitado a través sv-enable se iniciará cuando se abra una sesión de Termux por primera vez, pero no en el momento del arranque a menos que se inicie Termux. Si desea que un servicio se inicie en el momento del arranque, debe instalar la aplicación Termux:Boot y luego colocar los scripts que desea ejecutar en el momento del arranque dentro de la carpeta ~/.termux/boot .


Después de iniciar sshd y establecer una contraseña, debería poder iniciar sesión en su dispositivo Android a través de SSH:


 $ ssh -p 8022 anyuser@android-device


El siguiente paso es habilitar el acceso de Termux al almacenamiento interno (de manera predeterminada, solo puede acceder a la carpeta de datos de la aplicación). Esto se puede hacer fácilmente ejecutando termux-setup-storage y permitiendo el acceso al almacenamiento en el aviso. También es posible que queramos deshabilitar la optimización de la batería para Termux, para que los servicios no se eliminen en caso de inactividad.


Luego instale git, Redis, Platypush y sus dependencias de Python, e inicie/habilite el servidor Redis:


 $ pkg install git redis python3 $ pip install platypush


Si al ejecutar el comando redis-server se produce un error, es posible que deba deshabilitar explícitamente una advertencia de un error COW para arquitecturas ARM64 en el archivo de configuración de Redis. Simplemente agregue o descomente la siguiente línea en /data/data/com.termux/files/usr/etc/redis.conf :


 ignore-warnings ARM64-COW-BUG


Luego, debemos crear un servicio para Redis, ya que no está disponible de forma predeterminada. Termux no usa systemd para administrar servicios, ya que eso requeriría acceso al PID 1, que solo está disponible para el usuario raíz. En su lugar, utiliza su propio sistema de scripts que lleva el nombre de servicios de Termux .


Los servicios se instalan en /data/data/com.termux/files/usr/var/service . Simplemente haga un cd en ese directorio y copie el servicio sshd disponible en redis :


 $ cd /data/data/com.termux/files/usr/var/service $ cp -r sshd redis


Luego reemplace el contenido del archivo de run en el directorio del servicio con esto:


 #!/data/data/com.termux/files/usr/bin/sh exec redis-server 2>&1


Luego reinicie Termux para que actualice su lista de servicios e inicie/habilite el servicio Redis (o cree un script de arranque para él):


 $ sv up redis $ sv-enable redis


Verifique que pueda acceder a la carpeta /sdcard (almacenamiento compartido) después de reiniciar Termux. Si ese es el caso, ahora podemos clonar el repositorio del portátil en /sdcard/notebook :


 $ git clone git-url /sdcard/notebook


Los pasos para instalar y configurar la automatización Platypush son los mismos que se muestran en la sección anterior, con las siguientes excepciones:


  • repo_path en la secuencia de comandos notebook.py debe apuntar a /sdcard/notebook : si la computadora portátil se clona en el directorio de inicio del usuario, otras aplicaciones no podrán acceder a ella.
  • Si desea ejecutarlo en un servicio, deberá seguir los mismos pasos ilustrados para Redis en lugar de crear un servicio systemd.


También es posible que desee redirigir Platypush stdout/stderr a un archivo de registro, ya que los mensajes de Termux no tienen el mismo nivel sofisticado de registro proporcionado por systemd. Por lo tanto, el comando de inicio debería verse así:


 platyvenv start my-client > /path/to/logs/platypush.log 2>&1


Una vez que todo esté configurado y reinicie Termux, Platypush debería iniciarse automáticamente en segundo plano; puede verificar el estado ejecutando una tail en el archivo de registro o mediante el comando ps . Si cambia un archivo en su computadora portátil, ya sea en su dispositivo Android o en su computadora portátil, todo debería actualizarse en un minuto.


Finalmente, también podemos aprovechar Termux:Shortcuts para agregar un widget a la pantalla de inicio para activar manualmente el proceso de sincronización, tal vez porque se recibió una actualización mientras el teléfono estaba apagado o el servicio Platypush no se estaba ejecutando. Cree una carpeta ~/.shortcuts con un script dentro llamado, por ejemplo sync_notebook.sh :


 #!/data/data/com.termux/files/usr/bin/bash cat <<EOF | python from platypush.utils import run run('mqtt.publish', topic='notebook/sync', msg={'origin': None}) EOF


Este script aprovecha el método platypush.utils.run para enviar un mensaje al tema MQTT de notebook/sync sin origin para obligar a todos los clientes suscritos a extraer las últimas actualizaciones del servidor remoto.


Ahora puede navegar hasta el menú de widgets de su dispositivo Android (generalmente se hace presionando prolongadamente un área vacía en el iniciador), seleccione el acceso directo de Termux y luego seleccione su script recién creado. Al hacer clic en el icono, forzará una sincronización en todos los dispositivos conectados.


Una vez que Termux está correctamente configurado, no necesita repetir todo el procedimiento en otros dispositivos Android. Simplemente use los scripts de copia de seguridad de Termux para hacer una copia de seguridad de toda su configuración y copiarla/restaurarla en otro dispositivo, y tendrá toda la lógica de sincronización en funcionamiento.

La aplicación de obsidiana

Ahora que la lógica de sincronización de back-end está en su lugar, es hora de pasar al lado de front-end. Como mencioné anteriormente, Obsidian es una opción que realmente me gusta: tiene una interfaz moderna, es multiplataforma, está basada en electronjs , tiene muchos complementos, se basa en Markdown simple y solo necesita una carpeta local para funcionar.


Como se mencionó anteriormente, normalmente necesitaría suscribirse a Obsidian Sync para sincronizar notas entre dispositivos, pero ahora tiene una copia de git repo de sincronización automática en cualquier dispositivo que desee. ¡Simplemente instale Obsidian en su computadora o dispositivo móvil, apúntelo a la copia local del cuaderno git y estará listo para comenzar!


captura de pantalla de obsidiana

La opción NextCloud

Otra buena opción para sincronizar su computadora portátil en varios dispositivos es usar una instancia de NextCloud . NextCloud proporciona una aplicación de notas que ya admite notas en formato Markdown y también viene con una aplicación de Android .


Si esa es la forma en que desea ir, aún puede tener notas <-> sincronización de git simplemente configurando la automatización del cuaderno Platypush en el servidor donde se ejecuta NextCloud. Simplemente clone el repositorio en su carpeta NextCloud Notes:


 $ git clone git-url /path/to/nextcloud/data/user/files/Notes


Y luego configure repo_path en notebook.py en este directorio.


Sin embargo, tenga en cuenta que los cambios locales en la carpeta Notes no se sincronizarán con la aplicación NextCloud hasta que se ejecute el siguiente cron. Si desea que los cambios se propaguen tan pronto como se envíen al repositorio de git, deberá agregar una pieza de lógica adicional al script que sincroniza el cuaderno, para volver a escanear la carpeta de Notes en busca de cambios. Además, Platypush deberá ejecutarse con el mismo usuario que ejecuta el servidor web NextCloud debido a los requisitos para ejecutar el script occ :


 import logging from platypush.utils import run ... logger = logging.getLogger('notebook') # Path to the NextCloud occ script occ_path = '/srv/http/nextcloud/occ' ... def sync_notebook(path: str, **_): ... refresh_nextcloud() def refresh_nextcloud(): logger.info(run('shell.exec', f'php {occ_path} files:scan --path=/nextcloud-user/files/Notes')) logger.info(run('shell.exec', f'php {occ_path} files:cleanup'))


¡Su computadora portátil ahora está sincronizada con NextCloud y se puede acceder a ella desde cualquier cliente de NextCloud!

Automatización para analizar y guardar páginas web

Ahora que tenemos una manera de mantener nuestras notas sincronizadas en múltiples dispositivos e interfaces, exploremos cómo podemos analizar páginas web y guardarlas en nuestro cuaderno en formato Markdown; es posible que deseemos leerlas más tarde en otro dispositivo, leer el contenido sin todo el desorden, o simplemente mantener un registro persistente de los artículos que hemos leído.


Elija un cliente de notebook para que se encargue de raspar y guardar las URL. Este cliente tendrá una configuración como esta:


 # The name of your client device_id: my-client mqtt: host: your-mqtt-server port: 1883 # Uncomment the lines below for SSL/user+password authentication # port: 8883 # username: user # password: pass # tls_cafile: ~/path/to/ssl.crt # tls_version: tlsv1.2 # Specify the topics you want to subscribe here backend.mqtt: listeners: - topics: - notebook/sync # notebook/save will be used to send parsing requests - notebook/save # Monitor the local repository copy for changes backend.file.monitor: paths: # Path to the folder where you have cloned the notebook # git repo on your client - path: /path/to/the/notebook recursive: true # Ignore changes on non-content sub-folders, such as .git or # other configuration/cache folders ignore_directories: - .git - .obsidian # Enable the http.webpage integration for parsing web pages http.webpage: enabled: true # We will use Pushbullet to send a link to all the connected devices # with the URL of the newly saved link, but you can use any other # services for delivering notifications and/or messages - such as # Gotify, Twilio, Telegram or any email integration backend.pushbullet: token: my-token device: my-client pushbullet: enabled: true


Cree un entorno a partir de este archivo de configuración:


 $ platyvenv build -c config.yaml


Asegúrese de que, al final del proceso, tenga instalados los ejecutables de node y npm : la integración de http.webpage usa la API de Mercury Parser para convertir páginas web a Markdown.


Luego, copie la carpeta de scripts creada anteriormente en <environment-base-dir>/etc/platypush/scripts . Ahora queremos agregar un nuevo script (llamémoslo, por ejemplo, webpage.py ) que está a cargo de suscribirse a nuevos mensajes en notebook/save y usar la integración http.webpage para guardar su contenido en formato Markdown en la carpeta del repositorio. Una vez que el archivo analizado esté en el directorio correcto, la automatización creada previamente se encargará de sincronizarlo con el repositorio de git.


 import logging import os import re import shutil import tempfile from datetime import datetime from typing import Optional from urllib.parse import quote from platypush.event.hook import hook from platypush.message.event.mqtt import MQTTMessageEvent from platypush.procedure import procedure from platypush.utils import run logger = logging.getLogger('notebook') repo_path = '/path/to/your/notebook/repo' # Base URL for your Madness Markdown instance markdown_base_url = 'https://my-host/' @hook(MQTTMessageEvent, topic='notebook/save') def on_notebook_url_save_request(event, **_): """ Subscribe to new messages on the notebook/save topic. Such messages can contain either a URL to parse, or a note to create - with specified content and title. """ url = event.msg.get('url') content = event.msg.get('content') title = event.msg.get('title') save_link(url=url, content=content, title=title) @procedure def save_link(url: Optional[str] = None, title: Optional[str] = None, content: Optional[str] = None, **_): assert url or content, 'Please specify either a URL or some Markdown content' # Create a temporary file for the Markdown content f = tempfile.NamedTemporaryFile(suffix='.md', delete=False) if url: logger.info(f'Parsing URL {url}') # Parse the webpage to Markdown to the temporary file response = run('http.webpage.simplify', url=url, outfile=f.name) title = title or response.get('title') # Sanitize title and filename if not title: title = f'Note created at {datetime.now()}' title = title.replace('/', '-') if content: with open(f.name, 'w') as f: f.write(content) # Download the Markdown file to the repo filename = re.sub(r'[^a-zA-Z0-9 \-_+,.]', '_', title) + '.md' outfile = os.path.join(repo_path, filename) shutil.move(f.name, outfile) os.chmod(outfile, 0o660) logger.info(f'URL {url} successfully downloaded to {outfile}') # Send the URL link_url = f'{markdown_base_url}/{quote(title)}' run('pushbullet.send_note', title=title, url=link_url)


Ahora tenemos un servicio que puede escuchar los mensajes entregados en notebook/save . Si el mensaje contiene algún contenido de Markdown, lo guardará directamente en el cuaderno. Si contiene una URL, utilizará la integración http.webpage para analizar la página web y guardarla en el cuaderno. Lo que necesitamos ahora es una forma de enviar mensajes fácilmente a este canal mientras navegamos por la web. Un caso de uso común es aquel en el que está leyendo un artículo en su navegador (ya sea en una computadora o en un dispositivo móvil) y desea guardarlo en su computadora portátil para leerlo más tarde a través de un mecanismo similar al conocido botón Compartir . . Vamos a dividir este caso de uso en dos:


  • El maletín de sobremesa (o portátil)
  • la funda del movil

Compartir enlaces desde el escritorio

Si está leyendo un artículo en su computadora personal y desea guardarlo en su computadora portátil (por ejemplo, para leerlo más tarde en su dispositivo móvil), puede usar la extensión del navegador Platypush para crear una acción simple que envíe su pestaña actual al notebook/save canal MQTT.


Descargue la extensión en su navegador ( versión Firefox, versión Chrome ); más información sobre la extensión del navegador Platypush está disponible en un artículo anterior . Luego, haga clic en el ícono de la extensión en el navegador y agregue una nueva conexión a un host Platypush; podría ser su propia máquina o cualquiera de los clientes portátiles que haya configurado.


Nota al margen: la extensión solo funciona si la máquina Platypush de destino tiene backend.http (es decir, el servidor web) habilitado, ya que se utiliza para enviar mensajes a través de la API de Platypush. La configuración anterior no requería esto, pero ahora puede seleccionar uno de los dispositivos para exponer un servidor web simplemente agregando una sección backend.http al archivo de configuración y enabled: True (de manera predeterminada, el servidor web escuchar en el puerto 8008).

Primera pantalla de la extensión web Platypush

Segunda pantalla de la extensión web Platypush


Luego, desde el panel de configuración de la extensión, seleccione su host -> Ejecutar acción. Espere a que se llene la barra de autocompletar (la primera vez puede demorar un poco, ya que tiene que inspeccionar todos los métodos en todos los paquetes habilitados) y luego cree una nueva acción mqtt.publish que envíe un mensaje con la URL actual al notebook/save . notebook/save canal:


Acción de extensión para guardar URL


Haga clic en el botón Guardar acción en la parte inferior de la página, asigne un nombre a su acción y, opcionalmente, un icono, un color y un conjunto de etiquetas. También puede seleccionar una combinación de teclas entre Ctrl+Alt+0 y Ctrl+Alt+9 para ejecutar automáticamente su acción sin tener que agarrar el mouse.


Ahora navegue a cualquier página web que desee guardar, ejecute la acción (ya sea haciendo clic en el ícono de la extensión y seleccionándolo o a través del atajo de teclado) y espere un par de segundos. Pronto debería recibir una notificación Pushbullet con un enlace al contenido analizado, y el repositorio también debería actualizarse en todos sus dispositivos.

Compartir enlaces desde dispositivos móviles

Una manera fácil de compartir enlaces a su computadora portátil a través de un dispositivo Android es aprovechar Tasker con el complemento AutoShare y elegir una aplicación como MQTT Client que viene con una integración de Tasker. A continuación, puede crear una nueva intención de AutoShare denominada, por ejemplo, Guardar URL , crear una tarea Tasker asociada a ella que utilice la integración del cliente MQTT para enviar el mensaje con la URL al tema MQTT correcto. Cuando esté navegando por una página web que le gustaría guardar, simplemente haga clic en el botón Compartir y seleccione Comando AutoShare en la ventana emergente, luego seleccione la acción que ha creado.


Sin embargo, aunque realmente aprecio las funciones proporcionadas por Tasker, su ecosistema y el desarrollador detrás de él (lo he estado usando durante más de 10 años), estoy en camino de alejar cada vez más mi automatización de él. . En primer lugar, porque es una aplicación paga con servicios pagos, y el objetivo de configurar toda esta automatización es tener la misma calidad de un servicio pago sin tener que pagar: lo alojamos, lo poseemos. En segundo lugar, no es una aplicación de código abierto y es notablemente complicado migrar configuraciones entre dispositivos.


Termux también proporciona un mecanismo para intentos y ganchos , y podemos crear fácilmente un intento de compartir para el cuaderno creando un script en ~/bin/termux-url-opener . Asegúrese de que el archivo binario sea ejecutable y que tenga instalado Termux:GUI para admitir widgets visuales:


 #!/data/data/com.termux/files/usr/bin/bash arg="$1" # termux-dialog-radio show a list of mutually exclusive options and returns # the selection in JSON format. The options need to be provided over the -v # argument and they are comma-separated action=$(termux-dialog radio -t 'Select an option' -v 'Save URL,some,other,options' | jq -r '.text') case "$action" in 'Save URL') cat <<EOF | python from platypush.utils import run run('mqtt.publish', topic='notebook/save', msg={'url': '$arg'}) EOF ;; # You can add some other actions here esac


Ahora navegue a una página que desee guardar desde su dispositivo móvil, toque el botón Compartir , seleccione Termux y seleccione la opción Guardar URL . Todo debería funcionar fuera de la caja.

Envío de resúmenes RSS a su computadora portátil

Como último paso en nuestra configuración de automatización, consideremos el caso de uso en el que desea que un resumen del nuevo contenido de su fuente favorita (su periódico, revista, blog favorito, etc.) se entregue automáticamente de forma periódica a su cuaderno en formato legible.


Es relativamente fácil configurar dicha automatización con los componentes básicos que hemos implementado y la integración Platypush rss . Agrega una sección rss al archivo de configuración de cualquiera de tus clientes con la integración http.webpage . Contendrá las fuentes RSS a las que desea suscribirse:


 rss: subscriptions: - https://source1.com/feed/rss - https://source2.com/feed/rss - https://source3.com/feed/rss


Luego, reconstruya el entorno virtual ( platyvenv build -c config.yaml ) o instale manualmente la dependencia requerida en el entorno existente ( pip install feedparser ).


La integración de RSS activará un NewFeedEntryEvent cada vez que se agregue una entrada a una fuente RSS a la que esté suscrito. Ahora queremos crear una lógica que reaccione a tales eventos y haga lo siguiente:


  1. Cada vez que se crea una nueva entrada en un feed suscrito, agregue la URL correspondiente a una cola de enlaces para procesar
  2. Un cronjob que se ejecuta de forma específica recopilará todos los enlaces en la cola, analizará el contenido de las páginas web y los guardará en una carpeta de digests en el cuaderno.


Cree un nuevo script en $PREFIX/etc/platypush/scripts llamado, por ejemplo digests.py :


 import logging import pathlib import os import tempfile from datetime import datetime from multiprocessing import RLock from platypush.cron import cron from platypush.event.hook import hook from platypush.message.event.rss import NewFeedEntryEvent from platypush.utils import run from .notebook import repo_path logger = logging.getLogger('digest-generator') # Path to a text file where you'll store the processing queue # for the feed entries - one URL per line queue_path = '/path/to/feeds/processing/queue' # Lock to ensure consistency when writing to the queue queue_path_lock = RLock() # The digests path will be a subfolder of the repo_path digests_path = f'{repo_path}/digests' @hook(NewFeedEntryEvent) def on_new_feed_entry(event, **_): """ Subscribe to new RSS feed entry events and add the corresponding URLs to a processing queue. """ with queue_path_lock: with open(queue_path, 'a') as f: f.write(event.url + '\n') @cron('0 4 * * *') def digest_generation_cron(**_): """ This cronjob runs every day at 4AM local time. It processes all the URLs in the queue, it generates a digest with the parsed content and it saves it in the notebook folder. """ logger.info('Running digest generation cronjob') with queue_path_lock: try: with open(queue_path, 'r') as f: md_files = [] for url in f: # Create a temporary file for the Markdown content tmp = tempfile.NamedTemporaryFile(suffix='.md', delete=False) logger.info(f'Parsing URL {url}') # Parse the webpage to Markdown to the temporary file response = run('http.webpage.simplify', url=url, outfile=tmp.name) title = response.get('title', url) md_files.append(tmp.name) except FileNotFoundError: pass if not md_files: logger.info('No URLs to process') return try: pathlib.Path(digests_path).mkdir(parents=True, exist_ok=True) digest_file = os.path.join(digests_path, f'{datetime.now()}_digest') digest_content = f'# Digest generated on {datetime.now()}\n\n' for md_file in md_files: with open(md_file, 'r') as f: digest_content += f.read() + '\n\n' with open(digest_file, 'w') as f: f.write(digest_content) # Clean up the queue os.unlink(queue_path) finally: for md_file in md_files: os.unlink(md_file)


Ahora reinicie el servicio Platypush. En el primer inicio después de configurar la integración de rss , debería activar un montón de NewFeedEntryEvent con todo el contenido recién visto del feed suscrito.

Una vez que se ejecute el cronjob, procesará todas estas solicitudes pendientes y generará un nuevo resumen en la carpeta de su cuaderno. Dado que anteriormente configuramos una automatización para monitorear los cambios en esta carpeta, el archivo recién creado activará una sincronización de git y una solicitud de sincronización de transmisión en MQTT. Ahí tienes: tus suscripciones diarias o semanales, ¡directamente enviadas a tu cuaderno personalizado!

Conclusiones

En este artículo hemos aprendido:


  1. Cómo diseñar una arquitectura distribuida para sincronizar contenido en varios dispositivos utilizando scripts de Platypush como enlace entre un repositorio de git y un agente de MQTT.
  2. Cómo administrar un cuaderno basado en Markdown y qué opciones populares están disponibles para la visualización: Github/Gitlab, Obsidian, NextCloud Notes, Madness.
  3. Cómo instalar un entorno virtual Platypush sobre la marcha desde un archivo de configuración a través del comando platyvenv (en los artículos anteriores, me centré principalmente en las instalaciones manuales). Solo para que lo sepa, un comando platydock también está disponible para crear contenedores Docker sobre la marcha a partir de un archivo de configuración, pero dados los requisitos de hardware o las cadenas de dependencia específicas que pueden requerir algunas integraciones, el kilometraje de platydock puede variar.
  4. Cómo instalar y ejecutar Platypush directamente en Android a través de Termux. En realidad, esto es bastante grande: en este artículo específico, apuntamos a un caso de uso para la sincronización de carpetas entre dispositivos móviles y computadoras de escritorio, pero dada la gran cantidad de integraciones proporcionadas por Platypush, así como los potentes scripts proporcionados por Termux:API , es relativamente fácil. usar Platypush para configurar automatizaciones que reemplacen la necesidad de servicios pagos (y de código cerrado) como Tasker.
  5. Cómo usar la integración http.webpage para convertir páginas web en Markdown legibles.
  6. Cómo enviar enlaces a nuestra cadena de automatización a través de un navegador de escritorio (usando la extensión de navegador Platypush) o móvil (usando el termux-url-opener ).
  7. Cómo usar la integración de rss para suscribirse a fuentes y cómo conectarlo a http.webpage y cronjobs para generar resúmenes periódicos entregados a nuestro cuaderno.


Ahora debería tener algunas herramientas sólidas para construir su propio cuaderno automatizado. Algunas ideas sobre posibles seguimientos:


  1. Use su computadora portátil para administrar bases de datos (una función proporcionada por Notion) en formato CSV.
  2. Configure un mecanismo de sincronización distribuido similar para sincronizar fotos entre dispositivos.
  3. Aloje su propio wiki o sitio web basado en Markdown creado sobre una canalización de automatización de este tipo, de modo que en cada actualización, el sitio web se actualice automáticamente con el nuevo contenido.


¡Feliz piratería!


También publicado aquí