py-irclib dccreceive

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Crazed
User
Beiträge: 171
Registriert: Sonntag 18. Mai 2008, 11:44

Freitag 1. August 2008, 00:49

Hallo!

Ich habe jetzt probiert mit py-irclib einen kleinen IRCBot zu schreiben:

Code: Alles auswählen

#!/usr/bin/python

from ircbot import SingleServerIRCBot

class pyBot(SingleServerIRCBot):
    def __init__(self, channel, nickname, server, port=6667):
        SingleServerIRCBot.__init__(self, [(server, port)], nickname, nickname)
        self.channel = channel

    def on_welcome(self, connection, event):
        connection.join(self.channel)

    def on_privmsg(self, connection, event):
        nick = event.source().split('!')[0]
        message = event.arguments()[0]
        print '%s: %s' % (nick, message)

pybot = pyBot('#pybot', 'crackbot', 'irc.irchighway.net')
pybot.start()
Funktioniert auch. Jetzt wollte ich probieren die xdcc Funktionalität einzubauen. Also habe ich mir im Verzeichnis von py-irclib das dccrecv script angesehen. Dort sind 3 event-handler definiert in einer Klasse:

Code: Alles auswählen

class DCCReceive(irclib.SimpleIRCClient):
    def __init__(self):
        irclib.SimpleIRCClient.__init__(self)
        self.received_bytes = 0

    def on_ctcp(self, connection, event):
        args = event.arguments()[1].split()
        if args[0] != "SEND":
            return
        self.filename = os.path.basename(args[1])
        if os.path.exists(self.filename):
            print "A file named", self.filename,
            print "already exists. Refusing to save it."
            self.connection.quit()
        self.file = open(self.filename, "w")
        peeraddress = irclib.ip_numstr_to_quad(args[2])
        peerport = int(args[3])
        self.dcc = self.dcc_connect(peeraddress, peerport, "raw")

    def on_dccmsg(self, connection, event):
        data = event.arguments()[0]
        self.file.write(data)
        self.received_bytes = self.received_bytes + len(data)
        self.dcc.privmsg(struct.pack("!I", self.received_bytes))

    def on_dcc_disconnect(self, connection, event):
        self.file.close()
        print "Received file %s (%d bytes)." % (self.filename,
                                                self.received_bytes)
        self.connection.quit()

    def on_disconnect(self, connection, event):
        sys.exit(0)
Da ja alles event-driven ist habe ich gedacht ich kann einfach on_dccmsg und on_ctcp übernehmen und in meinen bot packen:

Code: Alles auswählen

#!/usr/bin/python

from ircbot import SingleServerIRCBot

class pyBot(SingleServerIRCBot):
    def __init__(self, channel, nickname, server, port=6667):
        SingleServerIRCBot.__init__(self, [(server, port)], nickname, nickname)
        self.channel = channel

    def on_welcome(self, connection, event):
        connection.join(self.channel)

    def on_privmsg(self, connection, event):
        nick = event.source().split('!')[0]
        message = event.arguments()[0]
        print '%s: %s' % (nick, message)

    def on_ctcp(self, connection, event):
        args = event.arguments()[1].split()
        if args[0] != "SEND":
            return
        self.filename = os.path.basename(args[1])
        if os.path.exists(self.filename):
            print "A file named", self.filename,
            print "already exists. Refusing to save it."
            self.connection.quit()
        self.file = open(self.filename, "w")
        peeraddress = irclib.ip_numstr_to_quad(args[2])
        peerport = int(args[3])
        self.dcc = self.dcc_connect(peeraddress, peerport, "raw")

    def on_dccmsg(self, connection, event):
        data = event.arguments()[0]
        self.file.write(data)
        self.received_bytes = self.received_bytes + len(data)
        self.dcc.privmsg(struct.pack("!I", self.received_bytes))

    def on_dcc_disconnect(self, connection, event):
        self.file.close()
        print "Received file %s (%d bytes)." % (self.filename,
                                                self.received_bytes)

pybot = pyBot('#pybot', 'crackbot', 'irc.irchighway.net')
pybot.start()
Das macht aber Probleme. Wenn der Bot nun probiert zu verbinden bekomme ich folgendes Traceback:
Traceback (most recent call last):
File "pyBot.py", line 44, in <module>
pybot.start()
File "/home/crackpod/Desktop/pyBot/ircbot.py", line 247, in start
SimpleIRCClient.start(self)
File "/home/crackpod/Desktop/pyBot/irclib.py", line 1104, in start
self.ircobj.process_forever()
File "/home/crackpod/Desktop/pyBot/irclib.py", line 229, in process_forever
self.process_once(timeout)
File "/home/crackpod/Desktop/pyBot/irclib.py", line 214, in process_once
self.process_data(i)
File "/home/crackpod/Desktop/pyBot/irclib.py", line 183, in process_data
c.process_data()
File "/home/crackpod/Desktop/pyBot/irclib.py", line 564, in process_data
self._handle_event(Event(command, prefix, target, m))
File "/home/crackpod/Desktop/pyBot/irclib.py", line 594, in _handle_event
self.irclibobj._handle_event(self, event)
File "/home/crackpod/Desktop/pyBot/irclib.py", line 326, in _handle_event
if handler[1](connection, event) == "NO MORE":
File "/home/crackpod/Desktop/pyBot/irclib.py", line 1043, in _dispatcher
getattr(self, m)(c, e)
File "pyBot.py", line 19, in on_ctcp
args = event.arguments()[1].split()
IndexError: list index out of range
Ich weiß zwar was der Fehler bedeutet, aber nicht warum er zustande kommt.
Zusätzlich verstehe ich auch das dccrecv modul nicht ganz. Man soll es laut descritpion folgendermaßen starten:
Usage: dccreceive <server[:port]> <nickname>
Aber dann hat man weder Username, noch ist man im channel.

Schade das es keine vernünftige Doku gibt. Kann mir jemand helfen?
Crazed
User
Beiträge: 171
Registriert: Sonntag 18. Mai 2008, 11:44

Freitag 1. August 2008, 09:46

Mal wieder niemand eine Antwort? Schade...

Irgendwie werden meine Fragen so selten beantwortet, ich frage mich ob ich sie falsche Stelle? Und Leonidas: Ich habe von dir ein paar Posts gelesen wo du sagtest das du diese IRC Library auch schon verwendet hast in einem Bot von dir (der aber veraltet ist).

Hast du dann vielleicht doch ein bisschen Erfahrung damit?

MfG,
CracKPod
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Freitag 1. August 2008, 11:35

Crazed hat geschrieben:Mal wieder niemand eine Antwort? Schade...

Irgendwie werden meine Fragen so selten beantwortet, ich frage mich ob ich sie falsche Stelle?
Nun, es könnte damit zusammen hängen, dass du fast ganze 9 Stunden auf eine Antwort wartest.

DCC funktioniert (AFAIK) mit CTCP-Nachrichten. Warum sollte man dafür also einen Channel benötigen? Der Bot wartet lediglich darauf, dass einem jemand über DCC eine Datei anbietet und empfängt diese dann. Und der Bot ist mit diesem Code nur darauf ausgelegt, dass es wirklich ein DCC-Befehl ist, den er als CTCP-Anfrage bekommt. Und wenn es eben keine ist, gibt es den IndexError. Und da es Netzwerke gibt, die beim Verbinden automatisch CTCP-Anfragen an die Clients schicken (VERSION, LAG, TIME beispielsweise), tritt eben genau dieser Fehler auf.
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Freitag 1. August 2008, 11:41

Crazed hat geschrieben:Mal wieder niemand eine Antwort? Schade...

Irgendwie werden meine Fragen so selten beantwortet, ich frage mich ob ich sie falsche Stelle?
Hmm, um ein Uhr posten und um zehn Uhr eine Antwort erwarten? Man muss bedenken, dass der altersdurchschnitt in diesem Forum recht niedrig liegt und wir einen eher geringen Rentneranteil haben, die am Freitag früh aufstehen und am Computer hocken. Viele schlafen um diese Zeit oder sind an der Schule/Uni.

Zu deinem Problem: der Traceback sagt doch genau wo das Problem ist. Zeile 19, das wäre dann ``args = event.arguments()[1].split()``, heißt also dass ``event.arguments()`` eine Liste zurückgibt, die kein Element am Index 1 enthält. Du musst also schauen was dort ankommt.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Crazed
User
Beiträge: 171
Registriert: Sonntag 18. Mai 2008, 11:44

Freitag 1. August 2008, 19:10

Hey, tut mir Leid ich hatte den Kopf nicht an und war schlecht gelaunt.

Auf jeden Fall habe ich das Problem mit dem DCC Dateitransfer jetzt gelöst.
Das erste Problem war das das Script immer automatisch davon ausgegangen ist das der DCC String mindestens 2 teile hat nachdem splitten, hat er aber nicht wenn man ihn in einem irc netzwerk benutzt. Das 2.te war das wenn Leerzeichen im Dateinamen vorkamen er den falschen Index hatte. Habe ich beides berichtigt.

Ich habe außerdem einen ziemlich simplen IRC Bot schreiben können der auf private Nachrichten und öffentliche Nachrichten lauscht und schaut ob das erste Wort der Nachricht ein Kommando ist das in der Konfiguration steckt.

Ist sicher nicht perfekt bzw. schön aber das ist (für den Programmierer) ein sehr einfacher weg neue Kommands zu schreiben:

pybot.py

Code: Alles auswählen

#!/usr/bin/python

from irclib import SimpleIRCClient
import config

class pyBot(SimpleIRCClient):
    def __init__(self, server, nickname, channel=None, port=6667,
                 password=None, username=None, ircname=None, localaddress="",
                 localport=0):
        '''Initiates the bot with the given arguments.'''
        
        SimpleIRCClient.__init__(self)
        self.server = server
        self.nickname = nickname
        self.channel = channel
        self.port = port
        self.password = password
        self.username = username
        self.ircname = ircname
        self.localaddress = localaddress
        self.localport = localport

        self.commands = config.Config().commands
        
    def _parse(self, string, connection, event):
        command = string.split()[0]
        user = event.source().split('!')[0]
        
        if self.commands.has_key(command):
            self.commands[command](self, connection, event)
            
    def start(self):
        '''Starts the bot and connects to the network.'''
        SimpleIRCClient.connect(self, self.server, self.port, self.nickname,
                                self.password, self.username, self.ircname,
                                self.localaddress, self.localport)
        SimpleIRCClient.start(self)
        
    ########## Event Handler ##########
    def on_welcome(self, connection, event):
        '''Handles the welcome event.'''
        if self.channel: connection.join(self.channel)
        
    def on_privmsg(self, connection, event):
        '''Handles the privmsg event.'''
        nick = event.source().split('!')[0]
        message = event.arguments()[0]
        print '%s: %s' % (nick, message)
        
        self._parse(message, connection, event)
        
    def on_pubmsg(self, connection, event):
        '''Handles the pubmsg event.'''
        nick = event.source().split('!')[0]
        message = event.arguments()[0]
        print '%s: %s' % (nick, message)
        
        self._parse(message, connection, event)
config.py

Code: Alles auswählen

#!/usr/bin/python

import sys

class Config(object):
    def __init__(self):
        self.users = {'crackpod':'test123', 'gehirnkompost':'passwd123'}
        self.authorized = []
        self.commands = {'!login':self.login, '!logout':self.logout,
                         '!user':self.get_users, '!die':self.die}

    def login(self, instance, connection, event):
        arguments = event.arguments()[0].split()[1:]
        if not len(arguments) > 0:
            return
        
        user = event.source().split('!')[0]
        password = arguments[0]
        
        if not self.users.has_key(user):
            return
        
        if self.users[user] == password:
            self.authorized.append(user)
            connection.privmsg(user, 'Successfully logged in!')
        
    def logout(self, instance, connection, event):
        user = event.source().split('!')[0]
    
        if self.authorized.count(user):
            self.authorized.remove(user)
            connection.privmsg(user, 'Succesfully logged out!')

    def get_users(self, instance, connection, event):
        user = event.source().split('!')[0]
    
        if not self.authorized.count(user):
            return
    
        for authorized in self.authorized:
            connection.privmsg(user, authorized)
            
    def die(self, instance, connection, event):
        user = event.source().split('!')[0]
        
        if not self.authorized.count(user):
            return
        
        connection.close()
        sys.exit()
Antworten