471,348 Members | 1,848 Online
Bytes | Software Development & Data Engineering Community
Post +

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 471,348 software developers and data experts.

asyncore/asynchat IRC client

6
Hi there guys and girls.

I'm writing an IRC bot (even though there are loads out there that can do what I want, I love to learn-by-doing) and currently use the following to connect to the server:

Expand|Select|Wrap|Line Numbers
  1. import sys, string, os
  2. import socket, asyncore, asynchat
  3. from irc_config import *
  4. from irc_parse import *
  5. from irc_modules import *
  6. #from irc_connect import *
  7.  
  8. # create our socket
  9. sock = socket.socket()
  10.  
  11. # start us up
  12. parser = parse( sock, config.s )
  13.  
  14. # load all our modules
  15. startup_modules( parser )
  16.  
  17. # connect to our server
  18. sock.connect( ( config.s, config.p ) )
  19.  
  20. # our buffer destination
  21. readbuffer = ''
  22.  
  23. # send our initial nick/user bits
  24. sock.send( 'NICK %s\r\n' % config.n )
  25. sock.send( 'USER %s iw 0 :%s\r\n' % ( config.i, config.r ) )
  26.  
  27. # enter our data loop
  28. while 1:
  29.     try:
  30.         bytes_received = readbuffer
  31.  
  32.         while bytes_received >= config.b:
  33.             packet = sock.recv( config.b )
  34.             bytes_received = len( packet )
  35.             readbuffer += packet
  36.  
  37.     except Exception, e:
  38.         if config.b == 1:
  39.             print e
  40.             print '<< DEBUG >> Fatal error.'
  41.         sys.exit(1)
  42.  
  43.     while readbuffer.find( '\r\n' ) >= 0:
  44.         currentline = readbuffer[:readbuffer.find( '\r\n' )]
  45.         readbuffer = readbuffer[len( currentline )+2:]
  46.         print currentline
  47.  
  48.         parser.irc_parse( currentline )
Now, this works - just fine and dandy. However, the birds aren't quite tweeting and I'm not sitting bare arsed on the office photocopier with a party hat on quite yet. No, no. In fact, I realise that this blocking method of reading sucks hard. I have written numerous IRC clients in C before that use select and fifo buffers and I wish to do the same in python.

I am having a very hard time getting my head around even where to start with this - can someone please help with a very simple example that I can butcher to my own evil devices?

Thanks for the help in advance,

Ian
Jul 2 '07 #1
12 6078
bartonc
6,596 Expert 4TB
Hey, Ian. Love the way you write. I've got a USB model that queues read packets for the app to read when it gets around to it around here somewhere. I'll dig up that link for you.

I hope you like our Python forum,
Barton
Jul 2 '07 #2
bartonc
6,596 Expert 4TB
Hey, Ian. Love the way you write. I've got a USB model that queues read packets for the app to read when it gets around to it around here somewhere. I'll dig up that link for you.

I hope you like our Python forum,
Barton
Ah, here it is. Threading and Queuing, all in one.

I haven't used the asyn* modules.
Jul 2 '07 #3
brks
6
Hey, Ian. Love the way you write.
Thank you - I've found over the years that the more entertaining your questions are to read, the more likely people are to read and reply! :o)

Ah, here it is. Threading and Queuing, all in one.

I haven't used the asyn* modules.
Thanks again for that - it was an interesting read. I see the logic behind the threading, almost deamonizing reading and the concept I think it pretty much what I need. However, much to my distress, every current example of python IRC bots manages to utilise asyn* modules in such a way as to completely confuse me and, I personally think, over-complicate the whole thing. I'm pretty sure it should go somewhere along the lines of (mostly from python bot 'phenny'):

Expand|Select|Wrap|Line Numbers
  1. import sys, os, socket, asyncore, asynchat
  2.  
  3. class Bot:
  4.    def __init__(self, nick): 
  5.       asynchat.async_chat.__init__(self)    
  6.       self.buffer = ''
  7.       self.set_terminator('\r\n')
  8.       self.nick = nick
  9.       self.msgstack = []
  10.  
  11.    def write(self, args, text=None): 
  12.       if text is not None: 
  13.          self.push(' '.join(args) + ' :' + text + '\r\n')
  14.       else: self.push(' '.join(args) + '\r\n')
  15.  
  16.    def handle_connect(self): 
  17.       self.write(('NICK', self.nick))
  18.       self.write(('USER', self.uid, '+iw', self.nick), self.name)
  19.  
  20.    def collect_incoming_data(self, data): 
  21.       self.buffer += data
  22.  
  23.    def found_terminator(self): 
  24.       line = self.buffer
  25.       self.buffer = ''
  26.       if line.startswith(':'): 
  27.          origin, line = line[1:].split(' ', 1)
  28.          origin = Origin(origin)
  29.       else: origin = None
  30.  
  31.       if ' :' in line: 
  32.          args, text = line.split(' :', 1)
  33.       else: args, text = line, ''
  34.       args = args.split()
  35.  
  36.       if origin: origin.replyTo(self.nick, args)
  37.       self.dispatch(args, text, origin)
  38.  
  39.    def todo(self, args, *text):
  40.       command = ' '.join(args)
  41.       if text:
  42.           command = '%s : %s' % (command, ' '.join(text))
  43.       command = re.sub(r"\n", "", command)
  44.       command = re.sub(r"\r", "", command)      
  45.       self.push(command + CRLF)
  46.  
  47.    def run(self, host, port): 
  48.       self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
  49.       time.sleep(0.5)
  50.       self.connect((host, port))
  51.       time.sleep(0.5)
  52.       asyncore.loop()
  53.  
  54. test = Bot.run
  55. test( 'irc.some.server.org', 6667 ) # should start the bot off
  56.  
Now that I've pasted nearly all of somebody else's work I guess I should ask a few questions hey!

1) asyncore.loop() - what exactly is that doing? Is that like my initial while 1: loop in my very first post? If so, where is the data read to? self.buffer?

2) does assigning something to self. make it globally available or is it just accessible within that class?

3) can someone explain what 'dispatch' is doing exactly?

I hope you like our Python forum,
Barton
If everyone is as helpful and friendly as yourself I'll be sticking around a lot more!

All the best,

Ian
Jul 3 '07 #4
brks
6
OK - I've finally done it.

After many cups of coffee and a very late night, the problem has been solved and I have asynchronous reading once more. Awesome.

Thanks for your help - I'm sure I'll be sticking around :o)

All the best,

Ian
Jul 3 '07 #5
bartonc
6,596 Expert 4TB
OK - I've finally done it.

After many cups of coffee and a very late night, the problem has been solved and I have asynchronous reading once more. Awesome.

Thanks for your help - I'm sure I'll be sticking around :o)

All the best,

Ian
Any chance that we can get a peek at you work for the benefit of the budding Pythoneers who visit in the future?
Jul 3 '07 #6
brks
6
Sure thing!

Expand|Select|Wrap|Line Numbers
  1. #!/usr/bin/env python
  2.  
  3. import os, sys
  4. import socket, asyncore, asynchat
  5. import irc_config
  6. import irc_parse
  7. import irc_modules
  8.  
  9. class Bot( asynchat.async_chat ):
  10.     def __init__( self, config ):
  11.         asynchat.async_chat.__init__( self )   
  12.         self.buffer = ''
  13.         self.set_terminator( '\r\n' )
  14.         self.nick = config.n
  15.         self.ident = config.i
  16.         self.realname = config.r
  17.         self.debug = config.d
  18.         self.parser = irc_parse.parse_irc_input( config.s, self.write )
  19.  
  20.         # load all our modules
  21.         irc_modules.startup_modules( self.parser )
  22.  
  23.     def write( self, text ):
  24.         if self.debug == 1:
  25.             print '<< DEBUG >> WRITING "%s" TO SOCKET!' % text
  26.         self.push( text + '\r\n' )
  27.  
  28.  
  29.     def handle_connect( self ):
  30.         self.write( 'NICK %s' % self.nick )
  31.         self.write( 'USER %s iw 0 :%s' % ( self.ident, self.realname ) )
  32.  
  33.     def collect_incoming_data( self, data ):
  34.         self.buffer += data
  35.  
  36.     def found_terminator( self ):
  37.         line = self.buffer
  38.         self.buffer = ''
  39.         if self.debug == 1:
  40.             print '<< DEBUG >> %s' % line
  41.         self.parser.parse_text( line )
  42.  
  43.     def run( self, host, port ):
  44.         self.create_socket( socket.AF_INET, socket.SOCK_STREAM )
  45.         self.connect( ( host, port ) )
  46.         asyncore.loop()
  47.  
  48. # attemptfork is thanks to phenny irc bot ( http://inamidst.com/phenny/ )
  49. def attemptfork(): 
  50.     try: 
  51.         pid = os.fork()
  52.         if pid > 0:
  53.             sys.exit( 0 )
  54.     except OSError, e: 
  55.         raise OSError( 'Could not daemonize process: %d ( %s )' % ( e.errno, e.strerror ) )
  56.     os.setsid()
  57.     os.umask( 0 )
  58.     try: 
  59.         pid = os.fork()
  60.         if pid > 0: 
  61.             sys.exit( 0 )
  62.     except OSError, e: 
  63.         raise OSError( 'Could not daemonize process: %d ( %s )' % ( e.errno, e.strerror ) )
  64.  
  65. # load our config
  66. conf = irc_config.config
  67.  
  68. if conf.d == 0 and conf.b == 1 and hasattr( os, 'fork' ): 
  69.     attemptfork()
  70.  
  71. # initialize
  72. mypybot = Bot( conf )
  73.  
  74. # and run
  75. mypybot.run( conf.s, conf.p )
there's a lot left to the imagination, but you see the general jist of what's happening as far as asynchronous data transfer goes!

All the best guys,

Ian
Jul 3 '07 #7
bartonc
6,596 Expert 4TB
Thanks, Ian. Hope to see ya 'round...
Barton
Jul 3 '07 #8
bvdet
2,851 Expert Mod 2GB
Ian,

I appreciate your sharing with us. This is new to me - maybe I can learn something!

BV
Jul 3 '07 #9
brks
6
no problems guys - glad it was so useful

i'll keep you all updated on the architecture i'm releasing as a barse-bones module driven irc bot

if you're a die-hard irc'er like myself then you'll love it!

cheerio

Ian
Jul 3 '07 #10
bartonc
6,596 Expert 4TB
no problems guys - glad it was so useful

i'll keep you all updated on the architecture i'm releasing as a barse-bones module driven irc bot

if you're a die-hard irc'er like myself then you'll love it!

cheerio

Ian
(OK) You meant to say "barse-bones", right? ROFL
Jul 3 '07 #11
brks
6
It was meant to be 'bare-bones' admittedly, but i thought no-one would notice ;)
Jul 4 '07 #12
This looks very interesting. I use irclib.py for my ircbot, but I think I will take a look at this, when you release it, as well.
Jul 5 '07 #13

Post your reply

Sign in to post your reply or Sign up for a free account.

Similar topics

reply views Thread by Michael Welsh | last post: by
4 posts views Thread by F.G.Testa | last post: by
4 posts views Thread by Joshua Moore-Oliva | last post: by
16 posts views Thread by Rob Snyder | last post: by
reply views Thread by David Hirschfield | last post: by
reply views Thread by Giampaolo Rodola' | last post: by
1 post views Thread by Ronak mishra | last post: by

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.