Aquí les dejo un script que me ayuda a mi a respaldar mi librería de música desde mi laptop a mi NSLU2 que por cierto esta a unas 30 millas (48 kilómetros) de distancia. El script usa Rsync sobre SSH. Démosle un vistazo a este script al cual he salvado como rsync_itunes.sh.

#!/bin/bash
#rsync -avx --delete --exclude='iTunes/iTunes\ Media/Movies/*' --progress ~/Music/iTunes -e "ssh -p22" usuario.aqui@host.remoto.aqui:~/
#rsync -avx --progress ~/Music/iTunes -e "ssh -p22" usuario.aqui@host.remoto.aqui:~/
rsync -avx --delete --progress ~/Music/iTunes -e "ssh -p22" usuario.aqui@host.remoto.aqui:~/

Como pueden ver tengo dos lineas comentadas las cuales las descomento de acuerdo con lo que quiera hacer. La primera omite el directorio 'Movies'. La segunda no borra los archivos que he eliminado de iTunes o que ya no están en esa ubicación. La ultima que es la que no esta comentada borra y sincroniza la librería entera. Noten que -e "ssh -p22" le dice a rsync que va a ejecutar SSH por el puerto 22, útil en caso de que engan SSH corriendo en otro puerto.

Yo en particular lo tengo en ~/scripts y lo ejecuto así:

sh ~/scripts/rsync_itunes.sh

Dejar de seguir a quienes no te siguen en Twitter con este script de Python. Hace unas semanas hice un script en Python que permitía saber quienes no te seguían pero con el cambio de la API a la versión 1.1 quedo obsoleto. Entre las peticiones que he recibido aparte de ponerlo a funcionar con la API nueva era para dejar de seguir a esos que no te seguían.

El siguiente script no te dice los nombres de quienes no te siguen como lo hacia el que mencione anteriormente pero cuenta cuantos de los que tu sigues no te siguen y los deja de seguir. Antes de ejecutarlo les recomiendo que organicen las cuentas que ustedes siguen y que tal vez no los sigan en listas, por ejemplo crear una lista con todas sus fuentes de noticia o una de robots y comedia, etc.

Este es el script:

import requests
from requests_oauthlib import OAuth1
import re
from time import sleep
import operator
import sys
import os
import collections


consumer_key=''
consumer_secret=''
access_token_key=''
access_token_secret=''


def get_follower_ids():
  cursor = "-1"
  listadeIDs = []
  while cursor != '0':
    try:
      api_url='https://api.twitter.com/1.1/followers/ids.json'
      payload = {'count':'5000', 'cursor':cursor}
      auth = OAuth1(consumer_key, consumer_secret, access_token_key, access_token_secret)
      r = requests.get(api_url, stream=False, auth=auth, params=payload)
      if r.headers['x-rate-limit-remaining'] and r.headers['x-rate-limit-remaining'] == "0":
        time_out = int(r.headers["x-rate-limit-reset"]) - int(time.time())
        print("We reached rate limit for ", api_url)
        print "Try again in", time_out, "seconds"
        quit()
      IDs = json.loads(r.content)
      cursor = IDs['next_cursor_str']
      listadeIDs = listadeIDs + IDs['ids']
      sleep(1)
    except KeyError:
      print "Unable to navigate through cursors, last attempt: ", cursor
      break
  return list(set(listadeIDs))


def get_friends_ids():
  cursor = "-1"
  listadeIDs = []
  while cursor != '0':
    try:
      api_url='https://api.twitter.com/1.1/friends/ids.json'
      payload = {'count':'5000', 'cursor':cursor}
      auth = OAuth1(consumer_key, consumer_secret, access_token_key, access_token_secret)
      r = requests.get(api_url, stream=False, auth=auth, params=payload)
      if r.headers['x-rate-limit-remaining'] and r.headers['x-rate-limit-remaining'] == "0":
        print("We reached rate limit for ", api_url)
        print("Try again at", r.headers["x-rate-limit-reset"])
        quit()
      IDs = json.loads(r.content)
      cursor = IDs['next_cursor_str']
      listadeIDs = listadeIDs + IDs['ids']
      sleep(1)
    except KeyError:
      print "Unable to navigate through cursors, last attempt: ", cursor
      break
  return list(set(listadeIDs))



def get_unfollowers_info(unfollowers):
   print len(unfollowers)
   api_url='https://api.twitter.com/1.1/users/lookup.json'
   auth = OAuth1(consumer_key, consumer_secret, access_token_key, access_token_secret)
   user_objs = {}
   sleep(1)
   i = 0
   user_id = []
   for i in unfollowers:
     user_id.append(i)
     if (len(user_id) == 100):
       payload = {'user_id':user_id}
       r = requests.get(api_url, stream=False, auth=auth, params=payload)
       if r.headers['x-rate-limit-remaining'] and r.headers['x-rate-limit-remaining'] == "0":
         print("We reached rate limit for ", api_url)
           print("Try again at", r.headers["x-rate-limit-reset"])
         quit()
       tmp_user_objs = json.loads(r.content)
       for y in range(len(tmp_user_objs)):
         print tmp_user_objs[y]['screen_name'] , tmp_user_objs[y]['followers_count'] , tmp_user_objs[y]['friends_count'] , tmp_user_objs[y]['following'] , tmp_user_objs[y]['verified'], tmp_user_objs[y]['default_profile']
       user_objs = user_objs , tmp_user_objs
       user_id = []
   payload = {'user_id':user_id}
   r = requests.get(api_url, stream=False, auth=auth, params=payload)
   if r.headers['x-rate-limit-remaining'] and r.headers['x-rate-limit-remaining'] == "0":
     print("We reached rate limit for ", api_url)
     print("Try again at", r.headers["x-rate-limit-reset"])
     quit()
   tmp_user_objs = json.loads(r.content)
   user_objs = user_objs , tmp_user_objs

def unfollow_by_id(user_id):
  api_url='https://api.twitter.com/1.1/friendships/destroy.json'
  payload = {'user_id':user_id}
  auth = OAuth1(consumer_key, consumer_secret, access_token_key, access_token_secret)
  r = requests.post(api_url, stream=False, auth=auth, params=payload)



followers = get_follower_ids()
print "Followers: ", len(followers)
following = get_friends_ids()
print "Following: ", len(following)



unfollower_ids = set(following) - set(followers)
print "unfollower ids: ", unfollower_ids , len(unfollower_ids)


get_unfollowers_info(unfollower_ids)
print "About to unfollow: " , len(unfollower_ids) , "users."
sleep(10)

for user_id in unfollower_ids:
  print "Unfollowing ", user_id
  unfollow_by_id(user_id)

En este script a diferencia de los anteriores que he hecho no use el módulo python-twitter y como ven intento trabajar con el API y su rate-limit En mi caso al ejecutarlo deje de seguir a algunas cuentas de comedia, noticias y cuentas interesantes que enriquecían mi timeline.

Si el tiempo me lo permite, en la próxima versión creo que voy a agregar esos usuarios a una lista privada antes de dejarlos de seguir. He trabajado en otros scripts privados en calcular la importancia de una cuenta en base a la cantidad de seguidores, cantidad de personas que esta sigue, cantidad de tweets, edad de la cuenta y otros factores que tal vez incorpore en un futuro.

Como siempre en todos mis publicaciones que tiene que ver con Python, les agradecería mucho si me dan consejos de como mejorar este script o la lógica del mismo, Incluso pull requests al repo github.com/orvtech/Python-tools-for-twitter/blob/master/unfollow-unfollowers.py.

Sigo en mi travesía por aprender Python y que mejor forma que solucionar problemas en funciona a la privacidad.

Varias personas me comentaron lo tedioso que es eliminar mensajes privados en Twitter así que decidi hacer una herramienta que me permita automatizar todo esto.

Lo primero es crear una app en twitter visitando apps.twitter.com y otorgarle permisos de lectura, escritura y DMs. Allí mismo pueden generar el consumer_key, consumer_secret, access_token_key y el access_token_secret.

El script no es nada complejo y de hecho hay oportunidades para mejorarlo, aqui se los dejo:

# https://github.com/orvtech/Python-tools-for-twitter
import json
import requests
from requests_oauthlib import OAuth1


consumer_key='<YOUR CONSUMER KEY HERE>'
consumer_secret='<YOUR CONSUMER SECRET HERE>'
access_token_key='<YOUR ACCESS TOKEN HERE>'
access_token_secret='<YOUR ACCESS TOKEN SECRET HERE>'


def get_messages_ids():
  api_url='https://api.twitter.com/1.1/direct_messages.json'
  payload = {'count':'200', 'cursor':'-1', 'skip_status':'1'}
  auth = OAuth1(consumer_key, consumer_secret, access_token_key, access_token_secret)
  r = requests.get(api_url, stream=False, auth=auth, params=payload)
  if r.headers['x-rate-limit-remaining'] and r.headers['x-rate-limit-remaining'] == "0":
    print("We reached rate limit for ", api_url)
    print("Try again at", r.headers["x-rate-limit-reset"])
    quit()
  DMs = json.loads(r.content)
  message_ids=[]
  for x in range(len(DMs)):
    current_ids=DMs[x]['id']
    message_ids.append(current_ids)
  api_url='https://api.twitter.com/1.1/direct_messages/sent.json'
  payload = {'count':'200'}
  r = requests.get(api_url, stream=False, auth=auth, params=payload)
  if r.headers['x-rate-limit-remaining'] and r.headers['x-rate-limit-remaining'] == "0":
    print("We reached rate limit for ", api_url)
    print("Try again at", r.headers["x-rate-limit-reset"])
    quit()
  DMs = json.loads(r.content)
  for x in range(len(DMs)):
    current_ids=DMs[x]['id']
    message_ids.append(current_ids)
  return message_ids


def nuke_messages(DMs):
  for x in DMs:
    api_url='https://api.twitter.com/1.1/direct_messages/destroy.json'
    payload = {'id':x}
    auth = OAuth1(consumer_key, consumer_secret, access_token_key, access_token_secret)
    r = requests.post(api_url, stream=False, auth=auth, params=payload)


while True:
  DMs = get_messages_ids()
  if DMs and len(DMs) > 0:
    print('Deleting:', DMs)
    nuke_messages(DMs)
  else:
    print('There appears that there are no more DMs', DMs)
    break

Usa solo 2 funciones, una en la que invoca primero los primeros 200 mensajes recibidos y luego los primeros 200 mensajes enviados. Tras evaluar si en realidad existen mensajes, se le pasan los IDs de los mensajes a eliminar a la segunda función y se repite este proceso hasta que no existan mas IDs a eliminar.

Tanto las llamadas de al API para saber los IDs de los mensajes enviados como las de los mensajes enviados tiene un limite así que implemente mecanismos para monitorear si se llego al limite leyendo las cabeceras de las peticiones HTTP.

Por lo pronto pueden hacer un fork de este repositorio de github En un futuro me gustaría convertirlo en una aplicación web usando flask o algo por ese estilo.


No hace mucho hice un shell script que mostraba la fecha de creación de una cuenta de Twitter pero a las pocas semanas quedo obsoleto por la implementación del API 1.1 que requiere que este tipo de llamas al API sean autenticadas vía OAuth lo cual no es tan sencillo si solo quieres usar bash.

Esta versión esta hecha en Python y a diferencia del anterior proporciona mas información sobre la cuenta. de hecho puede procesar varias cuentas al mismo tiempo, veamos el código fuente de este programa hecho en Python:

from __future__ import print_function, unicode_literals
import requests
from requests\_oauthlib import OAuth1
from urlparse import parse\_qs

handles = raw_input('Enter twitter handles: ').split()

CONSUMER_KEY = " "
CONSUMER_SECRET = " "
OAUTH_TOKEN = " "
OAUTH_TOKEN_SECRET = " "

def get_oauth():
  oauth = OAuth1(CONSUMER_KEY,
  client_secret=CONSUMER_SECRET,
  resource_owner_key=OAUTH_TOKEN,
  resource_owner_secret=OAUTH_TOKEN_SECRET)
  return oauth

if __name__ == "__main__":
  oauth = get_oauth()
  payload={'screen_name':handles}
  r = requests.post(url="https://api.twitter.com/1.1/users/lookup.json",data=payload,auth=oauth)
  user_obj = r.json()
  for line in user_obj:
    print("Handle: " , line['screen_name'])
    print("User ID: " , line['id'])
    print("Verified: " , line['verified'])
    print("Name: " , line['name'])
    print("Account Created: " , line['created_at'])
    print("Followers: " , line['followers_count'])
    print("Following: " , line['friends_count'])
    print("Tweet Count: " , line['statuses_count'])
    print("Favorites: " , line['favourites_count'])
    print('- - - - - - - - - - -\n')

El producto de este script usando mi Twitter handle (orvtech) luce así:

Handle: orvtech
User ID: 4412471
Verified: False
Name: Oliver
Account Created: Thu Apr 12 21:35:06 +0000 2007
Followers: 5710
Following: 558
Tweet Count: 20233
Favorites: 6514


¿Como ver la información de varias cuentas?

Para ver de varias cuentas de Twitter solo debes dárselas a este programa hecho en Python, separadas por espacios en blanco, sin el símbolo de arroba como lo puedes ver aquí:

$ ./user-info.py
Enter twitter handles: orvtech twitter protected


¿Que necesito para correr este script de Python?

Para ejecutar este script necesitas registar tu aplicación y generar unas llaves en apps.twitter.com/app/new para lo que deberás iniciar sesión con tu usuario de Twitter. Una vez registrada tu aplicacion deberas asignar los valores de las constantes CONSUMER_KEY, CONSUMER_SECRET, OAUTH_TOKEN y OAUTH_TOKEN_SECRET en el código fuente del script.

Con la ayuda de cron y Python podemos pre-programar el envío de tweets sin que estos se repitan y llevar un registro de los que se han enviado. Este corto script hecho en python es mi segunda aventura con este lenguaje que poco a poco me esta enamorando.

La lógica es simple, el programador de tweets lee un archivo que contiene un tweet por linea, actualiza mi estado en twitter con el contenido de esa linea, registra los resultados en un archivo y por ultimo elimina esa linea del archivo evitando así repetir el mismo tweet. Veamos el código fuente:

#!/usr/bin/python
import twitter
import datetime

now = datetime.datetime.now()

with open('lista.txt', 'r') as f:
  first_line = f.readline()
  first_line = first_line.strip('\n')
  print first_line
f.close()

logfile = open('procesados.txt','a')
fecha=now.strftime("%Y-%m-%d_%H:%M:%S")
logfile.write(fecha + ' ' + first_line + '\n')
logfile.close()

#send tweet
api = twitter.Api(consumer_key='',
  consumer_secret='',
  access_token_key='',
  access_token_secret='')

status = api.PostUpdate(first_line)
print status.text

#removing status from queue
lines = open('lista.txt').readlines()
open('lista.txt', 'w').writelines(lines[1:])

Alimentando el script programador de tweets.

Como vieron en el código fuente, solo necesitamos proporcionarle el archivo lista.txt donde cada tweet dependiendo del tipo tiene limitaciones de longitud distintas. Si no posee un hyper vinculo el limite es de 117 caracteres excluyendo la URL mientras que si no tiene un hiper vinculo el limite es de 140 caracteres. El archivo lista.txt se vería algo así:

Just ran out of bacon, the room seems to be shrinking, please help! ... and bacon
Programador de tweets hecho en python https://orvtech.com/planificador-tweets-python.html

Automatizando el envío de tweets.

Leí un poco y se que se puede hacer un demonio y dejar que python se encargue de esto pero para comenzar decidí hacerlo usando cron. Me decidí a tuitear dos veces por día entre semana mientras que los fines de semana solo una vez. Mi crontab se ve así:

15 15  *  *  0,6  /usr/bin/python /scheduler/reader.py >> /scheduler/reader.log 2>&1
14  6  *  *  1-5  /usr/bin/python /scheduler/reader.py >> /scheduler/reader.log 2>&1
11 20  *  *  1-5  /usr/bin/python /scheduler/reader.py >> /scheduler/reader.log 2>&1

Después de varios años decidí iniciarme en el mundo de Python y jugar un poco con el API de Twitter así que decidí hacer un programa en Python que compara tu lista de seguidores con tu lista de amigos para obtener una lista de las personas que tu sigues pero no te siguen. Es decir, quien te dejo de seguir en Twitter o como dicen en la red, quien te ha dado unfollow.

Como era mi primer intento decidí usar python-twitter que por lo que leí era la mas sencilla de usar. Para instalar este paquete de Python pueden ejecutar:

pip install python-twitter

El programa para listar unfollowers es este:

#!/usr/bin/python
#orvtech_no_me_sigue.py para API 1.0
import twitter

#Prepare the needed data for twitter's oauth

api = twitter.Api(consumer_key='<YOUR_CONSUMER_KEY_HERE>',
                  consumer_secret='<YOUR_CONSUMER_KEY_HERE>',
                  access_token_key='<YOUR_TOKEN_KEY_HERE>',
                  access_token_secret='<YOUR_TOKEN_SECRET_HERE>')

#Get followers by handle
followers = api.GetFollowers()
seguidores =[]

for u in followers:
  seguidores.append(u.screen_name)

#Get friends by handle
friends = api.GetFriends()
siguiendo =[]
for f in friends:
  siguiendo.append(f.screen_name)

#Get the list of people that you follow that but that are not in the followers list
unfollowers = list(set(siguiendo) - set(seguidores))
count_uncool = len(unfollowers)
print "Total friends not following you: " + str(count_uncool)
for uncool in unfollowers:
  print uncool + " " + "https://twitter.com/" + uncool

Como ven para poder ejecutar este programa y ver sus unfollowers deberán obtener una serie de datos de dev.twitter.com para autentificar su programa contra oauth. El producto de este programa se vera algo similar a esto:

Total friends not following you: 61
planetfedora https://twitter.com/planetfedora
twitter https://twitter.com/twitter
TwitterEng https://twitter.com/TwitterEng
twittercomms https://twitter.com/twittercomms
twitterapi https://twitter.com/twitterapi
fedora https://twitter.com/fedora
make https://twitter.com/make
dickc https://twitter.com/dickc
TwitterIO https://twitter.com/TwitterIO
TwitterSF https://twitter.com/TwitterSF
safety https://twitter.com/safety

Por ahora lo que quiero es exportarlo a una aplicacion web, si tienen sugerencias me las pueden dejar en los comentarios, me gustaría un poco de orientación de como hacer de este programa una aplicacion web.


UPDATE: Junio 20 del 2013

Con la salida de el API 1.0 de Twitter el código que esta arriba queda obsoleto y rápidamente alcanza el limite de peticiones de la API. El codigo a continuación trabaja perfectamente con la API versión 1.1 siempre y cuando no tengas mas de 5000 seguidores o mas de 100 personas que no te siguen.

#!/usr/bin/python
#orvtech_no_me_sigue.py para API 1.0
import twitter

#Prepare the needed data for twitter's oauth
api = twitter.Api(consumer_key='<YOUR_CONSUMER_KEY_HERE>',
                  consumer_secret='<YOUR_CONSUMER_KEY_HERE>',
                  access_token_key='<YOUR_TOKEN_KEY_HERE>',
                  access_token_secret='<YOUR_TOKEN_SECRET_HERE>')

followers = api.GetFollowerIDs()
friends = api.GetFriendIDs()

unfollowers = list(set(friends) - set(followers))
count_uncool = len(unfollowers)
nombre = api.UsersLookup(user_id=unfollowers)

print "Total friends not following you: " + str(count_uncool)

counter = 0
for uncool in nombre:
  print nombre[counter].name + " https://twitter.com/" + nombre[counter].screen_name
  counter += 1

En estos días me toco recuperar mi VPS vía consola remota, al revisar el /var/log/messages me di cuenta de el Out of Memory Killer (OOM Killer) había hecho de las suyas al quedarse sin memoria. Luego de analizar detenidamente los logs y me di cuenta que fue una combinación de factores, Google, Yahoo y Yandex me estaban indexando tanto este blog como unos foros de autos que alojo en este servidor al mismo tiempo.

El script para descargar torrents estaba corriendo al igual que el torrent tracker y alguien me estaba haciendo flood vía IRC en freenode puede había dejado irssi conectado.

El OOM Killer tiene un algoritmo que decide que procesos son los mejores para aniquilar sin embargo es posible manipular estos valores y dejarle saber al OOM Killer que procesos prefieres que mate primero. Para automatizar esta tarea hice un script que nos ayudara con esta tarea:

for programas in SCREEN irssi top
  do for pid_of_oomk_candidate in `pidof -x $programas`
    do echo 10 > /proc/$pid_of_oomk_candidate/oom_adj
    done
 done

Como ven el script anterior agrega 10 al OOMK score a los procesos que menos me interesan permanezcan vivos cuando me quede sin memoria mientras que el siguiente hará exactamente lo contrario, mantendrá vivo un poco mas estos procesos:

for programas in mysqld portsentry iptables
  do for pid_of_oomk_candidate in `pidof -x $programas`
    do echo -5 > /proc/$pid_of_oomk_candidate/oom_adj
  done
done

echo -17 > /proc/`pidof -s sshd`/oom_adj

En este ultimo mysql portsentry y iptables tendrán cinco puntos de ventaja en relación con el resto de los candidatos y si se fijan en la ultima linea estamos asegurándonos que sshd no sea candidato bajo ninguna circunstancia.


Automatizar el ajuste del score para el OOM Killer

Ahora unifiquemos estos dos scripts y demosle un poco mas de flexibilidad, estoy seguro que hay gente que prefiere asignarle a irssi un valor distinto de screen. Lo primero que necesitaremos es un script de configuración, vamos crearlo en /etc/candidatos_oomk.conf y el contenido se debería de ver algo así:

# Programas que queremos sacrificar, entre mas alto el valor
# mas peso tienen en la lista de candidatos.
irssi 4
SCREEN 3
smtpd 2
apache2 1

# Programas que queremos salvar, estos tendrán valores negativos
portsentry -2
mysqld -5
sshd -17

El script al que le puse de nombre oomk_adj_candidatos.sh se veria algo asi:

#!/bin/bash
CONFIGURACION="/etc/candidatos_oomk.conf"
while read programas
  do  proceso=`echo $programas |grep -v \# | grep [0-9]$| awk '{print $1}'`
  ajuste=`echo $programas |grep -v \# | grep [0-9]$| awk '{print $2}'`
  if [ -z "$proceso" ] then
    continue
  else
    echo $proceso
    for pids_proceso in `/bin/pidof $proceso`
      do echo "    echo $ajuste > /proc/$pids_proceso/oom_adj"
    done
  fi done < $CONFIGURACION

El script es simple, leemos el archivo de configuracion linea por linea separamos el nombre del proceso del ajuste que le vamos a hacer guardando cada valor en las variables $proceso y $ajuste. Evaluamos si la variable $proceso esta vacía, y de ser así seguimos con la próxima linea, de lo contrario continuamos procesando esa misma linea. Obtenemos el PID del proceso usando pidof y para cada PID vamos escribir el valor de $ajuste en su archivo oom_adj dentro de /proc.

Pueden colocar este script en su /etc/rc.local o en un cronjob y recuerden que si quieren consultar la lista de candidatos y ver como están sus scrores o saber un poco mas del tema, pueden consultar este articulo: El OOM Killer y manipulación de candidatos.

El día de hoy vi un tweet que me llamo la atención, vinculaba a una pagina que explica como ocultar código php en una imagen para luego ser ejecutado como parte de un exploit. El código maligno lo ocultan en la metadata de la imagen bajo el campo 'Comments' que luego es usado como parte de una consola web llamada Weevely.

El articulo va un poco mas profundo explicando que usan la función passthru() para pasar los comandos al servidor con una petición web y __halt_compiler() para evitar que la imagen siga siendo procesada como un archivo binario.

Aunque no es nada nuevo, recientemente vi que el ocultar código malicioso en imágenes se ha puesto de moda nuevamente, aquí te explico como eliminar código oculto en imágenes de tu servidor.

Eliminar el backdoor Weevely oculto en imágenes de tu servidor.

Si tienes un blog o eres webmaster de una pagina web que acepta que los usuarios suban imágenes, puedes ejecutar este script modificandolo a tu conveniencia.

#!/bin/bash ORVTECH_DIRS="/var/www/htdocs/wordpress/wp-content/uploads/20*" # for ARCHIVO_JPG in `find $ORVTECH_DIRS -type f| grep -i jpg$ `;   do   convert -strip $ARCHIVO_JPG $ARCHIVO_JPG done

Como ven, el script recorre todas las subdirectorios de $ORVTECH_DIRS en busca de imágenes jpg, luego las pasa por imagemagick para quitarle cualquier metadata innecesaria, incluyendo los comentarios. De esta forma terminas con imágenes sin código oculto y un tanto mas ligeras.

Tengo ya unos días tratando de recordar y averiguar cuando fue que empecé a usar Twitter y solo consigo paginas que no me inspiran mucha confianza, que se ofrecen para darte esta información.

Revisando la documentación de la API de esta red social, conseguí como hacerlo aunque no esta explícitamente documentado. Esta información del usuario se envía cada vez que uno hace una petición por su timeline usando grep, awk, tr y curl fácilmente se puede ver cuando se creó la cuenta.

cuando fue creada una cuenta en twitter

Este script muestra cuando fue creada la cuenta además de cuantas personas sigue, cuantos lo siguen y cuantos twits ha publicado:

#!/bin/bash
curl -s "https://api.twitter.com/1/statuses/user_timeline.json?include_entities=true&include_rts=true&screen_name=$1&count=0" | tr ',' '\n' | grep  \"created_at\" | tail -n 1
curl -s "https://api.twitter.com/1/statuses/user_timeline.json?include_entities=true&include_rts=true&screen_name=$1&count=0" | tr ',' '\n' | grep -m2 -E 'friends_count|statuses_count|followers_count'

Como ven el script acepta un parámetro que para que funcione correctamente debe de ser el handle que en mi caso es orvtech, para ver mi información pueden ejecutar:

./twitter-creado.sh orvtech "created_at":"Thu Apr 12 21:35:06 +0000 2007" "friends_count":236 "statuses_count":5065 "followers_count":251

Estoy seguro que se puede optimizar incluso remplazar esas dos peticiones web por una sola y remplazar el tr, grep y el tail con una sola instrucción de awk pero solo quería demostrar como hacerlo. Si quieren aprender mas del API de twitter pueden visitar https://dev.twitter.com/docs.

Hoy resolvi como enviar procesos al fondo dentro de un loop en BASH, tal vez exista una forma mas limpia de hacerlo pero esta me funciono. Este será un tip mas para esa serie de post cortos pero que vale la pena agregar a los bookmarks.

Enviar procesos al fondo desde un loop en bash

El tip como tal realmente simple, solo tienes que incluir lo que quieras enviar al fondo dentro de llaves {}. Lo que quieras enviar al fondo debe lucir muy parecido a esto:

{ ping orvtech.com & }

Veamos este otro ejemplo.

for i in `echo $correos`
  do echo "trabajando en usuario $i"
  { crawler.sh -H $i run "grep $1 crawler.tmp" | awk -F\" '{print $2}' | awk -F\, '{print $1}' > usuarios.txt & }
done
jobs

Con el commando jobs puedes monitorear los procesos que se han enviado al fondo.