Closed Thread
Results 1 to 7 of 7

Thread: IRC Bots in Python: A discussion

  1. #1
    pedro3005 is offline Newbie
    Join Date
    Mar 2010
    Location
    Rio de Janeiro, Brazil
    Posts
    14
    Rep Power
    0

    IRC Bots in Python: A discussion

    IMPORTANT: I completely re-sketched the bot's code. I believe it to be superior now. You can check the old version at Paste #189293 | LodgeIt! .

    BOT V2:

    - What has changed?

    Given the great number of functions and work-arounds existent in the last code, I decided to make a version 2. Now, we use classes (sweet !), providing a much cleaner, understandable and sane code. Thus far, this version is (function-wise) simpler than the other, having only a basic commands suit (act and say), plus a group permissions class. But, but, it's easier to understand and improve! At least I hope so. Let's get to the code:

    - The code:

    Code:
    # -*- coding: utf-8 -*-
    
    import socket, dbm, random, sys, time
    
    ########################### Editing below this is necessary:
    HOST = "irc.freenode.net"
    PORT = 6667
    NICK = "FailBot"
    USERNAME = "FailBot"
    REALNAME = "FailBot"
    CHANNELS = ["##devil"]
    bot_prefix = "!"
    import_plugins = "BasicCommands(), UserLevel(), Admin()" #must be classes
    ############################ Editing beyond here is not necessary.
    
    
    storage = dbm.open("botv2", "c")
    if "password" not in storage.keys():
        set_passwd = raw_input("Set an ident password, put 0 if none: ")
        storage["password"] = set_passwd
        set_admin = raw_input("Set a nick to be the admin. DO NOT leave blank: ")
        storage["users_Owners"] = set_admin
    
    class IrcMessage:
        '''Parses the IRC message, defining msg.prefix, msg.command, and msg.args'''
    
        def __init__(self, msg):
            self.prefix, self.command, self.args = self.parsemsg(msg)
    
        def parsemsg(self, s):
            trailing = 0
            prefix = 0
            if s[0] == ":":
                s = s[1:].split(' ', 1)
                prefix = s[0]
                s = s[1]
            if " :" in s:
                s = s.split(" :", 1)
                trailing = s[1]
                s = s[0]
            args = s.split()
            command = args.pop(0)
            if trailing != 0:
                args.append(trailing)
            return prefix, command, args
    
    class Privmsg:
        '''Parses a PRIVMSG on IRC, defining msg.nick, msg.channel, msg.message, msg.bot_command, and msg.bot_command_args. Also contains methods: reply (send message into channel), reply_act(send a /me into channel).'''
    
        def __init__(self, msg):
            try:
                self.nick = msg.prefix.split("!", 1)[0]
                self.channel = msg.args[0]
                self.message = msg.args[1]
                self.bot_command = self.message.split()[0]
                if not self.bot_command == self.message.split():
                    self.bot_command_args = self.message.split()[1:]
                self.true = 1
            except:
                self.true = 0
    
        def reply(self, msg):
            socket.send("PRIVMSG %s :%s\r\n" % (self.channel, msg))
    
        def reply_act(self, msg):
            socket.send("PRIVMSG %s :\x01ACTION %s\x01\r\n" % (self.channel, msg))
    
    class BasicCommands:
        '''A basic suit of commands. Include: <bot prefix>say (to say stuff in the channel) and <bot prefix>act (for /me). '''
    
        def say(self, privmsg):
            if has_permission("users_BasicCommands", privmsg.nick) :
                privmsg.reply(" ".join(privmsg.bot_command_args))
            else :
                privmsg.reply("Permission denied.")
    
    
        def act(self, privmsg):
            if has_permission("users_BasicCommands", privmsg.nick) :
                privmsg.reply_act(" ".join(privmsg.bot_command_args))
            else :
                privmsg.reply("Permission denied.")
    
    class UserLevel:
        '''An user level plugin. '''
    
        groups = [x for x in storage.keys() if x.startswith("users_")]
    
        def set_group(self, privmsg):
            groups_edited = []
            for grp in privmsg.bot_command_args[2:] :
                if privmsg.bot_command_args[1] == '+' and has_permission("users_Owners", privmsg.nick) :
                    if storage.get(grp, "") :
                        usr_list = storage[grp].split()
                        usr_list.append(privmsg.bot_command_args[0])
                        storage[grp] = " ".join(usr_list)
                        groups_edited.append(grp)
                    else :
                        storage[grp] = privmsg.bot_command_args[0]
                        groups_edited.append(grp)
                        self.groups.append(grp)
    
                elif privmsg.bot_command_args[1] == '-' and has_permission("users_Owners", privmsg.nick) and storage.get(grp, "") :
                    usr_list = storage[grp].split()
                    if privmsg.bot_command_args[0] in usr_list :
                        usr_list.remove(privmsg.bot_command_args[0])
                    storage[grp] = " ".join(usr_list)
                    groups_edited.append(grp)
            if privmsg.bot_command_args[1] == '+':
                privmsg.reply("Added user %s to groups: %s" % (privmsg.bot_command_args[0], ", ".join(groups_edited)))
            elif privmsg.bot_command_args[1] == '-':
                privmsg.reply("Removed user %s from groups: %s" % (privmsg.bot_command_args[0], ", ".join(groups_edited)))
    
        def read_group(self, privmsg):
            if privmsg.bot_command_args[0] == '*' :
                privmsg.reply("Groups: %s" % ", ".join(self.groups))
            else :
                if privmsg.bot_command_args[0] in self.groups :
                    privmsg.reply("People in the group %s: %s" % (privmsg.bot_command_args[0], ", ".join(storage[privmsg.bot_command_args[0]].split())))
                else :
                    privmsg.reply("Group %s not found" % privmsg.bot_command_args[0])
        
        def del_group(self, privmsg):
            grps_removed = []
            grps_notfound = []
            if has_permission("users_Owners", privmsg.nick) :
                for grp in privmsg.bot_command_args :
                    if grp in self.groups :
                        self.groups.remove(grp)
                        del storage[grp]
                        grps_removed.append(grp)
                    else :
                        grps_notfound.append(grp)
                privmsg.reply("Removed groups: %s. Could not find groups: %s." % (", ".join(grps_removed), ", ".join(grps_notfound)))
            else :
                privmsg.reply("Permission denied.")
    
    class Admin:
        def quit(self, privmsg):
            if has_permission("users_Owners", privmsg.nick):
                privmsg.reply("Quitting...")
                socket.close()
                sys.exit()
            else:
                privmsg.reply("Permission denied.")
        def sendraw(self, privmsg):
            if has_permission("users_Owners", privmsg.nick):
                socket.send(" ".join(privmsg.bot_command_args) + '\n')
            else:
                privmsg.reply("Permission denied.")
        def mode(self, privmsg):
            if has_permission("users_Owners", privmsg.nick):
                socket.send("MODE %s " % privmsg.channel + " ".join(privmsg.bot_command_args) + '\n')
            else:
                privmsg.reply("Permission denied.")
        def topic(self, privmsg):
            if has_permission("users_Owners", privmsg.nick):
                socket.send("TOPIC %s :" % privmsg.channel + " ".join(privmsg.bot_command_args) + '\n')
            else:
                privmsg.reply("Permission denied.")
        def kick(self, privmsg):
            if has_permission("users_Owners", privmsg.nick):
                socket.send("KICK %s %s :%s\n" % (privmsg.channel, privmsg.bot_command_args[0], " ".join(privmsg.bot_command_args[1:])))
            else:
                privmsg.reply("Permission denied.")
    
    def has_permission(group, user):
        if "UserLevel()" in import_plugins.split(", ") :
            try :
                if user in storage["users_Owners"].split() :
                    return True
                elif user in storage[group].split() :
                    return True
                else :
                    return False
            except :
                return False
        else :
            return True
    
    exec("plugins = %s" % import_plugins)
    
    #INITIAL HANDSHAKE
    socket = socket.socket()
    socket.connect((HOST, PORT))
    if not storage["password"] == "0":
        socket.send("PASS :%s\r\n" % (storage["password"]))
    time.sleep(3)
    socket.send("NICK %s\r\n" % (NICK))
    socket.send("USER %s * * :%s\r\n" % (USERNAME, REALNAME))
    for channel in CHANNELS:
        socket.send("JOIN %s\r\n" % (channel))
    #END OF HANDSHAKE
    
    recv = ""
    while True:
         recv = recv + socket.recv(4096)
         s = recv.split("\r\n")
         recv = s.pop()
         for msg in s:
            print msg
            msg = IrcMessage(msg)
            if msg.command == "PRIVMSG":
                privmsg = Privmsg(msg)
                for plugin in plugins:
                    if privmsg.true == 1:
                        if hasattr(plugin, privmsg.bot_command[1:]) and privmsg.message.startswith(bot_prefix) :
                            command = getattr(plugin, privmsg.bot_command[1:])
                            try :
                                command(privmsg)
                            except SystemExit :
                                raise
                            except :
                                privmsg.reply("Error calling function.")
            elif msg.command == "PING":
                socket.send("PONG\r\n")
            elif msg.command == "KICK" and NICK in msg.args:
                socket.send("JOIN %s\n" % msg.args[0])

    - How to run:

    Running it is quite simple, obvious and straightforward. There are no mysteries like in the last version, really. Just edit whatever you need and enter information accordingly.

    - How to edit:

    Basically, what you need to do do is create a class. And the command to call a method via irc is simply !name, so, if we have:
    Code:
    class Test:
        def test(self, privmsg):
            privmsg.reply("Success!")
    Typing !test on the channel will call Test.test. What is important to notice is the methods will be called with privmsg as an argument, and privmsg is an instance of the class Privmsg (what a surprise). Important things to know:

    privmsg.nick = nick of whoever typed the message which triggered the method
    privmsg.bot_command = the actual command. ex, in !kill pedro3005, !kill is the bot_command.
    privmsg.bot_command_args = A LIST of the arguments after bot_command split by spaces.
    privmsg.channel = the channel the message was sent on (although you usually won't have to worry about this)
    privmsg.message = the actual message. I.E., in
    Code:
    :pedro3005~pedro@unaffiliated/pedro3005 PRIVMSG #channel :a
    the actual message will be "a".
    privmsg.reply = this sends a message into the channel that triggered the method. I.E., privmsg.reply("I am here")
    privmsg.reply_act = this is the same as above, only it is an action.

    So, simply write a class and add it to the import_plugins list on the configuration area. REMEMBER that the method will always be called with privmsg as an argument, so never try to take more than one argument!

    - On the group permission system:

    The way this works is we have various groups, and these groups have access to all methods inside a class. So for instance, the group users_BasicCommands will have access to all the BasicCommand class methods. Whenever naming a group for this, ALWAYS precede it with users_. That is important! So, to check if a certain user has permission to access a certain class, I wrote the function has_permission(group, user). What it does is firstly check if the group permission system is enabled. If not, it will return TRUE. Then, it checks if the user is either in the owners group, users_Owners, or in the group the function was called with. If yes, it returns TRUE. If not, it will return FALSE.
    So, pragmatically, what you need to know about it:

    !set_group <user> <+/-> users_Group1 users_Group2
    !read_group <group/*> (* will list all groups, and if you specify a group it will list everyone on it)
    !del_group users_Group1

    Users on the users_Owners group have access to ALL commands. I wish to improve the code to this class (it looks rather ugly), so any tips are welcomed.

    EDIT: Some bug fixes, added Admin class (some sweet new functions).

    Last edited by pedro3005; 04-06-2010 at 09:25 PM. Reason: bugs fixed

  2. CODECALL Circuit advertisement
    Join Date
    Always
    Location
    Advertising world
    Posts
    Many

     
  3. #2
    Root23 is offline Programmer
    Join Date
    Jan 2010
    Posts
    144
    Rep Power
    8

    Re: IRC Bots in Python: A discussion

    This is pretty cool to see. I used to be on IRC a lot, and some of my first attempts at 'programming' was writing IRC scripts (I didn't make it very far).

    I never thought of writing a bot in Python for IRC.

  4. #3
    pedro3005 is offline Newbie
    Join Date
    Mar 2010
    Location
    Rio de Janeiro, Brazil
    Posts
    14
    Rep Power
    0

    Re: IRC Bots in Python: A discussion

    Yes, they're very fun to make-- you should try it some time!
    I am now in the arduous process of reinventing the code, since I learned about Classes.
    This will be good though, I'm making things more independent and nicely separated. Maybe the code will also be easier to understand.
    It's just that I have been really busy with school and don't have much time for python, but I'll keep working on it. AFAIK though, the code presented above, whilst ugly (let's admit that ), works.

  5. #4
    pedro3005 is offline Newbie
    Join Date
    Mar 2010
    Location
    Rio de Janeiro, Brazil
    Posts
    14
    Rep Power
    0

    Re: IRC Bots in Python: A discussion

    New version presented, let's check it out

  6. #5
    deskchecked's Avatar
    deskchecked is offline Newbie
    Join Date
    Apr 2010
    Location
    Melbourne, Australia
    Posts
    29
    Rep Power
    0

    Re: IRC Bots in Python: A discussion

    Also check out Twisted's IRCClient. Very easy to use/extend once you get your head around the event-driven nature of Twisted.

  7. #6
    pedro3005 is offline Newbie
    Join Date
    Mar 2010
    Location
    Rio de Janeiro, Brazil
    Posts
    14
    Rep Power
    0

    Re: IRC Bots in Python: A discussion

    Yes, I started the yet again redevelopment of this bot. It's over at launchpad and uses Twisted.
    Link: https://launchpad.net/failbot

  8. #7
    Metaphysicist is offline Newbie
    Join Date
    Aug 2010
    Posts
    1
    Rep Power
    0

    Re: IRC Bots in Python: A discussion

    Pedro3005,

    I love your bot, very interesting use of classes. However, I cannot seem to define a class that will let me send a message to a specific channel that the bot is in from another channel? i.e. a specified channel through a message. Can you help?

Closed Thread

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Similar Threads

  1. Logic Warz - Program your own Bot, battle other people's Bots
    By logicwarz in forum Community Projects
    Replies: 1
    Last Post: 05-14-2011, 06:08 AM
  2. Is there a windows application equivalent to web bots?
    By appguy1010 in forum Managed C++
    Replies: 1
    Last Post: 02-17-2010, 11:01 PM
  3. Making bots for the web
    By BosanskiCevap in forum C# Programming
    Replies: 6
    Last Post: 02-14-2010, 03:39 AM
  4. Will this function miss any bots?
    By codytaylor in forum PHP Development
    Replies: 4
    Last Post: 08-08-2009, 06:55 PM

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts