triptico.com

Un naufragio personal

New ActivityPub support in Gruta

I have implemented partial ActivityPub support in pygruta, the Python port of my decades-old web CMS software Gruta. It has been implemented as two big blocks; the first one is a daemon process that, getting queries redirected from the web server,

  • Answers to webfinger GET method requests,
  • Answers to ActivityPub actor (user) GET method requests,
  • Accepts (and confirms) follow commands from POST method requests and registers the follower,
  • Accepts un-following commands from POST method requests and de-registers the follower,
  • Accepts messages (direct or public) from any ActivityPub user from POST method requests and stores them in a special topic as Gruta stories.

On the other hand, a command-line tool (triggered by cron) does the following:

  • Collects a story feed (the same set of stories that would be used in an ATOM or RSS feed) and sends it to all registered followers as public ActivityPub notes.

As of now, you cannot answer to a message sent from this network. I've yet to find a way of doing it, but most probably it would be by writing a standard story and using a special destination field or searching for a mention inside the story content matching the @user@host standard way of mentioning.

It has been a very tedious work, as the documentation is poor and confusing and you never get helpful feedback from errors. I did the development against different software (Mastodon, Friendica and Pleroma), but only got it working for the first one (it's the most common out there, though).

You can follow my id @angel@triptico.com from this network.

El sueño del niño en el pasillo

Hoy he soñado que aparecía un niño en mi casa (que no sé quien era) y que estaba aterrorizado porque había visto en la cocina a una señora que tenía «los ojos largos».

El chico que volvió — Lista de temas


Ver en Youtube
  • Gustav Mahler — Feierlich und gemessen (Sinfonía nº 1, III)
  • Supertramp — Take The Long Way Home
  • The Cure — Boys Don't Cry
  • New Order — True Faith
  • Siouxsie and the Banshees — Israel
  • Bauhaus — Bela Lugosi's Dead
  • The Scavengers — Violence
  • Severed Heads — Dead Eyes Opened
  • Warpaint — Elephants
  • Apollo Sunshine — We Are Born When We Die
  • The Bolshoi — A Way II
  • Ann Hell — The Triumph of Death
  • George McCrae — Rock You Baby
  • Joe Dassin — Et Si Tu N'Existais Pas
  • Counting Crows — Mr. Jones
  • Laura Branigan — Self Control
  • Plastic Bertrand — Ça Plane Pour Moi
  • The Rolling Stones — Sympathy for the Devil
  • The Cranberries — Promises
  • Skin — Kill Everything
  • Giacomo Puccini — Intermezzo (Manon Lescaut)

A function to decode utf-8 in streaming mode

This is my C language function to decode utf-8 into wide characters, a byte at a time. It also detects encoding errors.

/**
 * mpdm_utf8_to_wc - Converts from utf8 to wchar (streaming).
 * @w: convert wide char
 * @s: temporal state
 * @c: char read from stream
 *
 * Converts a stream of utf8 characters to wide char ones. The input
 * stream is read one byte at a time from @c and composed into @w
 * until a Unicode codepoint is ready. The @s integer keeps an internal
 * state change and must be set to 0 before the stream is read. It
 * detects encoding errors; in this case, the special Unicode
 * char U+FFFD is returned.
 *
 * When 0 is returned, a new wide char is available into @w. If
 * the stream is interrupted in the middle of a multibyte character,
 * the @s state variable will not be 0.
 */
int mpdm_utf8_to_wc(wchar_t *w, int *s, char c)
{
    if (!*s && (c & 0x80) == 0) { /* 1 byte char */
        *w = c;
    }
    else
    if (!*s && (c & 0xe0) == 0xc0) { /* 2 byte char */
        *w = (c & 0x1f) << 6; *s = 1;
    }
    else
    if (!*s && (c & 0xf0) == 0xe0) { /* 3 byte char */
        *w = (c & 0x0f) << 12; *s = 2;
    }
    else
    if (!*s && (c & 0xf8) == 0xf0) { /* 4 byte char */
        *w = (c & 0x07) << 18; *s = 3;
    }
    else
    if (*s && (c & 0xc0) == 0x80) { /* continuation byte */
        switch (*s) {
        case 3: *w |= (c & 0x3f) << 12; break;
        case 2: *w |= (c & 0x3f) << 6;  break;
        case 1: *w |= (c & 0x3f);       break;
        }

        (*s)--;
    }
    else {
        *w = L'\xfffd';
        *s = 0;
    }

    return *s;
}

Sobre la creación de canales de Telegram

Hace unos días investigué para un amigo cómo crear y usar canales de Telegram y le envié este email contándole lo que he averiguado. Lo adjunto aquí para que no se pierda:

Hola. He investigado sobre los canales de Telegram y cómo automatizarlo. Como siempre todo es complicado y confuso. Te lo cuento aquí porque mañana seguro que se me ha olvidado.

Lo primero es crear un canal. Eso ya lo has hecho.

Lo segundo es crear un bot. Es una especie de usuario de Telegram que no es un usuario pero que puede postear en canales. Para crearlo hay que hablar con otro bot que se llama BotFather. Ahí hay que darle comandos tipo IRC para crear tu bot (sí, es así de confuso). Hay que mandarle el mensaje /newbot y él (como en un chat robótico) te pide el nombre del bot y el nombre del usuario del bot (no sé muy bien cuál es la diferencia ni por qué hacen falta dos nombres y por qué no puedes ponerle el mismo). Después de esto te da una cadena de texto mágica que es un identificador autenticado del bot para poder postear desde fuera.

Luego tienes que ir al canal y añadir al bot como administrador. Solo funciona si lo haces desde el móvil, desde el ordenador no lo he conseguido. Tienes que buscar el nombre del bot desde la lupa porque no te sale automáticamente tu lista de bots.

Una vez hecho esto un par de veces (a la primera no me ha funcionado), ya le puedes mandar comandos al bot por HTTP para postear en aquellos canales en los que está autorizado. Esto se hace con una query como la que sigue:

 https://api.telegram.org/botIDENTIFICADOR_DEL_BOT/sendMessage?chat_id=@CANAL&text=Hola%20caraculo

Esto se puede hacer directamente con el comando curl del Linux o desde algún programa, es una petición HTTP normal.

Eso publica en modo texto. Hay un modo especial HTML que es MUY limitado y solo acepta negrilla, cursiva, enlaces y poco más. Con eso hay que montárselo todo. En concreto, NO acepta <p> para separar líneas ni títulos <h1>, <h2>, etc ni nada de eso, así que hay que hacer una especie de conversión (no es que ignore los tags, es que casca y te rechaza el envío).

Leyendo por ahí he descubierto que el modo HTML sí acepta cortes de párrafo pero hay que meterlos a pelo, es decir, metiendo %0A (ascii del line feed). Para añadir imágenes hay otro truco, que es ponerlas como enlaces (NO como imágenes). Si dejas el texto del enlace vacío o con un espacio lo ignora. La gente dice que le pone un carácter Unicode especial que es el "separador que no ocupa espacio" pero a mí me lo rechaza.

Este comando funciona y genera una entrada más o menos bonita:

 curl 'https://api.telegram.org/botBLA_BLA_BLA/sendMessage?chat_id=@nutriguia&parse_mode=HTML&disable_web_page_preview=false&text=<b>Title</b>%0AThis is a <i>test</i>. <a href="https://triptico.com/img/mp.png">·</a>'

Así que se podría hacer un programita que instalado en un cron coja, por ejemplo, el feed ATOM o RSS de Nutriguía, magree un poco las entradas, las deje aceptables para el Telegram y las envíe con sendas peticiones HTTP como las que te he comentado arriba.

Quizá alguien ha hecho ya ese programa, no he investigado más allá.

Nada es lo que parece

Esto ocurrió. Un profesor dijo globo ocular. En el margen de un libro yo dibujé esto:

Se lo enseñé a un compañero y dije:

—¿Sabes qué es?

Y él me dijo:

—Pues claro. Una visión global.

The sprites of Freaks!

These are the sprites from the Freaks! 1993 game as animated GIFs for pixel-art lovers to enjoy.

Artwork by Ángel Ortega and Antonio Guerrero.

Tasso, Lamento e Trionfo

Tasso, Lamento e Trionfo es un relato sobre la pérdida, la injusticia y la belleza en el que se narra la lucha por la supervivencia de una institutriz caída en desgracia en los días anteriores al estreno del poema sinfónico que Franz Liszt compuso para conmemorar el centenario del nacimiento de Johann Wolfgang von Goethe.

Está incluido en la antología Tormenta e ímpetu, un libro de Tinta Púrpura Ediciones que toma el surgimiento del romanticismo como inspiración. Esta es la sinopsis:

«Amor y pasión. Sensaciones, sentimientos y emociones desatados. Tormenta e ímpetu. Sturm und Drang. Durante la segunda mitad del siglo XVIII, varios autores se revolvieron contra el racionalismo imperante. Su rechazo al estilo neoclásico del momento provocó el nacimiento de lo que llegaría a ser el Romanticismo. Frente a los fríos y rígidos moldes del neoclasicismo de origen francés, el Sturm und Drang alemán estableció el sentimiento y el yo, en lugar de la razón, como fuentes de inspiración. Esta antología recupera ese espíritu y la actualiza al siglo XXI. Y, ya que tanto Tinta Púrpura como el propio movimiento Sturm und Drang tienen una relación muy estrecha con la música, quisimos que esta antología también lo tenga. Cada uno de sus relatos es un particular homenaje de la palabra escrita a la música que tanto amamos; así como una interpretación personal, apasionada y desatada, de lo que el Romanticismo y, en particular, el Sturm und Drang, es para nosotros.»

El libro se puede comprar en Amazon.