Advertisement
Guest User

Untitled

a guest
Sep 14th, 2017
96
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 7.16 KB | None | 0 0
  1. #!/usr/bin/env python
  2.  
  3. import socket
  4.  
  5. class Connection(object):
  6.     """Connection object. All interactions with the network are handled
  7.       through this class.
  8.    """
  9.  
  10.     def __init__(self):
  11.         self.buffer = ""
  12.  
  13.     def connect(self, host, port):
  14.         # Note: create_connection automatically connects
  15.         self.connection = socket.create_connection((host, port))
  16.  
  17.     def read(self):
  18.         "Reads one line from the server, blocking until one is available."
  19.         while True:
  20.             if "\r\n" in self.buffer:
  21.                 line, self.buffer = self.buffer.split("\r\n", 1)
  22.                 return line
  23.             self.buffer += self.connection.recv(512)
  24.  
  25.     def write(self, line):
  26.         "Writes one line to the server, automatically adding a linebreak"
  27.         self.connection.send(line + "\r\n")
  28.  
  29.     def send(self, command, *args):
  30.         """A more convenient way to write to the server, as it builds a
  31.           message object and converts it into a raw line to send to the server.
  32.  
  33.           Note that clients cannot send prefixes.
  34.        """
  35.         self.write(str(Message(command, args)))
  36.  
  37. class Message(object):
  38.     """The Message object encapsulates an IRC line, or message. It also has the
  39.       ability to return to it's original IRC line.
  40.  
  41.       An IRC message is composed of a prefix, command, and arguments, which
  42.       are fields in this object. If the prefix is a hostmask, the relevant
  43.       fields (nickname, username, and hostname) are available. You can
  44.       determine if there is a hostmask by the is_mask boolean flag.
  45.    """
  46.  
  47.     def __init__(self, command, args, prefix=None):
  48.         self.command = command
  49.         self.args = args
  50.         self.prefix = prefix
  51.        
  52.         if self.prefix:
  53.             # Parse the prefix into a nickname, username, and hostname, if
  54.             # possible.
  55.             #
  56.             # TODO better searching
  57.             if "!" in self.prefix:
  58.                 # Hostmask format: nickname!~username@hostname
  59.                 self.nickname, userhost = prefix.split("!")
  60.                 self.username, self.hostname = userhost.split("@")
  61.  
  62.     @classmethod
  63.     def parse(cls, line):
  64.         "Parses a line and returns a new Message object"
  65.         prefix = ""
  66.         trailing = []
  67.  
  68.         if line[0] == ':':
  69.             prefix, line = line[1:].split(' ', 1)
  70.         if line.find(' :') != -1:
  71.             line, trailing = line.split(' :', 1)
  72.             args = line.split()
  73.             args.append(trailing)
  74.         else:
  75.             args = line.split()
  76.         command = args.pop(0)
  77.         return Message(command, args, prefix)
  78.  
  79.     def __str__(self):
  80.         """Returns a (directly sendable) raw IRC line, built from this object's
  81.           fields. A newline is appended.
  82.        """
  83.  
  84.         if self.prefix:
  85.             start = ":%s %s" % (self.prefix, self.command)
  86.         else:
  87.             start = self.command
  88.  
  89.         if self.args:
  90.             args = list(self.args)
  91.             if " " in args[-1] or ":" in args[-1] or not args[-1]:
  92.                 args[-1] = ":" + args[-1]
  93.             return "%s %s" % (start, " ".join(args)) + "\r\n"
  94.         else:
  95.             return start + "\r\n"
  96.  
  97. class Dispatch(object):
  98.     """Methods used to respond to IRC messages.
  99.  
  100.       Named in the format dispatch_$COMMAND, with $COMMAND representing the
  101.       relevant command or numeric.
  102.    """
  103.  
  104.     def dispatch_ping(self, grubbot, msg):
  105.         grubbot.connection.send("pong", msg.args[0])
  106.  
  107.     ## Methods for managing the user state
  108.  
  109.     def dispatch_353(self, grubbot, msg):
  110.         """Numeric 353 sends a list of all users in a channel as you join the
  111.           channel. If the bot was previously in the channel this session, the
  112.           the previous data is replaced anyway.
  113.        """
  114.        
  115.         channel = msg.args[2]
  116.         # Also strip  relevant first characters
  117.         users = [user.lstrip("@+") for user in msg.args[3].split()]
  118.  
  119.         if channel in grubbot.users:
  120.             del grubbot.users[channel]
  121.         grubbot.users[channel] = users
  122.  
  123.     def dispatch_join(self, grubbot, msg):
  124.         channel = msg.args[0]
  125.         nickname = msg.nickname
  126.         # BUGFIX The bot will crash on startup as the first person to join a
  127.         # channel without this
  128.         grubbot.users.setdefault(channel, []).append(nickname)
  129.    
  130.     def dispatch_part(self, grubbot, msg):
  131.         channel = msg.args[0]
  132.         nickname = msg.nickname
  133.         grubbot.users[channel].remove(nickname)
  134.  
  135.     def dispatch_kick(self, grubbot, msg):
  136.         self.dispatch_part(grubbot, msg)
  137.  
  138.     def dispatch_quit(self, grubbot, msg):
  139.         self.dispatch_part(grubbot, msg)
  140.  
  141.     def dispatch_nick(self, grubbot, msg):
  142.         "Nickname changes are important to watch while managing user state."
  143.         nickname = msg.nickname
  144.         new_nickname = msg.args[0]
  145.  
  146.         for users in grubbot.users.values():
  147.             if nickname in users:
  148.                 users.remove(nickname)
  149.                 users.append(new_nickname)
  150.  
  151.         # Take note of the bot's new nickname
  152.         if nickname == grubbot.nickname:
  153.             grubbot.nickname = new_nickname
  154.  
  155. class Grubbot(object):
  156.     """Glues together the bot - contains the read -> parse -> dispatch loop,
  157.       a couple misc. functions, and state information.
  158.    """
  159.  
  160.     # TODO Actual config file
  161.     NICKNAME = "grubbot3"
  162.     USERNAME = "grubbot3"
  163.     PASSWORD = ""
  164.     REALNAME = "grubby"
  165.     CHANNELS = ("##grubby",)
  166.     NETWORK  = "irc.freenode.net"
  167.     PORT     = 8001
  168.  
  169.     # To keep track of the bot's own nickname
  170.     nickname = NICKNAME
  171.  
  172.     def __init__(self):
  173.         self.connection = Connection()
  174.         self.dispatch = Dispatch()
  175.         # Dictionary of channels and their users, in the format
  176.         # channel: [list of users]
  177.         self.users = {}
  178.  
  179.     def connect(self):
  180.         "Set up a connection to the server and login the client."
  181.         self.connection.connect(self.NETWORK, self.PORT)
  182.  
  183.         if self.PASSWORD:
  184.             self.connection.send("pass", self.PASSWORD)
  185.         self.connection.send("nick", self.NICKNAME)
  186.         self.connection.send("user", self.USERNAME, "*", "*", self.REALNAME)
  187.  
  188.         for channel in self.CHANNELS:
  189.             self.connection.send("join", channel)
  190.  
  191.     # HACK this is to prevent a naming conflict
  192.     def do_dispatch(self, msg):
  193.         "Sets up the response to an IRC message."
  194.         # NOTE this would be a good place for a logger
  195.         print self.users
  196.         command = msg.command.lower()
  197.  
  198.         try:
  199.             func = getattr(self.dispatch, "dispatch_" + command)
  200.         except AttributeError:
  201.             pass # Unsupported command or numeric
  202.         else:
  203.             func(self, msg)
  204.             print "Dispatched", command # REPLACE
  205.  
  206.     def run(self):
  207.         "Runs the main read -> parse -> dispatch loop."
  208.         while True:
  209.             line = self.connection.read()
  210.             print line
  211.             msg = Message.parse(line)
  212.             self.do_dispatch(msg)
  213.  
  214. def main():
  215.     grubbot = Grubbot()
  216.     grubbot.connect()
  217.     grubbot.run()
  218.  
  219. if __name__ == "__main__":
  220.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement