Skip to content
Snippets Groups Projects
main.py 12.1 KiB
Newer Older
#!/usr/bin/env python
#
Svetlana Tkachenko's avatar
Svetlana Tkachenko committed
#  Copyright 2010, 2011 G24
#
#    This file is part of gpy.
#
#    gpy 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.
#
#    gpy 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 gpy.  If not, see <http://www.gnu.org/licenses/>.
import re
import argparse
import sys
Svetlana Tkachenko's avatar
Svetlana Tkachenko committed
import math
import time
import socket
import ConfigParser
from datetime import datetime 
Svetlana Tkachenko's avatar
Svetlana Tkachenko committed

class core:
    def dispatch(self,str):
        """Do not use!"""
        self.irc.send(str + '\n\r')
        self.ok("/" + str)
Svetlana Tkachenko's avatar
Svetlana Tkachenko committed
    def send(self,cmd,target,msg):
        self.dispatch(cmd + ' ' + target + ' :' + msg)
Svetlana Tkachenko's avatar
Svetlana Tkachenko committed
    def say(self,target,msg):
        self.send(self.config.get("connection","msgtype"),target,msg)
    def reply(self,msg):
        if self.recvinfo["target"] == self.vars["nick"]:
            self.say(self.recvinfo["origin.nick"],msg)
        else:
            self.say(self.recvinfo["target"],self.recvinfo["origin.nick"] + ": " + msg)
Svetlana Tkachenko's avatar
Svetlana Tkachenko committed
    def saypm(self,msg):
        self.send("PRIVMSG",target,msg)
    def cancel(self,line):
        print("\033[91m{0}\033[0m".format(line))
        self.log('! ' + line)
    def ok(self,line):
        print("\033[92m{0}\033[0m".format(line))
        self.log('  ' + line)
    def info(self,line):
        print("\033[93m{0}\033[0m".format(line))
        self.log('? ' + str(line))
        log = open(self.logpath,"a")
        log.write("{0} {1}\n".format(str(datetime.now()),line))
Svetlana Tkachenko's avatar
Svetlana Tkachenko committed
class connection(core):
    def __init__(self):
        # Constant.
        self.version = __import__('gpy').__version__
        print("  __ _ _ __  _   _   ")
        print(" / _` | '_ \| | | |")
        print("| (_| | |_) | |_| |    http://repo.or.cz/w/gpy.git/summary")
        print(" \__, | .__/ \__, |")
        print(" |___/|_|    |___/   {0}".format(self.version))
        print('')

        if 'HOME' in os.environ:
            self.directory = os.environ['HOME'] + '/.gpy/'
        elif 'HOMEPATH' in os.environ:
            self.directory = os.environ['HOMEPATH'] + '/.gpy/'
        else:
            self.directory = os.getcwd() + '/var/'

        print("Settings directory: %s" % self.directory)

        if not os.path.exists(self.directory):
            os.makedirs(self.directory)
        self.configpath = self.directory + 'main.cfg'
        self.logpath = self.directory + 'main.log'

        parser = argparse.ArgumentParser(description="Start gpy - a modular Python IRC bot.")
        parser.add_argument("-c", "--makeconf", required=False, help="edit the configuration file",action='store_true')
        parser.add_argument("-v", "--version", required=False, help="display version information and exit.",action='store_true')
        args = parser.parse_args(sys.argv[1:])
        if args.version == True:
            exit()
        if os.path.isfile(self.configpath) == False: 
            self.defaultconf()
            self.makeconf()
        
        if args.makeconf == True:
            self.makeconf()

        self.commands = []
        self.vars = {}
Svetlana Tkachenko's avatar
Svetlana Tkachenko committed
        
        self.config = ConfigParser.RawConfigParser()
        self.config.read(self.configpath)
        self.mainclasses = {};
        self.modules = {};
        tmp = (self.config.get("me","modules").strip() + ",").split(",")
        for each in tmp[0:(len(tmp) - 1)]:
Svetlana Tkachenko's avatar
Svetlana Tkachenko committed
            try:
                self.modules = __import__("gpy", globals(), locals(), [each], -1)
                self.mainclasses[each] = getattr(getattr(self.modules,each),"mod_main")(self)
                self.ok("Module {0} loaded.".format(each))
Svetlana Tkachenko's avatar
Svetlana Tkachenko committed
            except  Exception, e:
                self.cancel("Module {0} not loaded. {1}".format(each,e))
Svetlana Tkachenko's avatar
Svetlana Tkachenko committed
                pass

        self.vars["nick"] = self.config.get("connection","nick")
Svetlana Tkachenko's avatar
Svetlana Tkachenko committed
        # Connecting.
        self.info("Connecting to {0} ... ".format(self.config.get("connection","network")))
Svetlana Tkachenko's avatar
Svetlana Tkachenko committed
        self.irc=socket.socket()
        try:
            self.irc.connect ((self.config.get("connection","network"), self.config.getint("connection","port")))
            self.dispatch('NICK ' + self.config.get("connection","nick"))
            self.dispatch("USER " + self.config.get("connection","nick") + " * * :" + self.version+" http://repo.or.cz/w/gpy.git")
        except Exception, e:
            self.cancel("Connection failed. {0}".format(e))
            return
        if self.config.get("connection","ns_name") != "":
Svetlana Tkachenko's avatar
Svetlana Tkachenko committed
            self.dispatch("PRIVMSG NICKSERV :id {0} {1}".format(self.config.get("connection","ns_name"),self.config.get("connection","ns_pwd")))
Svetlana Tkachenko's avatar
Svetlana Tkachenko committed
        while True:
            self.data = self.irc.recv(4096)
            while(self.data[len(self.data)-2:len(self.data)]) != '\r\n':
                self.data += self.irc.recv(4096)
Svetlana Tkachenko's avatar
Svetlana Tkachenko committed
            for each in self.data.split("\r\n"):
                if each == "": # Do nothing with empty lines
Svetlana Tkachenko's avatar
Svetlana Tkachenko committed
                    break
                if each.split(' ')[0] == 'PING': # PING
                    self.dispatch('PONG ' + each.split()[1])
Svetlana Tkachenko's avatar
Svetlana Tkachenko committed
                    break
                self.parseline(each)
                if len(each.strip()) != 0: # Now listen.
Svetlana Tkachenko's avatar
Svetlana Tkachenko committed
                    self.listen()
                    try:
                        for mod in self.mainclasses:
                            self.mainclasses[mod].listen()
                    except:
                        pass
    def setvalue(self,section,option,helptext,default):
        value = default
        if self.config.has_section(section):
            if self.config.has_option(section, option):
                ivalue = self.config.get(section,option)
            else:
                ivalue = default
        else:
            self.config.add_section(section)
    def my_raw(self,prompt="", default=""):
        ret = raw_input("%s [%s]: "%(prompt,default))
        return default if ret == "" else ret
    def defaultconf(self):
        self.config = ConfigParser.RawConfigParser()
        self.config.read(self.configpath)
        self.config.add_section("connection")
        self.config.set("connection",'network','')
        self.config.set("connection",'channels','')
        self.config.set("connection",'nick','')
        self.config.set("connection",'msgtype','')
        self.config.set("connection",'ns_name','')
        self.config.set("connection",'ns_pwd','')
        self.config.set("connection",'port','6667')
        self.config.add_section("users")
        self.config.set("users",'username','')
        self.config.add_section("admins")
        self.config.set("admins",'username','')
        self.config.add_section("me")
        self.config.set("me","char",'')
        self.config.set("me","modules",'admin,web,fact,calc')
        with open(self.configpath, 'wb') as configfile:
            self.config.write(configfile)
    def set(self,section,option,text):
        self.config.set(section,option,self.my_raw(text,self.config.get(section,option)))
    def makeconf(self):
        self.config = ConfigParser.RawConfigParser()
        self.config.read(self.configpath)
        self.set("connection",'network',"IRC network to connect to")
        self.set("connection",'channels',"Channels to join (comma-separated, no spaces)")
        self.set("connection",'nick',"My nickname")
        self.set("connection",'msgtype',"Message type (privmsg or notice)")
        self.set("connection",'ns_name',"NickServ username (if there is none, press ENTER)")
        self.set("connection",'ns_pwd',"NickServ password (if there is none, press ENTER)")
        self.set("connection",'port',"Port (if unsure, type 6667)")
        username=self.my_raw("Your username (used for bot administration)",'username')
        self.config.set("users",username,self.my_raw("Your password",'password'))
        self.config.set("admins",username,'')
        self.set("me","char","My command char(s) (separate with |)")
        self.set("me","modules","Modules to load (comma-separated, no spaces, must include 'admin' module)")
        with open(self.configpath, 'wb') as configfile:
            self.config.write(configfile)
        self.ok('Configuration file has been created.')
        exit()
Svetlana Tkachenko's avatar
Svetlana Tkachenko committed
    def listen(self):
        # If we've read the MOTD - JOIN channels.
        if self.recvinfo["cmd"] == "MODE":
            self.info(self.recvinfo)
            for each in (self.config.get("connection","channels").strip() + ",").split(","):
                if each != "":
                    self.dispatch('JOIN ' + each)
Svetlana Tkachenko's avatar
Svetlana Tkachenko committed
            return
        for char in self.chars():
            if char=='':
                break
            if self.recvinfo["message"] == char: # We just said commandchar.
                if self.recvinfo["cmd"] != "PRIVMSG": return
                self.reply("Modules loaded: {0}.".format(str(self.mainclasses.keys())).replace('[','').replace(']','').replace('\'',''))
        if self.recvinfo["message.command"] in self.mainclasses and self.recvinfo["message.params"] == "":
            if self.recvinfo["cmd"] != "PRIVMSG": return
Svetlana Tkachenko's avatar
Svetlana Tkachenko committed
            mod=self.mainclasses[self.recvinfo["message.command"]]
            self.reply("{0} Includes commands: {1}.".format(mod.__doc__,str(mod.cmds.keys()) ).replace('[','').replace(']','').replace('\'',''))
Svetlana Tkachenko's avatar
Svetlana Tkachenko committed
            return
        if self.recvinfo["cmd"] == "INVITE":
Svetlana Tkachenko's avatar
Svetlana Tkachenko committed
            self.dispatch("JOIN "+self.recvinfo["message"])
            return
        if self.recvinfo["cmd"] == "NICK" and self.recvinfo["origin.nick"] == self.vars["nick"]:
            self.vars["nick"] = self.recvinfo["target"].replace(":","")
Svetlana Tkachenko's avatar
Svetlana Tkachenko committed
    def writeconfig(self):
        with open(self.configpath, 'w') as configfile:
Svetlana Tkachenko's avatar
Svetlana Tkachenko committed
            self.config.write(configfile)
    def parseline(self,line):
        self.recvinfo = {}
        self.recvinfo["origin"] = line.split(" ")[0].split(":")[1]
        self.recvinfo["origin.nick"] = ""
        self.recvinfo["origin.ident"] = ""
        self.recvinfo["origin.host"] = ""
        if self.recvinfo["origin"].find("@") != -1:
            self.recvinfo["origin.nick"] = self.recvinfo["origin"].split("!")[0]
            self.recvinfo["origin.ident"] = self.recvinfo["origin"].split("!")[1].split("@")[0]
            self.recvinfo["origin.host"] = self.recvinfo["origin"].split("!")[1].split("@")[1]
        self.recvinfo["cmd"] = line.split(" ")[1]
        self.recvinfo["target"] = line.split(" ")[2]
        self.recvinfo["message"] = ":".join(" ".join(line.split(" ")[3:]).split(":")[1:]).strip() #-echo Hello world.
        self.recvinfo["message.command"] = ""
        self.recvinfo["message.params"] = ""
        if self.recvinfo["message"] != "":
            for each in self.chars():
                if each == '':
                    break
                elif re.match(each,self.recvinfo["message"]) != None:
                    tmp = self.recvinfo["message"].replace(each,"",1) # echo Hello world.
                    self.recvinfo["message.command"] = tmp.split(" ")[0] # echo
                    self.recvinfo["message.params"] = " ".join(tmp.split(" ")[1:]).strip() # Hello world.
    def chars(self):
        a = (str(self.config.get("me","char"))+'|').replace('nick',self.vars['nick']).split('|')
        a.remove('')
        return a
Svetlana Tkachenko's avatar
Svetlana Tkachenko committed
class clisten():
    def __init__(self,c):
        self.conn=c
        if hasattr(self,"addcmds"):
            self.addcmds()
    def listen(self):
        if (self.conn.recvinfo["message.command"] in self.cmds) == False: return
        if hasattr(self,"clisten"): self.clisten()
        if self.conn.recvinfo["cmd"] != "PRIVMSG": return
        if self.conn.recvinfo["message.params"] == "":
Svetlana Tkachenko's avatar
Svetlana Tkachenko committed
            self.conn.reply(getattr(self,self.cmds[self.conn.recvinfo["message.command"]]).__doc__)
        else:
            try:
                getattr(self,self.cmds[self.conn.recvinfo["message.command"]])()
            except Exception,e:
                self.conn.reply(str(e))