Normalmente me encontraba leyendo más de 100 tweets al día, así que pensé que usar todo ese “poder de lectura” para leer un libro mediante tweets podría ser una buena idea.

Durante el feriado del 25 de Mayo hice la primera versión de esta loca idea, y la llamé Took, una mezcla entre Twitter y Book (sí, sé que es un mal nombre 😄).

En este post explicaré cómo hice Took usando Python y Tweepy, cómo lo alojé en Heroku (y más tarde en una Raspberry Pi) y si realmente fue útil leer un libro en Twitter usando este bot.

¿Buscas el código 👀? Puedes revisar el repositorio de GitHub de Took aquí.

Table Of Contents

Codificando un bot de Twitter

En esta sección explicaré cómo funciona Took y los cambios que ha tenido desde su primera versión.

Al principio, la idea era simple: Tomar el libro que quiero twittear, poner todas las frases del libro en una lista, verificar que esas frases tengan menos de 280 caracteres y tuitear una frase cada 30 minutos. Esta lógica inicial simple sigue siendo el núcleo de Took, solo se han hecho algunos cambios menores.

Para comenzar a explicar en profundidad cómo funciona el bot, debemos ir al archivo llamado book-preprocessing.py. Este archivo se encarga del preprocesamiento del libro, lo que significa que dentro de el hay funciones de utilidades que nos ayudarán con el trabajo de dividir el libro en frases tuiteables.

La función principal aquí es tweetify_book: primero convertirá el libro en una lista de frases utilizando la función txt_to_list_of_sentences, que utiliza la función de tokenización de la biblioteca nlkt para hacer el trabajo de una manera simple y legible.

# Convierte un archivo .txt en una lista de oraciones para twittear
def txt_to_list_of_sentences(txt_name):
    with open(txt_name, 'r') as file:
        data = file.read().replace('\n', ' ')
    return nltk.tokenize.sent_tokenize(data)

Después de que txt_to_list_of_sentences genera esa lista de oraciones, la función tweetify_book pasa a verificar si la oración actual está disponible para twittear, lo que significa que verifica si la longitud actual es de 280 caracteres o menos. Si la longitud es mayor a 280 caracteres, se llamará a la función make_sentence_available_for_tweet y se volverá a tokenizar esa oración para que esté disponible para twittear.

# Toma una oración y reemplaza , por . para tokenizarla y poder twittearla.
def make_sentence_available_for_tweet(sentence):
    sentence = sentence.replace(",", ".")
    return nltk.tokenize.sent_tokenize(sentence)

Veamos un ejemplo:

Texto original:

Adell was just drunk enough to try, just sober enough to be able to phrase the necessary symbols and operations into a question which, in words, might have corresponded to this: Will mankind one day without the net expenditure of energy be able to restore the sun to its full youthfulness even after it had died of old age? Or maybe it could be put more simply like this: How can the net amount of entropy of the universe be massively decreased? Multivac fell dead and silent.

La función txt_to_list_of_sentences no hará nada con este texto porque no hay puntos para tokenizar, por lo que colocará el texto completo de 476 caracteres en la lista de oraciones. Cuando llegue el momento de la verificación, se llamará a la función make_sentence_available_for_tweet y convertirá el texto anterior en esto:

[“Adell was just drunk enough to try”, ”just sober enough to be able to phrase the necessary symbols and operations into a question which”, ”in words”, ”might have corresponded to this: Will mankind one day without the net expenditure of energy be able to restore the sun to its full youthfulness even after it had died of old age?”, ”Or maybe it could be put more simply like this: How can the net amount of entropy of the universe be massively decreased?”, ”Multivac fell dead and silent.“ ]

Ahora, las oraciones resultantes estarán listas para twittear, ya que todas tienen 280 caracteres o menos. La magia aquí está en esta línea dentro de la función make_sentence_available_for_tweet:

sentence = sentence.replace(",", ".")

Eso convierte todas las comas en puntos, lo que nos permite volver a tokenizar esa oración utilizando nltk.

En Python la tokenización se refiere a dividir un cuerpo de texto grande en líneas de texto más pequeñas. - Fuente: Tutorials Point

Cubrir cómo funciona nlkt está fuera del alcance de este artículo, si desea leer más, puede ir a nltk.org.

Después de tokenizar todas las oraciones del libro, la función tweetify_book creará un archivo .txt donde cada línea representa un tweet listo para ser enviado. Puede ver un ejemplo aquí

Ahora centrémonos en el archivo main.py, donde ocurre toda la magia.

Si miras el archivo, verás que las primeras líneas son solo constantes aburridas que se usan para guardar las claves de la API de Twitter. No cubriré cómo se puede usar la API de Twitter, así que saltaremos esta parte del código e iremos directamente al final del archivo, específicamente donde abrimos nuestro libro ya tokenizado.

Dentro del bucle while al final del main.py es donde ocurre todo. Echemos un vistazo:

# Tweets the book, one line every TIME_DELAY seconds
with open('tweetify-books/tweetify-' + BOOK_TO_TWEET, 'r') as file:
    tweets = file.readlines()
    while current_index <= len(tweets):
        original_tweet_id = get_last_tweet_id()
        client.create_tweet(text = tweets[current_index], in_reply_to_tweet_id = original_tweet_id)
        print(tweets[current_index])
        current_index= current_index + 1
        update_index(current_index)
        time.sleep(TIME_DELAY)

Básicamente el bucle se ejecutará mientras tengamos tweets pendientes por enviar. Dentro del bucle, el programa obtendrá el último id de tweet enviado utilizando la función get_last_tweet_id y lo guardará en una variable llamada original_tweet_id, ¿por qué? Porque necesitamos conectar los tweets que estamos enviando para crear un hilo en Twitter donde el libro estará disponible para leer.

# Returns last tweet id, to create a thread
def get_last_tweet_id():
    # Obtiene el último tweet (el creado antes)
    tweets = client.get_users_tweets(id=USER_ID, tweet_fields=['context_annotations','created_at','geo'], user_auth=True)

    return tweets.data[0].id

Después de eso, se enviará el tweet actual mediante tweepy, y la variable current_index se incrementará en uno. Hagamos una pequeña interrupción aquí y hablemos sobre por qué el programa debe guardar el índice actual.

La siguiente función que se llama, update_index, se encarga de guardar el índice actual del tweet más reciente en un archivo local de texto. ¿Para qué sirve esto? Bueno, necesitamos guardar la posición del último tweet enviado porque en caso de que el programa deje de funcionar necesitamos reanudar el bot desde la última oración que tuiteó, y no desde el principio.

Después de tuitear, el bot espera 30 minutos antes de repetir el proceso nuevamente. De hecho, esa es la lógica detrás de Took: un bot de Twitter simple y divertido.

Recuerda que si quieres ver el código completo, puedes consultar el repositorio de Took en GitHub aquí.

Alojando un bot de Twitter

En esta sección discutiré brevemente cómo alojé Took y las diferentes opciones que he probado.

Cuando lancé la primera versión de Took estaba usando Heroku. En ese momento pensé que sería genial para ese trabajo y lo elegí porque lo había usado en proyectos anteriores.

Heroku fue el hogar de Took durante algunos días, pero después de un tiempo decidí usar mi propia Raspberry Pi para hacer el trabajo.

Hospedar el bot usando una Raspberry fue un trabajo bastante sencillo. Solo cloné el repositorio en el dispositivo, instalé las dependencias necesarias ¡y voilà! Fue hospedado perfectamente y en ejecución 24/7.

La Raspberry Pi que utilicé para hospedar a Took
La Raspberry Pi que utilicé para hospedar Took

La Raspberry Pi y Heroku son solo dos opciones dentro del vasto mar de servicios de hospedaje que tienes para hospedar un bot. Siéntete libre de hacer tu propia investigación y encontrar la mejor opción para tu trabajo.

Reflexiones finales sobre Took

Hablemos sobre la utilidad de Took y lo que he aprendido desarrollándolo.

El desarrollo de Took comenzó el 25 de mayo de 2022, y algunos días después, el 27, tuiteé sobre la existencia de Took en mi cuenta principal. Ese tuit llamó la atención e hizo que la cuenta de Took alcanzara 80 seguidores ese día.

Después de algunas pruebas, Took comenzó a tuitear el primer libro el 15 de junio y lo terminó el 6 de agosto. Es importante decir que el bot estuvo inactivo durante muchos días debido a pruebas y mejoras, y por eso hay una brecha de tiempo en algunos tuits.

“La última pregunta” de Isaac Asimov fue el libro que Took tuiteó. El libro tiene una longitud de 398 tuits. Por favor, tenga en cuenta que Took tiene fines educativos únicamente, y nunca tuve la intención de violar ningún derecho de autor. Para futuros libros, he estado pensando en usar exclusivamente libros de Project Gutenberg.

¿Cumplió Took su objetivo?

Sí. El libro se tuiteó por completo en un solo hilo.

¿Es un hilo de Twitter una buena forma de leer un libro completo?

Seamos honestos: no. Es un poco difícil leer solo una oración a la vez, y también es difícil marcar cuál fue la última oración que leíste (puedes usar los “me gusta” de Twitter para este propósito, pero no es muy práctico). Además, ~10 usuarios dejaron de seguir al bot porque era molesto ver un nuevo tuit de Took en su línea de tiempo cada 30 minutos.

¿Fue un buen proyecto para aprender a usar la API de Twitter y hacer algo divertido?

Absolutamente sí, y a mucha gente de Reddit y Twitter le encantó la idea. ¡Te animo a que intentes hacer algo similar!

¿Va a seguir tuiteando Took otros libros?

Por el momento Took estará pausado, ¡siéntete libre de bifurcar el código y comenzar tu propio bot si quieres ver algo similar en ejecución!

¡Eso es todo! No dudes en contactarme a través de Twitter o por correo electrónico → tadeodonegana[arroba]gmail[punto]com si tenes alguna duda