Żeby nudno nie było - bot czytający bash.org.pl

Kategoria: Javascript, HTML i CSS



pbnan
16 maj 2009 - 09:54
Dotychczas brakowało mi kogoś inteligentnego do rozmowy ;] Więc postanowiłem sobie napisać bota. Jako że ani na IRC-u codziennie nie siedzę, ani pulpitu nie oglądam zbyt często (okna go zasłaniają), tak też bot miał obsługiwać jedno z moich kont Jabber.

Dobranie odpowiedniego serwera, tak dla skryptu, jak i konta IM nie jest celem tego tutorialu, więc nie wspomnę tutaj w ogóle o jakości i stabilności serwera **** ;)

Gdy przychodzi co do czego, uczeń mistrza Pythona ma do wyboru kilka ostrzy - xmpppy, jakiś pyxmpp oraz jabber.py. W ciągu ostatniego roku korzystałem chyba z większości z nich, niemniej jednak do tej pory nie wyrobiłem sobie konkretnego zdania na ich temat. Z pewnością python-xmpp z Launchpadu nie jest dobrym wyborem - nie ma określonej licencji.

Do tego "projektu" wybrałem xmpppy - ma całkiem fajną dokumentację.

Jeszcze słówko o założeniach: przy uruchomieniu jak i zmianie godziny, bot ma wysyłać do wszystkich w swoim rosterze wylosowany cytat z bash.org.pl.

Klasa ogólnie będzie miała postać:
class Client: def __init__(self): ... def send_content(self): ...

Na początku zdefiniujmy parę przydatnych zmiennych. Ich znaczenie jest, mniemam, przejrzyste.
class Client: login = "bot" domain = "example.org" password = "abecadlo_z_pieca_spadlo"

Przenieśmy się teraz do metody __init__.
self.jid = xmpp.JID(node=self.login, domain=self.domain, resource="Botownica") self.client = xmpp.Client(server=self.jid.getDomain(), debug=[]) self.client.connect() self.client.auth(self.jid.getNode(), self.password, self.jid.getResource()) self.client.sendInitPresence(True) self.roster = self.client.getRoster()
Cały ten "ogromny" kod odpowiada za połączenie się z serwerem, zalogowanie oraz ustawienie obecności i pobranie listy kontaktów (rostera).

Klasa xmpp.JID nie robi nic ważnego, jest w zasadzie zwykłym pojemnikiem na ustawienia danego konta, jak na przykład login czy zasób. W tym kodzie zasób ustawiliśmy na "Botownica" :)

Natomiast klasa xmpp.Client jest już ważna. Dziedziczy ona po CommonClient i pomaga ustanowić połączenie szyfrowane (całkiem automagicznie), jak również ułatwia ustawianie obecności.

W następnych liniach łączymy się z serwerem i autentyfikujemy, potem wysyłamy obecność self.client.sendInitPresence(True) - True oznacza, że po zalogowaniu pobrany zostanie roster, który sobie "bierzemy" w ostatniej linii.

file = "bash.txt" if not os.access(file, os.F_OK): import urllib2 address = "http://bash.org.pl/text" data = urllib2.urlopen(address).read() with open(file, "w") as f: f.write(data) if os.access(file, os.R_OK): self.data = open(file).read() self.data = self.data.split("%\n") else: self.data = []
Ostatnie linie w metodzie __init__. Jako że bash.org.pl udostępnia tekstową wersję swoich cytatów, pobieramy ją, ale tylko wtedy, gdy jeszcze pobrana nie została. Dlaczego? Plik waży 1.7MB, przecież nie będzie pobierany co uruchomienie się skryptu.

Gdy uporamy się z pobraniem, staramy się go przeczytać i podzielić na osobne wpisy. Na szczęście są one wszystkie odseparowane przez linię ze znakiem procentu.

Nadszedł czas na metodę send_content, która wysyła do wszystkich w rosterze losowy cytat.
message = random.choice(self.data) for i in self.roster.keys(): if i != self.login+"@"+self.domain: self.client.send(xmpp.protocol.Message(i, message))
W pierwszej linii przypisujemy do message losowy cytat. Następuje pętla przez wszystkie klucze z rostera. Muszę tutaj wytłumaczyć, dlaczego dodałem tego IFa wewnątrz. Otóż, z niewiadomych mi przyczyn, roster przykładowego konta dla mojego bota zawiera sam siebie, dlatego też trzeba go wyfiltrować. Potem zostaje wysłana wiadomość (xmpp.protocol.Message(adresat, treść)) i... To w zasadzie wszystko.

Dodatkowo podam poniżej kod, dzięki któremu co godzinę (a konkretnie co zmianę godziny) będzie wywoływana przed chwilą opisana metoda send_content:
if __name__=="__main__": c = Client() try: c.send_content() last_hour = time.strftime("%H") while 1: if time.strftime("%H")!=last_hour: c.send_content() last_hour = time.strftime("%H") time.sleep(60) except KeyboardInterrupt as x: pass
Ważny fragment jest w bloku try: najpierw następuje wysłanie początkowe, tzn. tuż po zalogowaniu. Następnie co minutę, w pętli, sprawdzana jest, i porównywana z dotychczasową, godzina. Jeżeli się zmieniła, to należy wysłać cytaty oraz uaktualnić zmienną last_hour.

Życzę miłej zabawy i eksperymentowania z modułem xmpppy. A nuż powstanie nam powerful klient Jabbera w Pythonie? :)
pagenoare
20 maj 2009 - 21:13
with open(file, "w") as f:



W pythonie 2.5 nie zadziała bez importu:
from __future__ import with_statement

Pozatym, nie jest to tutorial dla początkujących, nie ma nic o potrzebnych importach (e.g. os) [:
pbnan
21 maj 2009 - 18:31
Czy napisałem, że to jest dla początkujących? :] Zasadniczo, jeśli miałbym uczyć początkujących, to wymuszałbym na nich samodzielność w rozwiązywaniu problemów - tj. w szukaniu w dokumentacji, między innymi.

Jeśli chodzi o niekompatybilność, to Python 2.6 zgłasza warning użycia przestarzałej biblioteki do SSL.

Natomiast w bloku try-except (ostatni codebox) pojawia się ponownie statement "as", który nie działa w Pythonie 2.5 - zamiast niego można użyć zwykłego przecinka.


Podobne tematy:
bash.org.pl
bash.org.pl
bash.org.pl
Bash.org.pl Zamkniety
Co zrobić żeby nic nie było białe??
Internet Mobilny W Jakiej Sieci Najtańszy I Żeby Limitów Nie Było Dużych