La necesidad de un en línea segundo cerebro Cuando 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. Evernote 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ó , y 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. pérdidas de datos brechas de seguridad controversias de privacidad 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: 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. En la mayoría de los casos, debe pagar una tarifa mensual o anual por algo tan simple como almacenar y sincronizar texto. 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. 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í . ¿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 $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": Obsidian cobra 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. Cada dispositivo debe tener una copia local de mi computadora portátil y debe ser sencillo sincronizar los cambios entre estas copias. 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 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. el blog de Platypush 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. 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. 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. 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. 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. 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: 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: El 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. seguimiento del historial : la inserción de nuevo contenido en sus notas se puede asignar a un , la sincronización de nuevo contenido en otros dispositivos se puede asignar a un . Fácil sincronización git push git pull 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. Interfaces : exportar su computadora portátil a otro dispositivo es tan simple como ejecutar un . Fácil de importar y exportar git clone : 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. Flexibilidad de almacenamiento 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. : 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. Pros : en realidad no es 100% autohospedado, ¿no es así? :) Contras Aloje una instancia de Gitlab usted mismo. : 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. Pros : 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. Contras 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 : 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. Pros : 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). Contras 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 y sus dependencias: Madness $ sudo apt install ruby-full $ gem install madness Tome nota de dónde se instaló el ejecutable y cree un nuevo archivo de servicio systemd de usuario en para administrar el servidor en su carpeta de repositorio: madness ~/.config/systemd/user/madness.service [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 y debería ver la interfaz de Madness con sus archivos de Markdown. http://host:9999 Puede configurar fácilmente un o un para exponer el servidor fuera de la red local. proxy inverso nginx túnel SSH 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 para la mayoría de mis proyectos, pero , o cualquier otro corredor deberían funcionar. Mosquitto RabbitMQ Aedes 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 desde su administrador de paquetes favorito; el proceso de instalación también debe crear un archivo de configuración en . En el caso de una configuración SSL con usuario y contraseña, normalmente configuraría las siguientes opciones: mosquitto /etc/mosquitto/mosquitto.conf # 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 para conectarse al corredor y verificar que todo funcione. MQTT Explorer 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: 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 para decirles a los otros clientes que deben sincronizar sus copias del repositorio. notebook/sync Cuando un cliente recibe un mensaje en 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. notebook/sync Cuando un cliente específico (que estará a cargo de extraer las URL y agregar nuevo contenido remoto) recibe un mensaje en el tema con una URL adjunta, el contenido de la página web asociada se analizará y guardará en el cuaderno ( función "Guardar URL"). notebook/save 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: ( y ), utilizado para suscribirse para sincronizar/guardar temas y enviar mensajes al intermediario. mqtt backend complemento backend, utilizado para monitorear los cambios en las carpetas locales. file.monitor [Opcional] o una forma alternativa de enviar notificaciones a otros dispositivos (como , , , ). Usaremos esto para notificar a otros clientes cuando se agregue contenido nuevo. pushbullet telegram twilio gotify mailgun [Opcional] la integración de , que se usa para raspar el contenido de una página web a Markdown o PDF. http.webpage Comience creando un archivo con sus integraciones: config.yaml # 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 . Cree una estructura para almacenar sus scripts en (una copia del archivo ya debería estar allí en este punto). La estructura se verá así: $PREFIX $PREFIX/etc/platypush config.yaml $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 , 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. Termux Primero, instale la aplicación en su dispositivo Android. Opcionalmente, también puede instalar las siguientes aplicaciones: Termux : 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:API : para iniciar servicios como Redis y Platypush en el momento del arranque sin tener que abrir primero la aplicación Termux (recomendado). Termux:Boot : para agregar scripts (por ejemplo, para iniciar manualmente Platypush o sincronizar el portátil) en la pantalla de inicio. Termux:Widget : para agregar soporte para elementos visuales (como diálogos y widgets para compartir contenido) a sus scripts. Termux:GUI Después de instalar Termux, abra una nueva sesión, actualice los paquetes, instale (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): termux-services $ 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 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 y luego colocar los scripts que desea ejecutar en el momento del arranque dentro de la carpeta . sv-enable Termux:Boot ~/.termux/boot Después de iniciar y establecer una contraseña, debería poder iniciar sesión en su dispositivo Android a través de SSH: sshd $ 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 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. termux-setup-storage 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 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 : redis-server /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 . Simplemente haga un en ese directorio y copie el servicio disponible en : /data/data/com.termux/files/usr/var/service cd sshd redis $ cd /data/data/com.termux/files/usr/var/service $ cp -r sshd redis Luego reemplace el contenido del archivo de en el directorio del servicio con esto: run #!/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 (almacenamiento compartido) después de reiniciar Termux. Si ese es el caso, ahora podemos clonar el repositorio del portátil en : /sdcard /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: en la secuencia de comandos debe apuntar a : si la computadora portátil se clona en el directorio de inicio del usuario, otras aplicaciones no podrán acceder a ella. repo_path notebook.py /sdcard/notebook 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 en el archivo de registro o mediante el comando . 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. tail ps Finalmente, también podemos aprovechar 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 con un script dentro llamado, por ejemplo : Termux:Shortcuts ~/.shortcuts 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 para enviar un mensaje al tema MQTT de sin para obligar a todos los clientes suscritos a extraer las últimas actualizaciones del servidor remoto. platypush.utils.run notebook/sync origin 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 y luego seleccione su script recién creado. Al hacer clic en el icono, forzará una sincronización en todos los dispositivos conectados. el acceso directo de Termux Una vez que Termux está correctamente configurado, no necesita repetir todo el procedimiento en otros dispositivos Android. Simplemente use los scripts de 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. copia de seguridad de Termux 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á , tiene muchos complementos, se basa en Markdown simple y solo necesita una carpeta local para funcionar. basada en electronjs 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! La opción NextCloud Otra buena opción para sincronizar su computadora portátil en varios dispositivos es usar una instancia de . NextCloud proporciona una que ya admite notas en formato Markdown y también viene con una aplicación de . NextCloud aplicación de notas 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 en en este directorio. repo_path notebook.py Sin embargo, tenga en cuenta que los cambios locales en la carpeta 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 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 : Notes Notes 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 y : la integración de usa la API de para convertir páginas web a Markdown. node npm http.webpage Mercury Parser Luego, copie la carpeta de creada anteriormente en . Ahora queremos agregar un nuevo script (llamémoslo, por ejemplo, ) que está a cargo de suscribirse a nuevos mensajes en y usar la integración 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. scripts <environment-base-dir>/etc/platypush/scripts webpage.py notebook/save http.webpage 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 . Si el mensaje contiene algún contenido de Markdown, lo guardará directamente en el cuaderno. Si contiene una URL, utilizará la integración 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 . . Vamos a dividir este caso de uso en dos: notebook/save http.webpage Compartir 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 para crear una acción simple que envíe su pestaña actual al canal MQTT. extensión del navegador Platypush notebook/save Descargue la extensión en su navegador ( ); más información sobre la extensión del navegador Platypush está disponible en un . 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. versión Firefox, versión Chrome artículo anterior Nota al margen: la extensión solo funciona si la máquina Platypush de destino tiene (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 al archivo de configuración y (de manera predeterminada, el servidor web escuchar en el puerto 8008). backend.http backend.http enabled: True 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 que envíe un mensaje con la URL actual al . canal: mqtt.publish notebook/save notebook/save Haga clic en el botó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. Guardar acción 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 con el complemento y elegir una aplicación como que viene con una integración de Tasker. A continuación, puede crear una nueva intención de AutoShare denominada, por ejemplo, , 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 y seleccione en la ventana emergente, luego seleccione la acción que ha creado. Tasker AutoShare MQTT Client Guardar URL Compartir Comando AutoShare 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 , y podemos crear fácilmente un intento de compartir para el cuaderno creando un script en . Asegúrese de que el archivo binario sea ejecutable y que tenga instalado para admitir widgets visuales: intentos y ganchos ~/bin/termux-url-opener Termux:GUI #!/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 , seleccione y seleccione la opción . Todo debería funcionar fuera de la caja. Compartir Termux Guardar URL 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 . Agrega una sección al archivo de configuración de cualquiera de tus clientes con la integración . Contendrá las fuentes RSS a las que desea suscribirse: rss rss http.webpage rss: subscriptions: - https://source1.com/feed/rss - https://source2.com/feed/rss - https://source3.com/feed/rss Luego, reconstruya el entorno virtual ( ) o instale manualmente la dependencia requerida en el entorno existente ( ). platyvenv build -c config.yaml pip install feedparser La integración de RSS activará un 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: NewFeedEntryEvent Cada vez que se crea una nueva entrada en un feed suscrito, agregue la URL correspondiente a una cola de enlaces para procesar 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 en el cuaderno. digests Cree un nuevo script en llamado, por ejemplo : $PREFIX/etc/platypush/scripts 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 , debería activar un montón de con todo el contenido recién visto del feed suscrito. rss NewFeedEntryEvent 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: 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. 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. Cómo instalar un entorno virtual Platypush sobre la marcha desde un archivo de configuración a través del comando (en los artículos anteriores, me centré principalmente en las instalaciones manuales). Solo para que lo sepa, un comando 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 puede variar. platyvenv platydock platydock 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 , es relativamente fácil. usar Platypush para configurar automatizaciones que reemplacen la necesidad de servicios pagos (y de código cerrado) como Tasker. Termux:API Cómo usar la integración para convertir páginas web en Markdown legibles. http.webpage 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 Cómo usar la integración de para suscribirse a fuentes y cómo conectarlo a y cronjobs para generar resúmenes periódicos entregados a nuestro cuaderno. rss http.webpage Ahora debería tener algunas herramientas sólidas para construir su propio cuaderno automatizado. Algunas ideas sobre posibles seguimientos: Use su computadora portátil para administrar bases de datos (una función proporcionada por Notion) en formato CSV. Configure un mecanismo de sincronización distribuido similar para sincronizar fotos entre dispositivos. 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í