-
- #!/usr/bin/python
- """
- XDCC file downloader
-
- Hacked together by Gregory Eric Sanderson Turcot Temlett MacDonnell Forbes
- Requires the python package "twisted"
-
- This script connects to an IRC server and batch downloads files using the XDCC
- protocol.
-
- TODO:
- * Manage partial downloads
- * Manage unexpected DCC connections
- * Add optional port to command line
-
- License:
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
- """
-
- import sys
- import logging as log
-
- from twisted.words.protocols import irc
- from twisted.internet import reactor, protocol
- from twisted.python import log as twistedlog
-
-
- def setup_logger(logger_name, log_file, level=log.INFO):
- l = log.getLogger(logger_name)
- formatter = log.Formatter('%(asctime)s : %(message)s')
- fileHandler = log.FileHandler(log_file, mode='w')
- fileHandler.setFormatter(formatter)
- #streamHandler = log.StreamHandler()
- #streamHandler.setFormatter(formatter)
-
- l.setLevel(level)
- l.addHandler(fileHandler)
- #l.addHandler(streamHandler)
-
-
-
-
- class XdccBot( irc.IRCClient ):
-
- @property
- def nickname( self ):
- return self.factory.nickname
-
- def connectionMade( self ):
- irc.IRCClient.connectionMade( self )
- logP.info("Bot connection made")
-
- def signedOn( self ):
- logP.info("Bot has signed on")
- self.join( self.factory.channel )
-
- def joined( self, channel ):
- logP.info("Bot has joined channel %s" % channel )
- self.processXdccRequests()
-
- def processXdccRequests( self ):
-
- xdccmsg = "XDCC SEND #%s"
-
- if len( self.factory.xdccRequests ) > 0:
- number = self.factory.xdccRequests.pop(0)
- message = xdccmsg % str(number)
- print "message: ", self.factory.xdccBot, message
- self.msg( self.factory.xdccBot, message )
- else:
- reactor.stop()
-
- def privmsg( self, user, channel, msg ):
- if channel == self.nickname:
- logP.info(user, channel, msg)
- print "private: ", user, channel, msg
- else:
- #print "gobal:", user, channel, msg
- logC.info( "<%s> %s" % (user, msg) )
-
- def dccDoSend( self, user, address, port, filename, size, data ):
- print "dccDoSend", user, address, port, filename, size, data
- factory = XdccDownloaderFactory(
- self,
- filename,
- self.factory.destdir,
- (user, self.factory.channel, data) )
-
- if not hasattr( self, 'dcc_sessions' ):
- self.dcc_sessions = []
- self.dcc_sessions.append( factory )
-
- reactor.connectTCP(address, port, factory)
-
- def dccDownloadFinished( self, filename, success ):
-
- logP.info("Download of %s finished. Download status: %s" %
- ( filename, success ) )
- self.processXdccRequests()
-
- class XdccBotFactory( protocol.ClientFactory ):
-
- def __init__( self, channel, nickname, xdccBot, xdccRequests, destdir = '.' ):
- self.channel = channel
- self.nickname = nickname
- self.destdir = destdir
- self.xdccBot = xdccBot
- self.xdccRequests = xdccRequests
-
- def clientConnectionLost(self, connector, reason):
- logP.warning("Lost connection. Will try to reconnect. reason : %s" %
- reason)
- connector.connect()
-
- def buildProtocol( self, addr ):
- bot = XdccBot()
- bot.factory = self
- return bot
-
- def clientConnectionFailed(self, connector, reason):
- logP.warning("Connection failed. reason: %s" % reason)
- reactor.stop()
-
- class XdccDownloader( irc.DccFileReceive ):
-
- notifyBlockSize = 1024 * 1024
-
- def __init__( self, filename, filesize=-1, queryData=None, destDir='.',
- resumeOffset=0):
-
- irc.DccFileReceive.__init__( self, filename, filesize, queryData, destDir,
- resumeOffset )
- self.bytesNotify = 0
-
- def isDownloadSuccessful( self ):
- return self.bytesReceived == self.fileSize
-
- def dataReceived( self, data ):
- irc.DccFileReceive.dataReceived( self, data )
- if self.bytesReceived >= self.bytesNotify + self.notifyBlockSize:
-
- self.bytesNotify += self.notifyBlockSize
-
- logP.info("%s is now at %s bytes" % (self.filename,
- self.bytesReceived) )
-
- def connectionLost( self, reason ):
- logP.info(reason)
- self.fileSize = self.file.tell()
- irc.DccFileReceive.connectionLost( self, reason )
-
- if hasattr( self.factory.client, 'dccDownloadFinished' ):
- self.factory.client.dccDownloadFinished( self.filename,
- self.isDownloadSuccessful() )
-
- class XdccDownloaderFactory( protocol.ClientFactory ):
-
- def __init__( self, client, filename, destdir, queryData ):
- self.client = client
- self.queryData = queryData
- self.filename = filename
- self.destdir = destdir
-
- def buildProtocol( self, addr ):
- downloader = XdccDownloader(
- self.filename,
- -1,
- self.queryData,
- self.destdir)
- downloader.factory = self
- return downloader
-
- def clientConnectionFailed( self, connector, reason ):
- logP.warning("connection failed during DCC download. reason: %s" %
- reason)
- self.client.dcc_sessions.remove(self)
-
- def clientConnectionLost( self, connector, reason ):
- self.client.dcc_sessions.remove(self)
-
- if __name__ == "__main__":
-
- if( len( sys.argv ) < 6 ):
- print "Usage: %s IRC_SERVER CHANNEL NICKNAME BOTNICKNAME XDCC_REQUESTS"
- sys.exit(1)
-
- #log.basicConfig( level = log.INFO )
-
- #log.basicConfig(
- # filename='twitter_effect.log',
- # level=log.INFO,
- #)
-
- setup_logger('log_private', '/tmp/log_private')
- setup_logger('log_channel', '/tmp/log_channel')
-
- logP = log.getLogger('log_private')
- logC = log.getLogger('log_channel')
-
-
-
- observer = twistedlog.PythonLoggingObserver()
- observer.start()
-
- server, channel, nickname, xdccbot = sys.argv[1:5]
- xdccrequests = sys.argv[5:]
-
- print server, channel, nickname, xdccbot, xdccrequests
-
- factory = XdccBotFactory( channel, nickname, xdccbot, xdccrequests )
- reactor.connectTCP( server, 6667, factory )
- reactor.run()
-