domingo, 3 de maio de 2009

Bot do MSN em Python

Existem muitos serviços de DNS dinâmico, mas eles são atualizados de "tempos em tempos". O ideal seria ter alguém do outro lado pra nos dizer qual é o IP atual. Então, vamos criar um BOT para isso. Já que quase todo mundo usa MSN, vamos usar ele! :D

Primeiro, vamos usar o site http://www.cmyip.com/. Ele foi escolhido porque é o que tem menos "firulas" ao mostrar o IP. O código (arquivo commandos.py):

import httplib
import re

def getIP(msg):
h = httplib.HTTP('www.cmyip.com')
h.putrequest('GET', '/')
h.endheaders()
returncode, returnmsg, headers = h.getreply()
if returncode == 200:
f = h.getfile().readlines()[27].strip()
ip = re.compile('\d+\.\d+\.\d+\.\d+').match(f).group()
return ip
else:
return 'ops! erro interno (%s)! =/' % (returncode)

COMMAND = {
'IP': getIP,
}


Pode testar. É feio pegar a resposta na linha 27, mas funciona! :D

Agora criamos o cliente para o MSN. Usando o pymsn.

O código:

# -*- coding: utf-8 -*-

import pymsn
import pymsn.event

import gobject

import logging
#logging.basicConfig(level=logging.DEBUG) #para DEBUG =]

logging.basicConfig(level=logging.CRITICAL)

ALLOWED_CONTACTS = ('*****@hotmail.com',)

from comandos import COMMAND

# respostas a mudanças de status (do BOT)
class ClientEvents(pymsn.event.ClientEventInterface):
def on_client_state_changed(self, state):
if state == pymsn.event.ClientState.OPEN:
# mudar status para online

self._client.profile.presence_msn_object = pymsn.Presence.ONLINE, None
# definir subnick
self._client.profile.personal_message_current_media = "BOT!!", None

def on_client_error(self, error_type, error):
print "ERROR :", error_type, " ->", error

# respostas a eventos de conversação

class ClientConversation(pymsn.event.ConversationEventInterface):

# ao receber uma mensagem
def on_conversation_message_received(self, sender, message):
self._do(sender.account, message.content)

def _do(self, contact, cmd):
if contact not in ALLOWED_CONTACTS:
msg = "Hello %s! I'm a bot! :D" % contact
fmt = pymsn.TextFormat("Comic Sans MS",
pymsn.TextFormat.UNDERLINE | pymsn.TextFormat.BOLD,
'FF0000')
else:
c = cmd.split(' ')[0]
fmt = pymsn.TextFormat("Comic Sans MS",
pymsn.TextFormat.NO_EFFECT,
'000000')
try:
msg = COMMAND[c](cmd)
except KeyError:
msg = 'Bad command or filename... ;)'

fmt = pymsn.TextFormat("Comic Sans MS",
pymsn.TextFormat.BOLD,
'FF0000')
except:
msg = 'Erro interno... alguma coisa na função'
fmt = pymsn.TextFormat("Comic Sans MS",
pymsn.TextFormat.UNDERLINE | pymsn.TextFormat.BOLD,
'FF0000')

self._client.send_text_message(pymsn.ConversationMessage(msg, fmt))
return True

def on_conversation_error(self, error_type, error):
print "ERROR :", error_type, " ->", error


# "convite" para iniciar uma conversa
class Invite(pymsn.event.InviteEventInterface):
def on_invite_conversation(self, conversation):
self._conversation = ClientConversation(conversation)


class Client(pymsn.Client):
def __init__(self, account):
server = ('messenger.hotmail.com', 1863)
self.account = account

pymsn.Client.__init__(self, server)
self._event_handler = ClientEvents(self)
self._invite_handler = Invite(self)
gobject.idle_add(self._connect)

def _connect(self):
print 'connecting...'

self.login(*self.account)

def main():
import gobject
mainloop = gobject.MainLoop(is_running=True)

# aqui vão os dados da conexão: melhorar!
cl = Client(('bill@hotmail.com', 'Linux@PC'))
cl._connect()
while mainloop.is_running():
mainloop.run()

if __name__ == '__main__':
main()



Pode-se ver algumas validações. No exemplo, só vai responder para a minha conta no MSN. Para os outros, nada. E ainda é possível expandir através do arquivo commandos.py.

Acho que ficou bom. Alguém tem sujestões de aplicações? Lembrem-se que é um protocolo em texto plano, sem nenhum tipo de segurança das informações transmitidas...


Fontes:
http://ubuntuforums.org/showthread.php?t=1052281
arquivo test.py do pacote python-msn :)
http://madpython.com/pymsn/module-tree.html
Minha cabeça e ociosidade

Para deixar o código python legível no blog:
http://aima.cs.berkeley.edu/python/py2html.html (ainda tem que melhorar, precisei alterar o HTML pra poder colocar aqui... mas ajuda)