Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python
- import socket
- class Connection(object):
- """Connection object. All interactions with the network are handled
- through this class.
- """
- def __init__(self):
- self.buffer = ""
- def connect(self, host, port):
- # Note: create_connection automatically connects
- self.connection = socket.create_connection((host, port))
- def read(self):
- "Reads one line from the server, blocking until one is available."
- while True:
- if "\r\n" in self.buffer:
- line, self.buffer = self.buffer.split("\r\n", 1)
- return line
- self.buffer += self.connection.recv(512)
- def write(self, line):
- "Writes one line to the server, automatically adding a linebreak"
- self.connection.send(line + "\r\n")
- def send(self, command, *args):
- """A more convenient way to write to the server, as it builds a
- message object and converts it into a raw line to send to the server.
- Note that clients cannot send prefixes.
- """
- self.write(str(Message(command, args)))
- class Message(object):
- """The Message object encapsulates an IRC line, or message. It also has the
- ability to return to it's original IRC line.
- An IRC message is composed of a prefix, command, and arguments, which
- are fields in this object. If the prefix is a hostmask, the relevant
- fields (nickname, username, and hostname) are available. You can
- determine if there is a hostmask by the is_mask boolean flag.
- """
- def __init__(self, command, args, prefix=None):
- self.command = command
- self.args = args
- self.prefix = prefix
- if self.prefix:
- # Parse the prefix into a nickname, username, and hostname, if
- # possible.
- #
- # TODO better searching
- if "!" in self.prefix:
- # Hostmask format: nickname!~username@hostname
- self.nickname, userhost = prefix.split("!")
- self.username, self.hostname = userhost.split("@")
- @classmethod
- def parse(cls, line):
- "Parses a line and returns a new Message object"
- prefix = ""
- trailing = []
- if line[0] == ':':
- prefix, line = line[1:].split(' ', 1)
- if line.find(' :') != -1:
- line, trailing = line.split(' :', 1)
- args = line.split()
- args.append(trailing)
- else:
- args = line.split()
- command = args.pop(0)
- return Message(command, args, prefix)
- def __str__(self):
- """Returns a (directly sendable) raw IRC line, built from this object's
- fields. A newline is appended.
- """
- if self.prefix:
- start = ":%s %s" % (self.prefix, self.command)
- else:
- start = self.command
- if self.args:
- args = list(self.args)
- if " " in args[-1] or ":" in args[-1] or not args[-1]:
- args[-1] = ":" + args[-1]
- return "%s %s" % (start, " ".join(args)) + "\r\n"
- else:
- return start + "\r\n"
- class Dispatch(object):
- """Methods used to respond to IRC messages.
- Named in the format dispatch_$COMMAND, with $COMMAND representing the
- relevant command or numeric.
- """
- def dispatch_ping(self, grubbot, msg):
- grubbot.connection.send("pong", msg.args[0])
- ## Methods for managing the user state
- def dispatch_353(self, grubbot, msg):
- """Numeric 353 sends a list of all users in a channel as you join the
- channel. If the bot was previously in the channel this session, the
- the previous data is replaced anyway.
- """
- channel = msg.args[2]
- # Also strip relevant first characters
- users = [user.lstrip("@+") for user in msg.args[3].split()]
- if channel in grubbot.users:
- del grubbot.users[channel]
- grubbot.users[channel] = users
- def dispatch_join(self, grubbot, msg):
- channel = msg.args[0]
- nickname = msg.nickname
- # BUGFIX The bot will crash on startup as the first person to join a
- # channel without this
- grubbot.users.setdefault(channel, []).append(nickname)
- def dispatch_part(self, grubbot, msg):
- channel = msg.args[0]
- nickname = msg.nickname
- grubbot.users[channel].remove(nickname)
- def dispatch_kick(self, grubbot, msg):
- self.dispatch_part(grubbot, msg)
- def dispatch_quit(self, grubbot, msg):
- self.dispatch_part(grubbot, msg)
- def dispatch_nick(self, grubbot, msg):
- "Nickname changes are important to watch while managing user state."
- nickname = msg.nickname
- new_nickname = msg.args[0]
- for users in grubbot.users.values():
- if nickname in users:
- users.remove(nickname)
- users.append(new_nickname)
- # Take note of the bot's new nickname
- if nickname == grubbot.nickname:
- grubbot.nickname = new_nickname
- class Grubbot(object):
- """Glues together the bot - contains the read -> parse -> dispatch loop,
- a couple misc. functions, and state information.
- """
- # TODO Actual config file
- NICKNAME = "grubbot3"
- USERNAME = "grubbot3"
- PASSWORD = ""
- REALNAME = "grubby"
- CHANNELS = ("##grubby",)
- NETWORK = "irc.freenode.net"
- PORT = 8001
- # To keep track of the bot's own nickname
- nickname = NICKNAME
- def __init__(self):
- self.connection = Connection()
- self.dispatch = Dispatch()
- # Dictionary of channels and their users, in the format
- # channel: [list of users]
- self.users = {}
- def connect(self):
- "Set up a connection to the server and login the client."
- self.connection.connect(self.NETWORK, self.PORT)
- if self.PASSWORD:
- self.connection.send("pass", self.PASSWORD)
- self.connection.send("nick", self.NICKNAME)
- self.connection.send("user", self.USERNAME, "*", "*", self.REALNAME)
- for channel in self.CHANNELS:
- self.connection.send("join", channel)
- # HACK this is to prevent a naming conflict
- def do_dispatch(self, msg):
- "Sets up the response to an IRC message."
- # NOTE this would be a good place for a logger
- print self.users
- command = msg.command.lower()
- try:
- func = getattr(self.dispatch, "dispatch_" + command)
- except AttributeError:
- pass # Unsupported command or numeric
- else:
- func(self, msg)
- print "Dispatched", command # REPLACE
- def run(self):
- "Runs the main read -> parse -> dispatch loop."
- while True:
- line = self.connection.read()
- print line
- msg = Message.parse(line)
- self.do_dispatch(msg)
- def main():
- grubbot = Grubbot()
- grubbot.connect()
- grubbot.run()
- if __name__ == "__main__":
- main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement