Tuitbot para enfrentar trolls

Parece que hay un troll en tuiter que me escribe cada vez que menciono los #narcoindultos. Este troll es aprista y le gusta alabar a los principales líderes del APRA (ya sea por tuiter, o comentando en artículos de la revista Caretas).

Como contestar a los trolls da un poco de flojera, decidí fabricar un bot que lo haga por mí. Este bot es un programa muy simple, escrito en Python, que responde a aquellos que deciden trolear mi cuenta de tuiter @AniversarioPeru.

Para esto modifiqué un muy simple chat-bot que encontré aquí: http://pythonism.wordpress.com/2010/04/18/a-simple-chatbot-in-python/.

Este bot necesita ser entrenado, osea hay que hacerlo «leer» unos cuantos libros para que sepa qué responder a los trolls.

Decidí hacer mi bot picaresco pero inteligente. Entonces le hice leer las Tradiciones Peruanas de don Ricardo Palma (se bajan el libro de aquí).

Podría hacer otro bot que tenga lenguaje un poco achorado. Para eso, su entrenamiento consistiría en hacerlo leer las columnas antiguas del malapalabrero.

En resumen, este bot busca si algún troll ha empezado a fastidiar por tuiter. Si esto es cierto, responderá al troll con frases sacadas de las Tradiciones Peruanas. Este bot es muy simple y las respuestas no tienen relación con lo que haya escrito el troll. Incluso, las respuestas no tienen mucho sentido. Supongo que será suficiente para que el troll se canse de fastidiar.

Lo bueno de tener un tuitbot es que interactuaría con los trolls mientras yo estoy durmiendo, en la discoteca o de vacaciones. El bot se activa cada media hora, pero si la cantidad de trolls aumenta, podría activarse cada 10 minutos.

Tengo planes de ir mejorando poco a poco este bot. Por ahora usa una aproximación a la cadena Márkov para construir su respuesta. Por ejemplo podría emplear estadísticas Bayesianas para entrenar el bot. Lo podría entrenar para que emita saludos, preguntas, afirmaciones, despedidas, insultos etc. El bot podría usar una aproximación bayesiana para darse cuenta si el troll ha emitido una pregunta, o un insulto y responder como amerite.

La ventaja de la inferencia Bayesiana es que al analizar las palabras de un tuit, puede ir aumentando o disminuyendo la probabilidad que el tuit sea —por ejemplo— saludo o insulto. Asumamos que un tuit tenga las palabras «caviar + rojo» hay probabilidad que el troll esté insultando. Si además el tuit contiene signos de interrogación repetidos, existe alta probabilidad que este tuit sea un insulto. Los estadísticos bayesianos llaman a esto último «probabilidad posterior».

Idealmente el bot analizaría cada tuit y decidiría qué responder si la probabilidad posterior indica que he sido insultado. Tendría que hacer que mi bot se entrene leyendo los tuits que le endilgan los trolls a @rmapalacios.

Aquí hay un ejemplo de cómo sería una interacción entre un troll y mi tuitbot actual (en rojo las respuestas del bot).

>ola k ase
de Lima títere que el refrán: más allá esas calles. de su límite, de cucarachas. de la tía carnal; estas palabras:
>Que triste tu vida                               
esposa. de una vaca, de asiento, de la guitarra. de oro, de que le da colores--palabras que le sucede un tanto como hacemos
>mermelada que defiendes!!!                                                  
el reinado de la mazamorra, de grave en una mujer del rey. de nuestras abuelas eran una formidable invasión de plano.
>arriba alianza!
de la jerga franciscana. de la Monclova y el guardián del mando, de recibido varios de los jesuítas.
>matemáticamente podemos clasificar al mundial
de 1796 se plantaron en el Callao constituyó en su lado, de 1746, de aquel oportuno encarcelar, de una soberana paliza

Sección geek

Aqui les dejo el código completo del tuitbot. Requiere haber instalado un cliente de tuiter que se pueda usar desde una consola de comandos. En un post anterior detallé como usar el cliente llamado «t».

También requiere tener instalado Python. Y configurar el programa cron para que este tuitbot se active cada 30 minutos en búsqueda de tuits emitido por trolls (en este post explico como configurar cron).

Una vez que un troll es identificado, es necesario incluir el nombre de usuario en la lista de trolls para que el tuitbot sepa a quién dirigir su verbo florido.

Este es el programa que sirve para entrenar al bot. Se puede usar cualquier texto (de preferencia uno o más libros).


import pickle
import sys
# Modified from http://pythonism.wordpress.com/2010/04/18/a-simple-chatbot-in-python/
b = open('tradiciones_peruanas.txt')
text = []
for line in b:
line = line.strip()
for word in line.split():
text.append(word)
b.close()
textset = list(set(text))
follow={}
for w in range(len(text)-1):
check = text[w]
next_word = text[w+1]
print check
print next_word
if check[-1] not in '(),.?!':
if follow.has_key(check):
follow[check].append(next_word)
else:
follow[check] = [next_word]
a = open('lexicon-luke','wb')
pickle.dump(follow,a,2)
a.close()

Este es el programa que se encarga de recibir un mensaje y responder.


import pickle
import random
import sys
import re
# Modified from http://pythonism.wordpress.com/2010/04/18/a-simple-chatbot-in-python/
a = open('lexicon-luke','rb')
successorlist = pickle.load(a)
a.close()
avoid = ["a", "en", "el", "que", "la", "del", "esta", "de", "su", "con"]
def nextword(a):
if a in successorlist:
try:
return random.choice(successorlist[a])
except:
return ''
else:
return 'de'
def get_response(input):
response = ''
s = random.choice(input.split())
while True:
neword = nextword(s)
if neword != '':
response += neword + ' '
s = neword
if len(response) > 120:
for i in avoid:
regex = re.compile('\s%s\s*$'%i, re.I)
if regex.search(response):
response = regex.sub("", response).strip()
break
return response.strip()
if __name__ == "__main__":
speech = ''
while speech != 'quit':
speech = raw_input('>')
response = get_response(speech)
print response

view raw

lukebot.py

hosted with ❤ by GitHub

Este es el tuitbot que se encarga de leer lo que emiten los trolls y responderles via tuiter.


#!/usr/bin/env python
# -*- coding: UTF8 -*-
import codecs;
import os;
import datetime;
import re;
import bitly;
import subprocess;
import time;
import sys;
from random import choice;
import lukebot
debug = 0;
saved_tuits_file = "/data/projects/aniversario_peru/saved_tuits.txt"
# t update "mi primer tuit"
cmd2 = '/usr/local/bin/t set active AniversarioPeru'
p = subprocess.check_call(cmd2, shell=True);
def get_last_tuit(user):
if debug:
cmd = "cat debug_tuits.txt | grep -i " + user + " | head -n 1"
p = subprocess.check_output(cmd, shell=True);
else:
cmd = "/usr/local/bin/t mentions -l -c | grep -i " + user + " | head -n 1"
p = subprocess.check_output(cmd, shell=True);
if len(p) > 0:
p_ = p.split(",")
tuit_id = p_[0]
tuit_date = p_[1]
message = p.split(',"')[1]
message = message.replace('"', '')
message = re.sub("\s*@AniversarioPeru\s*", "", message, re.I)
return {
"tuit_id": tuit_id,
"tuit_date": tuit_date,
"user": user,
"message": message
}
return "none"
def is_new_tuit(tuit):
# file full of tuits
f = open(saved_tuits_file, "r")
file = f.readlines();
f.close()
is_new_tuit = "true"
for line in file:
line = line.strip()
line = line.split(",")
print line
if line[0] == tuit['tuit_id'] and line[2] == tuit['user']:
is_new_tuit = "false"
return is_new_tuit
def get_message(frases_to_reply):
return choice(frases_to_reply)
def reply(tuit):
#message = get_message(frases_to_reply)
message = lukebot.get_response(tuit['message'])
cmd = "/usr/local/bin/t reply " + tuit['tuit_id'] + " '" + message + "'"
p = subprocess.check_call(cmd, shell=True);
users = [
"addTrollUser"
]
for user in users:
tuit = get_last_tuit(user)
if tuit != "none":
# is the last tuit a new one?
if is_new_tuit(tuit) == "true":
reply(tuit)
# save tuit
f = open(saved_tuits_file, "a")
f.write(tuit['tuit_id'] + "," + tuit['tuit_date'] + "," + tuit['user'] + "\n")
f.close()
else:
print "we replied already"

view raw

tuit_chat.py

hosted with ❤ by GitHub