Siempre he usado geany como IDE de desarrollo ahora, me gustaría conocer uno nuevo decidí instalar Atom

Entramos como root

sudo su

y luego ejecutamos

add-apt-repository ppa:webupd8team/atom
apt-get update
apt-get install atom

En el cual, agregamos el repositorio desde lo vamos a descargar, sincronizamos las cabeceras de los repositorios y se procede a instalar.

Ya tenemos a Atom instalado, mas adelante explico como se configura extensiones y templates.

En mi caso por que tenia instalado 32 bits, pero si tienes 64bits de un entorno debian o derivado te bajas directamente el .deb

Atom 64 Bits

Lo descargas  y luego lo instalas con dpkg -i

La idea es saber que tienes todo lo necesario para levantar una instancia en django

Tenemos que instalar pyhton para eso, depende de tu Sistema Operativo en mi caso uso Elementary

aptitude install python

Luego entramos en la carpeta y creamos con el nombre de nuestro proyecto

mkdir entorno

Al siguiente paso accedemos dentro de la carpeta entorno
Ya dentro de la carpeta ejecutamos el siguiente comando para crear el entorno
virtualenv .
Installing distribute.............................................................................................................................................................................................done.
Installing pip...............done

Luego para entrar a nuestro entorno ejecutamos lo siguiente
source bin/activate

Ya dentro de nuestro entorno nos damos cuenta por lo siguiente la terminal se coloca de la siguiente manera
(entorno)julioh@mordor:~/githubJulioh/entorno$
Ahora procedemos a instalar
pip install django
Luego de instalar, creamos los archivos base
django-admin startproject entorno

Cuando realizamos este comando nos crea una carpeta llamada entorno y adentro los siguientes archivos.

??? db.sqlite3
??? entorno
?   ??? __init__.py
?   ??? __init__.pyc
?   ??? settings.py
?   ??? settings.pyc
?   ??? urls.py
?   ??? urls.pyc
?   ??? wsgi.py
?   ??? wsgi.pyc
??? manage.py

Luego por ultimo nos falta es levantar el servicio y verlo desde nuestro navegador

python manage.py runserver
Nos sale lo siguiente
Performing system checks...

System check identified no issues (0 silenced).

You have 13 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run ‘python manage.py migrate’ to apply them.

June 17, 2017 – 11:45:47
Django version 1.11.2, using settings ‘entorno.settings’
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Nos sale la siguiente imagen  It worked!
Congratulations on your first Django-powered page

No hace mucho fui víctima de cyberbulling con amenazas, insultos y elevadas menciones, retweets y favoritos en Twitter cuando el Gobierno Venezolano confundió mi nombre y profesión por alguien que según ellos es el webmaster de DolarToday.com en un programa de TV donde me dedicaron 15 minutos en la emisora del estado VTV.

Gracias a las experiencias en IRC le reste importancia pero aun persistía la oleada de interacciones que molestaban mis notificaciones en Twitter. Al principio pensé en simplemente proteger mi cuenta y dejar que esto pasara pero sentí que eso era lo que los atacantes querían, La alternativa era silenciar a estas cuentas pero dejar que le lean al mismo tiempo.

La tarea suena sencilla pero cuando tienes cientos de cuentas insultando, amenazando y cientos de cientos mas retuiteando cada minuto, se hace un poco tediosa la tarea así que decidí automatizar esto con este script:

import json
import requests
from requests_oauthlib import OAuth1
import sys
import csv
from time import sleep

whitelist = ['orvtech']

shitlist = ['amaia_roja', 'anat5', 'carollafra', 'contraofensiva',
            'correoorinoco', 'forocandanga', 'guerrillaragua',
            'hectorodriguez', 'izarradeverdad', 'jorgerpsuv',
           'jrodriguezpsuv', 'lahojillaentv', 'laradiodelsur',
            'maperezpirela', 'nicolasmaduro', 'prisciliano_alf',
            'thaivama', 'yndiratorregros', 'PatriciaDorta40']

#credentials from ghost account
consumer_key=''
consumer_secret=''
access_token_key=''
access_token_secret=''

#credentials from actual account
consumer_key2=''
consumer_secret2=''
access_token_key2=''
access_token_secret2=''

def get_retweets(tweetid):
  retweetdata={}
  handles=[]
  api_url="https://api.twitter.com/1.1/statuses/retweets/"+tweetid+".json"
  payload = {'count':'100'}
  auth = OAuth1(consumer_key, consumer_secret, access_token_key, access_token_secret)
  r = requests.get(api_url, stream=False, auth=auth, params=payload)
  try:
    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()
  except KeyboardInterrupt:
      sys.exit()
  except:
     print "OK"
  retweetsObj = json.loads(r.content)
  for y in range(len(retweetsObj)):
    handles.append(retweetsObj[y]['user']['screen_name'])
  return handles

BLOCK_URL = 'https://api.twitter.com/1.1/blocks/create.json'
MUTE_URL = 'https://api.twitter.com/1.1/mutes/users/create.json'

def getTweets(usuarios):
   api_url="https://api.twitter.com/1.1/statuses/user_timeline.json"
   payload = {'screen_name': usuarios, 'count':'1', 'trim_user':'t', 'include_rts':'false'}
   auth = OAuth1(consumer_key, consumer_secret, access_token_key, access_token_secret)
   r = requests.get(api_url, stream=False, auth=auth, params=payload)
   statusObj = json.loads(r.content)
   return statusObj[0]['id']

def act_on_handle(api_url, auth, payload):
    try:
       r = requests.post(api_url, stream=False, auth=auth, params=payload)
       msgs = json.loads(r.content)
       print r.headers['status'] , payload
       if r.headers['x-rate-limit-remaining'] and r.headers['x-rate-limit-remaining'] == "0":
          print('We reached rate limit for {url}'.format(url=api_url))
          print('Try again at {reset}'.format(reset=r.headers["x-rate-limit-reset"]))
          sys.exit()
    except KeyboardInterrupt:
       sys.exit()
    except:
       pass

def main():
    for cuenta in shitlist:
       try:
         tweet = str(getTweets(cuenta))
         print "\nAnalizyng retweeters of" , cuenta , "TweetID:", tweet
         print "https://twitter.com/" + cuenta + "/status/" + tweet
         HANDLES = get_retweets(tweet)
         for user in HANDLES:
            if user not in whitelist:
              auth2 = OAuth1(consumer_key2, consumer_secret2, access_token_key2, access_token_secret2)
              payload = "screen_name=" + user
              #act_on_handle(BLOCK_URL, auth2, payload)
              act_on_handle(MUTE_URL, auth2, payload)
              sleep(1)
            else:
              print "YOU FOLLOW " , user , "WHO RETWEETED" , cuenta
              sleep(40)
         sleep(60)
       except KeyboardInterrupt:
         sys.exit()
       except:
         pass

if __name__ == '__main__':
    main()

Este script usa dos cuentas, La cuenta que esta siendo atacada la cual va a ser protegida y una segunda cuenta que es la que usamos para ver las interacciones de las cuentas que nos están atacando. Al momento de escribir este articulo, no es necesario usar la segunda cuenta pero en un futuro twitter podría prohibir que uno viera las interacciones de tweets de cuentas que no tiene en mudo o bloqueadas por el API así que decidí prepararme para eso. Como siempre los OAuth tokens y llaves las pueden obtener registrando sus apps en apps.twitter.com.

El programa lo que hace es buscar todos los tweets de una lista de cuentas que sabemos nos atacan y tomar acción contra quienes retuitean estos tweets, las acciones pueden ser bloquear y/o enmudecer.

Tras dejarlo correr un par de ciclos el resultado fue total normalidad en mi cuenta de Twitter. Ayudo un poco que los clusters de cuentas automatizadas fueron reportados y suspendidas de Twitter un par de días después.

Para la versión mas reciente de este programa, colaborar y reportar bugs pueden visitar el mi repositorio en GitHub.

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
Si estás utilizando la librería python-oauth para autenticarte contra un servicio por medio de OAuth y te consigues con el error:
AttributeError: 'module' object has no attribute 'OAuthSignatureMethod_HMAC_SHA1'

Entonces tengo la solución para ti.

El problema es que la clase no está en el módulo oauth, sino en oauth.oauth. Entonces, en lugar de hacer:
import oauth

Prueba con:
try:
import oauth.oauth
except:
import oauth

De nada ;)

Referencias:
Singleton es un patrón de diseño cuya función es evitar que un objeto pueda ser instanciado más de una vez. En este post traigo una receta simple para implementar el patrón singleton en Python.

En esta implementación utilizaremos un archivo de bloqueo (lock file) para indicar si la aplicación está en ejecución o no. Python cuenta con el módulo fcntl que nos proporciona una interfaz bastante cómoda para el control de archivos pero está disponible solo para Linux/Unix, eso implica que debemos usar medidas alternativas para Windows.

Detectar SO e importar módulos

Lo primero que debemos hacer es detectar el sistema operativo y ejecutar los import correspondientes para cada caso. Usaremos además el módulo tempfile para generar el lock file como un archivo temporal del sistema.
#!/usr/bin/python2
# -*- coding: utf-8 -*-

import os
import sys
import tempfile

OS = None
if sys.platform.startswith('linux'):
OS = 'linux'
import fcntl
elif sys.platform.startswith('win32'):
OS = 'windows'


Definir la clase Singleton

Después de detectar el sistema operativo definimos la clase Singleton. Básicamente, esta clase será la encargada de crear el lock file al inicio o generar una advertencia y termina la ejecución en caso de que el archivo ya exista (es decir, que ya existe una instancia de la aplicación en ejecución).
class Singleton:
def __init__(self):
# Variable para almacenar el file descriptor
self.fd = None
# Ruta para el lock file en la carpeta temporal del sistema
self.filepath = os.path.abspath(os.path.join(tempfile.gettempdir(),
'myapp.pid'))

if OS == 'linux':
# Para el caso de linux usamos el módulo fcntl para crear el archivo
# y bloquearlo automáticamente. Si la operación falla es porque el
# archivo ya existe y está bloqueado.
self.fd = open(self.filepath, 'w')
try:
fcntl.lockf(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
self.__exit()
elif OS == 'windows':
try:
# Para el caso windows simplemente creamos el archivo "a mano",
# pero verificamos primero si el archivo existe e intentamos
# removerlo (para casos en que la ejecución previa haya sido
# interrumpida)
if os.path.exists(self.filepath):
os.unlink(self.filepath)
self.fd = os.open(self.filepath, os.O_CREAT|os.O_EXCL|os.O_RDWR)
except OSError, err:
if err.errno == 13:
self.__exit()

def __del__(self):
# Para el caso de windows también debemos destruir el archivo "a mano"
# al finalizar la ejecución del programa.
if OS == 'windows':
if self.fd:
os.close(self.fd)
os.unlink(self.filepath)

def __exit(self):
print 'Ya hay una instancia en ejecución. Saliendo'
sys.exit(-1)
En el __init__ se observa que creamos una variable para almacenar el file descriptor (fd), luego generamos una ruta para el lock file. Posteriormente creamos un lock file con el módulo fcntl (en el caso linux) o creamos un archivo regular (para el caso windows). Adicionalmente, en el caso windows necesitamos hacernos cargo del archivo al finalizar la ejecución, para eso sobreescribimos el método __del__ y colocamos nuestro código. Adicionalmente tenemos la función __exit(), que es la encargada de detener la ejecución del programa de forma elegante.

Clase de pruebas

Con los pasos anteriores tenemos lista nuestra implementación simple del patrón singleton. Ahora, ¿Cómo la usamos? Creamos una clase (por ejemplo MyApp) que herede de singleton y ponemos un bucle infinito para que se mantenga haciendo "algo".
class MyApp(Singleton):
def __init__(self):
Singleton.__init__(self)
print 'Ejecutando MyApp'
# Creamos un bucle infinito solo para mantener la aplicación en
# ejecución
while 1:
continue


¿Cómo se vería nuestro script?

El código completo de nuestro script se vería así:
#!/usr/bin/python2
# -*- coding: utf-8 -*-

import os
import sys
import tempfile

OS = None
if sys.platform.startswith('linux'):
OS = 'linux'
import fcntl
elif sys.platform.startswith('win32'):
OS = 'windows'

class Singleton:
def __init__(self):
# Variable para almacenar el file descriptor
self.fd = None
# Ruta para el lock file en la carpeta temporal del sistema
self.filepath = os.path.abspath(os.path.join(tempfile.gettempdir(),
'myapp.pid'))

if OS == 'linux':
# Para el caso de linux usamos el módulo fcntl para crear el archivo
# y bloquearlo automáticamente. Si la operación falla es porque el
# archivo ya existe y está bloqueado.
self.fd = open(self.filepath, 'w')
try:
fcntl.lockf(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
self.__exit()
elif OS == 'windows':
try:
# Para el caso windows simplemente creamos el archivo "a mano",
# pero verificamos primero si el archivo existe e intentamos
# removerlo (para casos en que la ejecución previa haya sido
# interrumpida)
if os.path.exists(self.filepath):
os.unlink(self.filepath)
self.fd = os.open(self.filepath, os.O_CREAT|os.O_EXCL|os.O_RDWR)
except OSError, err:
if err.errno == 13:
self.__exit()

def __del__(self):
# Para el caso de windows también debemos destruir el archivo "a mano"
# al finalizar la ejecución del programa.
if OS == 'windows':
if self.fd:
os.close(self.fd)
os.unlink(self.filepath)

def __exit(self):
print 'Ya hay una instancia en ejecución. Saliendo'
sys.exit(-1)

class MyApp(Singleton):
def __init__(self):
Singleton.__init__(self)
print 'Ejecutando MyApp'
# Creamos un bucle infinito solo para mantener la aplicación en
# ejecución
while 1:
continue

if __name__ == '__main__':
app = MyApp()


Probando el singleton

Para probarlo abrimos un terminal, nos colocamos en la carpeta donde esté ubicado nuestro script y lo ejecutamos por primera vez. Eso nos dará como resultado algo como:
$ python myapp.py 
Ejecutando MyApp


Abrimos una terminal nueva (sin cerrar la terminal anterior) e intentamos ejecutar la aplicación por segunda vez. Eso nos devolverá:
$ python myapp.py 
Ya hay una instancia en ejecución. Saliendo
¡Y voilá! Logramos que un script de Python pueda ser ejecutado una sola vez.

Hay implementaciones más complejas que almacenan el ID del proceso dentro del lock file y cada vez que se intenta ejecutar una nueva instancia se lee el ID y se verifica que realmente exista un proceso en ejecución con ese identificador. Pero como dije al principio, esta es una receta simple, así que as implementaciones más complejas las dejamos como tareas para el lector ;)