Ver Fonte

first version with database

tBKwtWS há 7 anos atrás
pai
commit
8f33615a5f

BIN
__pycache__/commands.cpython-35.pyc


BIN
__pycache__/ircservices.cpython-35.pyc


BIN
commands/__pycache__/admin.cpython-36.pyc


BIN
commands/__pycache__/common.cpython-36.pyc


BIN
commands/__pycache__/games.cpython-36.pyc


BIN
commands/__pycache__/public.cpython-35.pyc


BIN
commands/__pycache__/public.cpython-36.pyc


+ 195 - 0
commands/admin.py

@@ -0,0 +1,195 @@
+from commands.common import CommandHelpers as CH
+bold = "\x02"
+italic = "\x1D"
+underline = "\x1F"
+reverse = "\x16" 	# swap background and foreground colors ("reverse video")
+reset = "\x0F"
+blue = "\x0302"
+green = "\x0303"
+red = "\x0304"
+grey = "\x0314"
+
+def do_command(self, connection, event):
+    cmdtype, trigger, command, replyto = CH.disect_command(self, event)
+    
+    # Ignore channel commands from users that do not have at least voice in homechannel or operator status in target channel.
+    if event.type == "pubmsg":   # It's a channel message.
+        if not self.channels[self.homechannel].is_owner(event.source.nick) and not self.channels[self.homechannel].s_admin(event.source.nick) and not self.channels[self.homechannel].is_oper(event.source.nick) and not self.channels[event.target].is_owner(event.source.nick) and not self.channels[event.target].is_admin(event.source.nick) and not self.channels[event.target].is_oper(event.source.nick): # Does not have at least voiced status in homechannel or operator status in target channel.
+            return
+    
+    if command == "cmd" or command == "commands":
+        if cmdtype == "cmd":
+            connection.privmsg(replyto, grey + "Admin: " + CH.ccc(self, "channelfunctions",  {"homechan": "oper", "chan": "oper"}, event) + CH.ccc(self, "join", {"homechan": "oper",  "chan": None}, event) + CH.ccc(self, "die",  {"homechan": "admin",  "chan": None}, event) + CH.ccc(self, "reconnect", {"homechan": "oper",  "chan": None}, event) + CH.ccc(self, "recovernick",  {"homechan": "oper",  "chan": None}, event)+ CH.ccc(self, "msg",  {"homechan": "oper",  "chan": "oper"}, event) + CH.ccc(self, "act", {"homechan": "oper",  "chan": "oper"}, event)[:-2] + ".")
+    
+    elif command.split()[0] == "channelfunctions":
+        if cmdtype == "help":    #Display help text. # Help code block first, as it is impossible to predict for what channel a later command is going to be issued. Rights filtering after help test.
+            if len(command.split()) is not 1:
+                return
+            connection.privmsg(replyto, "Display or toggle the status channel functions. Channel, function and value optional.")
+            connection.privmsg(replyto, grey + "Usage: " + blue + "!channelfunctions " + red + italic + "channel " + reset + italic + "function value")
+        elif cmdtype == "cmd":
+            if len(command.split()) == 1:   # No arguments.
+                if event.target == connection.get_nickname():   # Command issued via PM.
+                    connection.privmsg(replyto, "Nothing to display, Specify a channel.")
+                else:   # Command issued as channel message.
+                    message = CH.get_channelfunctions(self, event.target)
+                    connection.privmsg(replyto, message)
+            elif len(command.split()) == 2:   # One argument.
+                if command.split()[1] in self.channels: # Info requested on specific channel.
+                    message = CH.get_channelfunctions(self, command.split()[1])
+                    connection.privmsg(replyto, message)
+                else:   # First argument is not a channel the bot inhabits.
+                    connection.privmsg(replyto, command.split()[1] + " is not a channel I inhabit. For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
+            elif len(command.split()) == 3: # Two arguments.
+                if event.target == connection.get_nickname():   # Command issued via PM.
+                    connection.privmsg(replyto, "One or three arguments required. For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
+                else:   # Command issued via channel.
+                    if not CH.is_channelfunction(self, command.split()[1]):   # First argument is not a channelfunction.
+                        connection.privmsg(replyto, command.split()[1] + " is not a channel function. For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
+                        return
+                    if not command.split()[2].lower() in ["on",  "off"]:    # Second argument is not "on" or "off".
+                        connection.privmsg(replyto, "The value of this channel function can only be \"on\" or \"off\". For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
+                        return
+                    if not self.channels[self.homechannel].is_owner(event.source.nick) and not self.channels[self.homechannel].is_admin(event.source.nick) and not self.channels[self.homechannel].is_oper(event.source.nick) and not self.channels[event.target].is_owner(event.source.nick) and not self.channels[event.target].is_admin(event.source.nick) and not self.channels[event.target].is_oper(event.source.nick):   # Does not have operator status or higher in target or home channel.
+                        connection.privmsg(replyto, "Denied. You need to have at least operator status in " + red + event.target + reset + " or " + red + self.homechannel + reset + ".")
+                        return
+                    self.db.run("UPDATE channels SET " + command.split()[1] + "='" + command.split()[2].lower() + "' WHERE name='" + event.target + "' AND network='" + self.network + "'")
+            elif len(command.split()) == 4: # Three arguments.
+                if not command.split()[1] in self.channels: # Bot does not inhabit channel to be altered.
+                    connection.privmsg(replyto, command.split()[1] + " is not a channel I inhabit. For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
+                    return
+                if not CH.is_channelfunction(self, command.split()[2]):  # Function does not exist.
+                    connection.privmsg(replyto, command.split()[2] + " is not a valid channel function. For a list help type: " + blue + self.cmdchar + "channelfunctions" + red + italic + "channel")
+                    return
+                if not command.split()[3] in ["on", "off"]: # Third argument is not "on" or "off".
+                    connection.privmsg(replyto, "The value of this channel function can only be \"on\" or \"off\". For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
+                    return
+                if not self.channels[self.homechannel].is_owner(event.source.nick) and not self.channels[self.homechannel].is_admin(event.source.nick) and not self.channels[self.homechannel].is_oper(event.source.nick) and not self.channels[command.split()[1]].is_owner(event.source.nick) and not self.channels[command.split()[1]].is_admin(event.source.nick) and not self.channels[command.split()[1]].is_oper(event.source.nick):   # Does not have operator status or higher in target or home channel.
+                    connection.privmsg(replyto, "Denied. You need to have at least operator status in " + red + event.target + reset + " or " + red + self.homechannel + reset + ".")
+                    return
+                try:
+                    self.db.run("UPDATE channels SET " + command.split()[2] + "='" + command.split()[3] + "' WHERE name='" + command.split()[1] + "' AND network='" + self.network + "'")
+                except:
+                    connection.privmsg(replyto, "Error, database record not updated.")
+                    return
+            else:
+                connection.privmsg(replyto, "Too many arguments. For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
+    
+    elif command.split()[0] == "join":
+        if not self.channels[self.homechannel].is_owner(event.source.nick) and not self.channels[self.homechannel].is_admin(event.source.nick) and not self.channels[self.homechannel].is_oper(event.source.nick):
+            connection.privmsg(replyto, "Denied, you need to have admin (super operator) status or higher in " + red + self.homechannel  + reset + ".")
+            return
+        if cmdtype == "help":    #Display help text.
+            if len(command.split()) is not 1:
+                return
+            connection.privmsg(replyto, "Make " + connection.get_nickname() + " join a channel. Password optional.")
+            connection.privmsg(replyto, grey + "Usage: " + blue + "!join " + red + italic + "channel " + reset + italic + "password")
+        elif cmdtype == "cmd":
+            try:
+                channel = command.split()[1]
+            except IndexError:
+                connection.privmsg(replyto, "Specify channel. For help type: " + blue + self.helpchar + "join")
+                return
+            try:
+                key = command.split(maxsplit=2)[2]
+            except IndexError:
+                connection.join(channel)
+                return
+            print(channel + " | " + key)
+            connection.join(channel, key=key)
+    
+    elif command.split()[0] == "die":
+        if not self.channels[self.homechannel].is_owner(event.source.nick) and not self.channels[self.homechannel].is_admin(event.source.nick):
+            connection.privmsg(replyto, "Denied, you need to have admin (super operator) status or higher in " + red + self.homechannel  + reset + ".")
+            return
+        if cmdtype == "help":    # Display help text.
+            if len(command.split()) is not 1:
+                return
+            connection.privmsg(replyto, "Kill " + connection.get_nickname() + ". Optionally with reason.")
+            connection.privmsg(replyto,  grey + "Usage: " + blue + "!die " + reset + italic + "reason")
+        elif cmdtype == "cmd":
+            if len(command.split()) == 1:
+                self.die(msg = "Killed by " + event.source.nick)
+            else:
+                self.die(msg = "[" + event.source.nick + "] " + command.split(maxsplit=1)[1])
+    
+    elif command.split()[0] == "reconnect":
+        if not self.channels[self.homechannel].is_owner(event.source.nick) and not self.channels[self.homechannel].is_admin(event.source.nick) and not self.channels[self.homechannel].is_oper(event.source.nick): 
+            connection.privmsg(replyto, "Denied, you need to have operator status or higher in " + red + self.homechannel  + reset + ".")
+            return
+        if cmdtype == "help":    # Display help text.
+            if len(command.split()) is not 1:
+                return
+            connection.privmsg(replyto, "Reconnect " + connection.get_nickname() + ". Reason optional.")
+            connection.privmsg(replyto,  grey + "Usage: " + blue + "!reconnect " + reset + italic + "reason")
+        elif cmdtype == "cmd":
+            if len(command.split()) == 1:
+                self.disconnect(msg = "reconnect requested by " + event.source.nick)
+            else:
+                self.disconnect(msg = "[" + event.source.nick + "] " + command.split(maxsplit=1)[1])
+    
+    elif command.split()[0] == "recovernick":
+        if not self.channels[self.homechannel].is_owner(event.source.nick) and not self.channels[self.homechannel].is_admin(event.source.nick) and not self.channels[self.homechannel].is_oper(event.source.nick) and not self.channels[self.homechannel].is_halfop(event.source.nick) and not self.channels[self.homechannel].is_voiced(event.source.nick):
+            connection.privmsg(replyto, "Denied, you need to have voiced status or higher in " + red + self.homechannel  + reset + ".")
+            return
+        if cmdtype == "help":    # Display help text.
+            if len(command.split()) is not 1:
+                return
+            connection.privmsg(replyto, "Let " + connection.get_nickname() + " try to recover " + connection.nickname + " as nickname.")
+        elif cmdtype == "cmd":
+            if connection.get_nickname() == connection.nickname:
+                connection.action(replyto,  "is already named " + red + connection.nickname + reset + ".")
+                return
+            from common.networkservices import NickServ
+            NickServ.recover_nick(connection, self.password)
+    
+    elif command.split()[0] == "msg" or  command.split(maxsplit=1)[0] == "act":
+        if cmdtype == "help":    #Display help text.
+            if len(command.split()) is not 1:
+                return
+            if command.split(maxsplit=1)[0] == "act":
+                message = "Let " + connection.get_nickname() + " send an action to a channel."
+                arguments = "action-message"
+            else:
+                message = "Let " + connection.get_nickname() + "send a message to a channel."
+                arguments = "name message"
+            connection.privmsg(replyto, message)
+            connection.privmsg(replyto, grey + "Usage: " + blue + "!" + command.split(maxsplit=1)[0] + " " + reset + italic + arguments)
+        elif cmdtype == "cmd":
+            
+            # Parse user input.
+            try:
+                destination = trigger.split()[1]
+            except IndexError:  # User did not specify a destination.
+                connection.privmsg(replyto, "Destination not specified. For help type: " + blue + self.helpchar + command.split(maxsplit=1)[0])
+                return
+            try:
+                message = trigger.split(maxsplit=2)[2]
+            except IndexError:  # User did not specify a message.
+                connection.privmsg(replyto, "Message not specified. For help type: " + blue + self.helpchar + command.split(maxsplit=1)[0])
+                return
+            
+            # Send the message if user has owner status in the home channel.
+            if self.channels[self.homechannel].is_owner(event.source.nick): 
+                if destination == connection.get_nickname():
+                    connection.privmsg(replyto, "To prevent dying in a loop I shall not message myself.")
+                    return
+                if command.split(maxsplit=1)[0] == "act":
+                    connection.action(destination, message)
+                else:
+                    connection.privmsg(destination, message)
+            
+            # Reply error when bot does not inhabit destination channel.
+            elif destination not in self.channels:
+                connection.action(replyto, "does not inhabit " + red + destination + reset + ".")
+            
+            # Send message if user has at least operator status the home channel or the channel the message is intended for.
+            elif self.channels[self.homechannel].is_owner(event.source.nick) or self.channels[self.homechannel].is_admin(event.source.nick) or self.channels[self.homechannel].is_oper(event.source.nick) or self.channels[destination].is_owner(event.source.nick) or self.channels[destination].is_admin(event.source.nick) or self.channels[destination].is_oper(event.source.nick):
+                if command.split(maxsplit=1)[0] == "act":
+                    connection.action(destination, message)
+                else:
+                    connection.privmsg(destination, message)
+            
+            # Reply error if user is not operator of destination channel.
+            else:
+                connection.privmsg(replyto, "Denied, you need to be an operator of " + red + destination + reset +".")

+ 88 - 0
commands/common.py

@@ -0,0 +1,88 @@
+import random
+bold = "\x02"
+italic = "\x1D"
+underline = "\x1F"
+reverse = "\x16" 	# swap background and foreground colors ("reverse video")
+reset = "\x0F"
+blue = "\x0302"
+green = "\x0303"
+red = "\x0304"
+grey = "\x0314"
+
+class CommandHelpers():
+    def disect_command(self, event):
+        trigger = event.arguments[0]
+        command = trigger[1:]   #Command without prefix.
+        
+        # Determine command type.
+        if trigger.startswith(self.cmdchar):
+            cmdtype = "cmd"
+        elif trigger.startswith(self.helpchar):
+            cmdtype = "help"
+        else:
+            cmdtype = False
+        
+        # Determine where to reply to.
+        if event.type == "pubmsg":
+            replyto = event.target
+        elif event.type == "privmsg":
+            replyto = event.source.nick
+        else:
+            replyto = False
+            
+        return(cmdtype, trigger, command, replyto)
+    
+    def ccc(self, command, rights=False, event=False):   # Commandlist colour coding and channel rights filter.
+        if rights:
+            if rights["homechan"] == "owner":
+                if self.channels[self.homechannel].is_owner(event.source.nick):
+                    show = True
+            if rights["homechan"] == "admin":
+                if self.channels[self.homechannel].is_owner(event.source.nick) or self.channels[self.homechannel].is_admin(event.source.nick):
+                    show = True
+            if rights["homechan"] == "oper":
+                if self.channels[self.homechannel].is_owner(event.source.nick) or self.channels[self.homechannel].is_admin(event.source.nick) or self.channels[self.homechannel].is_oper(event.source.nick):
+                    show = True
+            if rights["chan"] == "owner":
+                if self.channels[event.target].is_owner(event.source.nick):
+                    show = True
+            if rights["chan"] == "admin":
+                if self.channels[event.target].is_owner(event.source.nick) or self.channels[event.target].is_admin(event.source.nick):
+                    show = True
+            if rights["chan"] == "oper":
+                if self.channels[event.target].is_owner(event.source.nick) or self.channels[event.target].is_admin(event.source.nick) or self.channels[event.target].is_oper(event.source.nick):
+                    show = True
+            if show:
+                return blue + self.cmdchar + command + reset + ", "
+        else:
+            return blue + self.cmdchar + command + reset + ", "
+    
+    def get_channelfunctions(self, channel):
+        channelfunctions = self.db.one("SELECT join_greeting, statistics_commands, games FROM channels WHERE name='" + channel + "' AND network='" + self.network + "'")
+        if channelfunctions[0]:
+            joingreet = "on"
+        else:
+            joingreet = "off"
+        if channelfunctions[1]:
+            statscmds = "on"
+        else:
+            statscmds = "off"
+        if channelfunctions[2]:
+            games = "on"
+        else:
+            games = "off"
+        return ("join_greeting " + joingreet + ", statistics_commands " + statscmds + ", games " + games + ".")
+        
+    def is_channelfunction(self, value):
+        if value not in ["join_greeting",  "statistics_commands",  "games"]:
+            return False
+        else:
+            return True
+
+class GameHelpers():
+    def roll_dice(amount, type):
+        rolls = []
+        for iterations in range(amount):
+            rolls.append(random.randint(1,  type))
+        return rolls
+    

+ 117 - 0
commands/games.py

@@ -0,0 +1,117 @@
+import random
+from commands.common import CommandHelpers as CH
+from commands.common import GameHelpers
+bold = "\x02"
+italic = "\x1D"
+underline = "\x1F"
+reverse = "\x16" 	# swap background and foreground colors ("reverse video")
+reset = "\x0F"
+blue = "\x0302"
+green = "\x0303"
+red = "\x0304"
+grey = "\x0314"
+
+def do_command(self, connection, event):
+    cmdtype, trigger, command, replyto = CH.disect_command(self, event)
+    
+    if command == "cmd" or command == "commands":
+        if cmdtype == "cmd":
+            connection.privmsg(replyto, grey + "Games: " + CH.ccc(self, "8ball") + CH.ccc(self, "dice")[:-2] + ".")
+    
+    elif command.split()[0] == "8ball":
+        if cmdtype == "help":    #Display help text.
+            connection.privmsg(replyto, "Ask a question of the mighty and illusive 8-Ball.")
+            connection.privmsg(replyto, grey + "Usage: " + blue + "!8ball " + reset + italic + "question")
+        elif cmdtype == "cmd":
+            if len(command.split()) < 2:    # Command contains only !8ball.
+                messages = [
+                    "Don't forget to ask a question...", 
+                    "Hey, that's not a question!", 
+                    "What would you like to know?", 
+                    "You want me to predict nothing?", 
+                    "Are you intentionally not asking a question?", 
+                    "Ask a question you tease!", 
+                    "You do not seem to grasp this, for help type: " + blue + self.helpchar + "8ball", 
+                    "You will die alone.", 
+                ]
+                connection.privmsg(replyto, random.choice(messages))
+            else:
+                messages = [
+                    "Yes.", 
+                    "No.", 
+                    "Affirmative.",
+                    "No way!", 
+                    "Negative.", 
+                    "Positive.", 
+                    "Correct.", 
+                    "Incorrect.", 
+                    "Likely", 
+                    "Unlikely", 
+                    "Maybe.", 
+                    "Definately!", 
+                    "Perhaps?", 
+                    "Most indubitably.", 
+                    "Does the pope shit in the woods?", 
+                    "When hell freezes over.", 
+                    "Only between 9 and 5.", 
+                    "Only just before you die.", 
+                    red + bold + "ERROR: " + bold + "probability failure.", 
+                    "Ask again later.", 
+                    "I don't know.", 
+                    "Unpredictable.", 
+                ]
+                connection.privmsg(replyto, random.choice(messages))
+    
+    elif command.split()[0] == "dice":
+        if cmdtype == "help":    #Display help text.
+            if len(command.split()) is not 1:
+                return
+            connection.privmsg(replyto, "Rolls multiple dices of chosen type. Specifying type or amount is optional.")
+            connection.privmsg(replyto, grey + "Usage: " + blue + "!dice " + reset + italic + "amount type")
+            connection.privmsg(replyto, grey + "Example: " + blue + "!dice " + reset + italic + "2 d10")
+            connection.privmsg(replyto, grey + "Note: " + reset + "The dice type is specified as a " + italic + "d " + reset + "followed by the amount of sides.")
+        elif cmdtype == "cmd":
+            
+            arguments = len(command.split()) - 1
+            if arguments == 0: 
+                connection.privmsg(replyto,  str(random.randint(1,  6)) + " & " + str(random.randint(1,  6)) + ".")
+            elif arguments == 1:
+                try:
+                    diceamount = int(command.split()[1])
+                except ValueError:  # Argument is not an integer.
+                    if command.split()[1].startswith("d"):    # Specific dice type.
+                        try:
+                            dicetype = int(command.split()[1][1:])
+                        except ValueError:  # "d" not followd by interger.
+                            connection.privmsg(replyto, "Invalid number or type of dice. For help type: " + blue + self.helpchar + "dice" + reset + ".")
+                            return
+                        if dicetype == 0 :
+                            connection.action(replyto, "can not create objects with less then one side.")
+                        else:
+                            connection.privmsg(replyto, str(GameHelpers.roll_dice(1,  dicetype)[0]))
+                    else:   # Argument does not start with "d" and is not an integer.
+                        connection.privmsg(replyto, "Invalid number or type of dice. For help type: " + blue + self.helpchar + "dice" + reset + ".")
+                    return
+                if diceamount == 0:
+                    connection.privmsg(replyto, "Rolling " + bold + "no " + reset + "dice.")
+                else:
+                    connection.privmsg(replyto, ", ".join(str(rolls) for rolls in GameHelpers.roll_dice(diceamount,  6)) + ".") # Roll x amount of dice.
+            elif arguments == 2:
+                try:
+                    diceamount = int(command.split()[1])
+                except ValueError:  # First argument not an integer.
+                    connection.privmsg(replyto, "Invalid number of dice. For help type: " + blue + self.helpchar + "dice" + reset + ".")
+                    return
+                try:
+                    dicetype = int(command.split()[2][1:])
+                except ValueError:  # Second argument not a dice type.
+                    connection.privmsg(replyto, "Invalid type of dice. For help type: " + blue + self.helpchar + "dice" + reset + ".")
+                    return
+                if diceamount == 0:
+                    connection.privmsg(replyto, "Rolling " + bold + "no " + reset + "dice.")
+                elif dicetype == 0 :
+                    connection.action(replyto, "can not create objects with less then one side.")
+                else:
+                    connection.privmsg(replyto, ", ".join(str(rolls) for rolls in GameHelpers.roll_dice(diceamount,  dicetype)) + ".") # Roll x amount of x type dice.
+            else:   # Invalid amount of arguments.
+                connection.privmsg(replyto, "Too many arguments. For help type: " + blue + self.helpchar + "dice" + reset + ".")

+ 90 - 0
commands/public.py

@@ -0,0 +1,90 @@
+from commands.common import CommandHelpers as CH
+bold = "\x02"
+italic = "\x1D"
+underline = "\x1F"
+reverse = "\x16" 	# swap background and foreground colors ("reverse video")
+reset = "\x0F"
+blue = "\x0302"
+green = "\x0303"
+red = "\x0304"
+grey = "\x0314"
+
+def do_command(self, connection, event):
+    cmdtype, trigger, command, replyto = CH.disect_command(self, event)
+    
+    # The actual commands:
+    if command == "test":
+        if cmdtype == "help":    #Display help text.
+            connection.privmsg(replyto, "Strictly for testing purposes only!")
+        elif cmdtype == "cmd":
+            
+            connection.privmsg(replyto, event)
+            
+    elif command == "cmd" or command == "commands":
+        if cmdtype == "help":    #Display help text.
+            connection.privmsg(replyto, "Displays a list of commands.")
+        elif cmdtype == "cmd":
+            connection.privmsg(replyto, grey + "Commands: " + CH.ccc(self, "cmd") + CH.ccc(self, "help") + CH.ccc(self, "joins")[:-2] + ".")
+    
+    elif command == "help":
+        if cmdtype == "help":    #Display help text.
+            connection.privmsg(replyto, "Explains how to get help on any specific command and hints the user to the commandlist.")
+        elif cmdtype == "cmd":
+            connection.privmsg(replyto, "Replace the " + italic + "! " + reset + "prefix of any comand with " + italic + self.helpchar + " " + reset + "for help with a specific command. Request the command list with: " + blue + "!cmd")
+            connection.privmsg(replyto, grey + "Example: " + reset + blue + self.helpchar + "help")
+    
+    
+    elif command.split()[0] == "joins":
+        if cmdtype == "help":    #Display help text.
+            connection.privmsg(replyto, "Display amount of joins of user and or channel. Channel and user optional.")
+            connection.privmsg(replyto, grey + "Usage: " + blue + "!joins " + reset + italic + "channel user")
+        elif cmdtype == "cmd":
+            
+            # Parse user input
+            if len(command.split()) < 2:    # Command contains only !joins.
+                user = event.source.nick
+                if connection.get_nickname() is not event.target:
+                    channel = event.target
+            elif len(command.split()) < 3:  # Command has one argument.
+                if command.split()[1] in self.channels:
+                    channel = command.split()[1]
+                else:
+                    user = command.split()[1]
+            elif len(command.split()) < 4: # Command has two arguments.
+                if not command.split()[1] in self.channels: # Bot does not inhabit requested channel.
+                    if not command.split()[2] in self.channels: # User did not revert channel and user in command syntax.
+                        connection.action(replyto, "does not inhabit " + red + command.split()[1] + reset + ".")
+                        return
+                    else:   # User reverted user and channel in command syntax.
+                        user = command.split()[1]
+                        channel = command.split()[2]
+                else: # Bot does inhabit requested channel.
+                    user = command.split()[2]
+                    channel = command.split()[1]
+            elif len(command.split()) < 5:  # To many arguments
+                connection.privmsg(replyto, "To many arguments. For help type " + blue + self.helpchar + "joins" + reset + ".")
+                return
+            try: 
+                if user and channel:
+                    userjoins = str(self.db.one("SELECT joins FROM joins WHERE channel_network='" + self.network + "' AND \"user\"='" + user + "' AND user_network='" + self.network + "'"))
+                    userchanneljoins = str(self.db.one("SELECT joins FROM joins WHERE channel='" + channel + "' AND channel_network='" + self.network + "' AND \"user\"='" + user + "' AND user_network='" + self.network + "'"))
+                    channeljoins = str(sum(self.db.all("SELECT joins FROM joins WHERE channel='" + channel + "' AND channel_network='" + self.network + "' AND user_network='" + self.network + "'")))
+                    connection.privmsg(replyto, red + user + reset + " has " + green + userjoins + reset + " joins. Of which " + green + userchanneljoins + reset + " have been in " + red + channel + reset + ", that has " + green + channeljoins + reset + " joins" + reset + " in total.")
+                    return
+            except:
+                pass
+            try:
+                if user:
+                    userjoins = str(self.db.one("SELECT joins FROM joins WHERE channel='" + event.target + "' AND channel_network='" + self.network + "' AND \"user\"='" + event.source.nick + "' AND user_network='" + self.network + "'"))
+                    connection.privmsg(replyto,  red + user + reset + " has " + green + userjoins + reset + " joins in channels I monitor.")
+                    return
+            except:
+                pass
+            try:
+                if channel:
+                    channeljoins = str(sum(self.db.all("SELECT joins FROM joins WHERE channel='" + channel + "' AND channel_network='" + self.network + "' AND user_network='" + self.network + "'")))
+                    connection.privmsg(replyto,  red + channel + reset + " has been joined " + green + channeljoins + reset + " times.")
+                    return
+            except:
+                    pass
+            connection.privmsg(replyto, "tBKwtWS Was wondering if this programming was sloppy. When you see this message, it was.")

BIN
common/__pycache__/networkservices.cpython-35.pyc


BIN
common/__pycache__/networkservices.cpython-36.pyc


+ 6 - 0
common/networkservices.py

@@ -0,0 +1,6 @@
+class NickServ():
+    def recover_nick(connection, password):
+        connection.privmsg("NickServ", "identify " + connection.nickname + " " + password) # Identify with NickServ.
+        connection.privmsg("NickServ", "recover " + connection.nickname + " " + password)  # Recover control of nickname via NickServ.
+        connection.nick(connection.nickname)    # Set original nickname. Should have happened during the nickServ recover, this fails when still connecting. So this creates a loop to successfully recover via NickServ.
+

BIN
events/__pycache__/on_join.cpython-36.pyc


+ 47 - 0
events/on_join.py

@@ -0,0 +1,47 @@
+bold = "\x02"
+italic = "\x1D"
+underline = "\x1F"
+reverse = "\x16" 	# swap background and foreground colors ("reverse video")
+reset = "\x0F"
+blue = "\x0302"
+green = "\x0303"
+red = "\x0304"
+grey = "\x0314"
+
+def process_event(self, connection, event):
+    # Add join event to database for statistics.
+    if not self.db.one("SELECT id FROM channels WHERE name='" + event.target + "' AND network='" + self.network + "'"): # Channel does not exist in database.
+        print("INFO Adding channel " + event.target + " to the database.")
+        self.db.run("INSERT INTO channels (name, network) VALUES ('" + event.target + "', '" + self.network + "')")
+    if not self.db.one("SELECT id FROM users WHERE name='" + event.source.nick + "' AND network='" + self.network + "'"):   # User does not exist in database.
+        print("INFO Adding user " + event.source.nick + " to the database.")
+        self.db.run("INSERT INTO users (name, network) VALUES ('" + event.source.nick + "', '" + self.network + "')")
+    if not self.db.one("SELECT id FROM joins WHERE channel='" + event.target + "' AND channel_network='" + self.network + "' AND \"user\"='" + event.source.nick + "' AND user_network='" + self.network + "'"):    # No record yet
+        self.db.run("INSERT INTO joins (channel, channel_network, \"user\", user_network, joins) VALUES ('" + event.target + "', '" + self.network + "', '" + event.source.nick + "', '" + self.network + "', 0)")
+    self.db.run("UPDATE joins SET joins = joins + 1 WHERE channel='" + event.target + "' AND channel_network='" + self.network + "' AND \"user\"='" + event.source.nick + "' AND user_network='" + self.network + "'")
+    
+    if event.source.nick == connection.get_nickname():  # The bot joined a channel.
+        return
+    # If channelsettings permit show welcome message.
+    joins = self.db.one("SELECT joins FROM joins WHERE channel='" + event.target + "' AND channel_network='" + self.network + "' AND \"user\"='" + event.source.nick + "' AND user_network='" + self.network + "'")
+    if not self.db.one("SELECT join_greeting FROM channels WHERE name='" + event.target + "' AND network ='" + self.network + "'"): # Do not greet users joining the channel.
+        return
+    if joins == 1:
+        message = "Welcome to " + red + event.target + reset + ", " + red + event.source.nick + reset + ". For a list of command type " + blue + self.cmdchar + "!cmd" + reset + "."
+    if joins == 3:
+        message = "Welcome back in " + red + event.target + reset + ", " + red + event.source.nick + reset + ". Tip: To turn of greetings, type " + blue + "!stopgreet " + reset + "."
+    if joins == 5:
+        if self.channels[event.target].has_key():   # Channel has a password.
+            message = "Welcome back again " + red + event.source.nick + reset + ". To automaticly join this channel type " + blue + "/ns ajoin " + reset + "ADD " + red + event.source.nick + " " + event.target + reset + italic + " password"
+        else:   # Channel does not have a password.
+            message = "Welcome back again " + red + event.source.nick + reset + ". To automaticly join this channel type " + blue + "/ns ajoin " + reset + "ADD " + red + event.source.nick + " " + event.target
+    if joins < 100 and str(joins)[-1:] == "0":
+        message = red + event.source.nick + reset + " you have joined " + red + event.target + reset + " " + joins + " times!"
+    if joins < 1000 and str(joins)[-2:] == "00" or joins < 1000 and str(joins)[-2:] == "50":
+        message = "Epic! " + red + event.source.nick + reset + " you have joined " + red + event.target + reset + " " + joins + " times!"
+    if joins < 10000 and str(joins)[-2:] == "00":
+        message = "AMAZING! " + red + event.source.nick + reset + " you have joined " + red + event.target + reset + " " + joins + " times!"
+    try:
+        connection.privmsg(event.target, message)
+    except:
+        pass

+ 323 - 0
pyrot.database.schema.sql

@@ -0,0 +1,323 @@
+--
+-- PostgreSQL database dump
+--
+
+-- Dumped from database version 9.6.6
+-- Dumped by pg_dump version 9.6.6
+
+SET statement_timeout = 0;
+SET lock_timeout = 0;
+SET idle_in_transaction_session_timeout = 0;
+SET client_encoding = 'UTF8';
+SET standard_conforming_strings = on;
+SET check_function_bodies = false;
+SET client_min_messages = warning;
+SET row_security = off;
+
+--
+-- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: 
+--
+
+CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
+
+
+--
+-- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: 
+--
+
+COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';
+
+
+SET search_path = public, pg_catalog;
+
+SET default_tablespace = '';
+
+SET default_with_oids = false;
+
+--
+-- Name: channels; Type: TABLE; Schema: public; Owner: pyRot
+--
+
+CREATE TABLE channels (
+    id integer NOT NULL,
+    name character varying(64) NOT NULL,
+    network character varying(40) NOT NULL,
+    autojoin boolean,
+    join_greeting boolean,
+    statistics_commands boolean,
+    games boolean
+);
+
+
+ALTER TABLE channels OWNER TO "pyRot";
+
+--
+-- Name: channel_id_seq; Type: SEQUENCE; Schema: public; Owner: pyRot
+--
+
+CREATE SEQUENCE channel_id_seq
+    START WITH 1
+    INCREMENT BY 1
+    NO MINVALUE
+    NO MAXVALUE
+    CACHE 1;
+
+
+ALTER TABLE channel_id_seq OWNER TO "pyRot";
+
+--
+-- Name: channel_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: pyRot
+--
+
+ALTER SEQUENCE channel_id_seq OWNED BY channels.id;
+
+
+--
+-- Name: joins; Type: TABLE; Schema: public; Owner: pyRot
+--
+
+CREATE TABLE joins (
+    id integer NOT NULL,
+    channel character varying(64) NOT NULL,
+    channel_network character varying(40) NOT NULL,
+    "user" character varying(31) NOT NULL,
+    user_network character varying(40) NOT NULL,
+    joins bigint DEFAULT 0
+);
+
+
+ALTER TABLE joins OWNER TO "pyRot";
+
+--
+-- Name: joins_id_seq; Type: SEQUENCE; Schema: public; Owner: pyRot
+--
+
+CREATE SEQUENCE joins_id_seq
+    START WITH 1
+    INCREMENT BY 1
+    NO MINVALUE
+    NO MAXVALUE
+    CACHE 1;
+
+
+ALTER TABLE joins_id_seq OWNER TO "pyRot";
+
+--
+-- Name: joins_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: pyRot
+--
+
+ALTER SEQUENCE joins_id_seq OWNED BY joins.id;
+
+
+--
+-- Name: networks; Type: TABLE; Schema: public; Owner: pyRot
+--
+
+CREATE TABLE networks (
+    id integer NOT NULL,
+    name character varying(40) NOT NULL,
+    host character varying(60) NOT NULL,
+    port integer DEFAULT 6667,
+    use_ssl boolean,
+    nickname character varying(31) DEFAULT 'RotBot'::character varying,
+    username character varying(31) DEFAULT 'pyRot'::character varying,
+    password character varying(31),
+    home_channel character varying(64) DEFAULT '#RotBot'::character varying NOT NULL,
+    command_character character(1) DEFAULT '!'::bpchar,
+    help_character character(1) DEFAULT '@'::bpchar
+);
+
+
+ALTER TABLE networks OWNER TO "pyRot";
+
+--
+-- Name: networks_id_seq; Type: SEQUENCE; Schema: public; Owner: pyRot
+--
+
+CREATE SEQUENCE networks_id_seq
+    START WITH 1
+    INCREMENT BY 1
+    NO MINVALUE
+    NO MAXVALUE
+    CACHE 1;
+
+
+ALTER TABLE networks_id_seq OWNER TO "pyRot";
+
+--
+-- Name: networks_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: pyRot
+--
+
+ALTER SEQUENCE networks_id_seq OWNED BY networks.id;
+
+
+--
+-- Name: users; Type: TABLE; Schema: public; Owner: pyRot
+--
+
+CREATE TABLE users (
+    id integer NOT NULL,
+    name character varying(31) NOT NULL,
+    network character varying(40) NOT NULL
+);
+
+
+ALTER TABLE users OWNER TO "pyRot";
+
+--
+-- Name: users_id_seq; Type: SEQUENCE; Schema: public; Owner: pyRot
+--
+
+CREATE SEQUENCE users_id_seq
+    START WITH 1
+    INCREMENT BY 1
+    NO MINVALUE
+    NO MAXVALUE
+    CACHE 1;
+
+
+ALTER TABLE users_id_seq OWNER TO "pyRot";
+
+--
+-- Name: users_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: pyRot
+--
+
+ALTER SEQUENCE users_id_seq OWNED BY users.id;
+
+
+--
+-- Name: channels id; Type: DEFAULT; Schema: public; Owner: pyRot
+--
+
+ALTER TABLE ONLY channels ALTER COLUMN id SET DEFAULT nextval('channel_id_seq'::regclass);
+
+
+--
+-- Name: joins id; Type: DEFAULT; Schema: public; Owner: pyRot
+--
+
+ALTER TABLE ONLY joins ALTER COLUMN id SET DEFAULT nextval('joins_id_seq'::regclass);
+
+
+--
+-- Name: networks id; Type: DEFAULT; Schema: public; Owner: pyRot
+--
+
+ALTER TABLE ONLY networks ALTER COLUMN id SET DEFAULT nextval('networks_id_seq'::regclass);
+
+
+--
+-- Name: users id; Type: DEFAULT; Schema: public; Owner: pyRot
+--
+
+ALTER TABLE ONLY users ALTER COLUMN id SET DEFAULT nextval('users_id_seq'::regclass);
+
+
+--
+-- Name: channels channel_pkey; Type: CONSTRAINT; Schema: public; Owner: pyRot
+--
+
+ALTER TABLE ONLY channels
+    ADD CONSTRAINT channel_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: channels channels_name_network_key; Type: CONSTRAINT; Schema: public; Owner: pyRot
+--
+
+ALTER TABLE ONLY channels
+    ADD CONSTRAINT channels_name_network_key UNIQUE (name, network);
+
+
+--
+-- Name: joins joins_channel_channel_network_user_user_network_key; Type: CONSTRAINT; Schema: public; Owner: pyRot
+--
+
+ALTER TABLE ONLY joins
+    ADD CONSTRAINT joins_channel_channel_network_user_user_network_key UNIQUE (channel, channel_network, "user", user_network);
+
+
+--
+-- Name: joins joins_pkey; Type: CONSTRAINT; Schema: public; Owner: pyRot
+--
+
+ALTER TABLE ONLY joins
+    ADD CONSTRAINT joins_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: networks networks_host_key; Type: CONSTRAINT; Schema: public; Owner: pyRot
+--
+
+ALTER TABLE ONLY networks
+    ADD CONSTRAINT networks_host_key UNIQUE (host);
+
+
+--
+-- Name: networks networks_name_key; Type: CONSTRAINT; Schema: public; Owner: pyRot
+--
+
+ALTER TABLE ONLY networks
+    ADD CONSTRAINT networks_name_key UNIQUE (name);
+
+
+--
+-- Name: networks networks_pkey; Type: CONSTRAINT; Schema: public; Owner: pyRot
+--
+
+ALTER TABLE ONLY networks
+    ADD CONSTRAINT networks_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: users users_name_network_key; Type: CONSTRAINT; Schema: public; Owner: pyRot
+--
+
+ALTER TABLE ONLY users
+    ADD CONSTRAINT users_name_network_key UNIQUE (name, network);
+
+
+--
+-- Name: users users_pkey; Type: CONSTRAINT; Schema: public; Owner: pyRot
+--
+
+ALTER TABLE ONLY users
+    ADD CONSTRAINT users_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: channels channels_network_fkey; Type: FK CONSTRAINT; Schema: public; Owner: pyRot
+--
+
+ALTER TABLE ONLY channels
+    ADD CONSTRAINT channels_network_fkey FOREIGN KEY (network) REFERENCES networks(name);
+
+
+--
+-- Name: joins joins_channel_fkey; Type: FK CONSTRAINT; Schema: public; Owner: pyRot
+--
+
+ALTER TABLE ONLY joins
+    ADD CONSTRAINT joins_channel_fkey FOREIGN KEY (channel, channel_network) REFERENCES channels(name, network);
+
+
+--
+-- Name: joins joins_user_fkey; Type: FK CONSTRAINT; Schema: public; Owner: pyRot
+--
+
+ALTER TABLE ONLY joins
+    ADD CONSTRAINT joins_user_fkey FOREIGN KEY ("user", user_network) REFERENCES users(name, network);
+
+
+--
+-- Name: users users_network_fkey; Type: FK CONSTRAINT; Schema: public; Owner: pyRot
+--
+
+ALTER TABLE ONLY users
+    ADD CONSTRAINT users_network_fkey FOREIGN KEY (network) REFERENCES networks(name);
+
+
+--
+-- PostgreSQL database dump complete
+--
+

+ 151 - 152
rotbot.py

@@ -1,56 +1,141 @@
 #! /usr/bin/env python
-#
-# Example program using irc.bot.
-#
-# Joel Rosdahl <joel@rosdahl.net>
 
-"""A simple example bot.
-This is an example bot that uses the SingleServerIRCBot class from
-irc.bot.  The bot enters a channel and listens for commands in
-private messages and channel traffic.  Commands in channel messages
-are given by prefixing the text by the bot name followed by a colon.
-It also responds to DCC CHAT invitations and echos data sent in such
-sessions.
-The known commands are:
-    stats -- Prints some channel information.
-    disconnect -- Disconnect the bot.  The bot will try to reconnect
-                  after 60 seconds.
-    die -- Let the bot cease to exist.
-    dcc -- Let the bot invite you to a DCC CHAT connection.
-"""
-
-import irc.bot
-import irc.strings
-#from irc.bot import Channel
-from irc.client import ip_numstr_to_quad, ip_quad_to_numstr
-
-class RotBot(irc.bot.SingleServerIRCBot):
-    def __init__(self, channel, nickname, server, port=6667):
-        print("Connecting")
-        irc.bot.SingleServerIRCBot.__init__(self, [(server, port)], nickname, nickname)
-        print("Joining channel")
-        self.channel = channel
-
-    def on_nicknameinuse(self, c, e):
-        c.nick(c.get_nickname() + "_")
-
-    def on_welcome(self, c, e):
-        print("Joining channel")
-        c.join(self.channel)
+import sys, random, string, ssl
+import irc.bot, irc.strings
+from irc.client import ip_numstr_to_quad#, ip_quad_to_numstr
+from postgres import Postgres
+import commands.public, commands.admin, commands.games
+import events.on_join
+from common.networkservices import NickServ
+bold = "\x02"
+italic = "\x1D"
+underline = "\x1F"
+reverse = "\x16" 	# swap background and foreground colors ("reverse video")
+reset = "\x0F"
+blue = "\x0302"
+green = "\x0303"
+red = "\x0304"
+grey = "\x0314"
 
+class PyRot(irc.bot.SingleServerIRCBot):
+    def __init__(self, network, db, homechannel, nickname, username, password, host, port=6667, usessl=False, cmdchar="!", helpchar="@"):
+        self.network = network
+        self.db = db
+        self.homechannel = homechannel
+        self.password = password
+        self.cmdchar = cmdchar
+        self.helpchar = helpchar
+        
+        if usessl:
+            factory = irc.connection.Factory(wrapper=ssl.wrap_socket)
+        else:
+            factory = irc.connection.Factory()
+        
+        print("Connecting to " + host + ":" + str(port) + "/" + self.homechannel)
+        try:
+            irc.bot.SingleServerIRCBot.__init__(self, [(host, port)], nickname, username, connect_factory=factory)
+        except irc.client.ServerConnectionError:
+            sys.stderr.write(sys.exc_info()[1])
+    
+    # Events.
+    def on_nicknameinuse(self, connection, event):
+        print("Nickname in use, attempting to recover: " + connection.nickname)
+        connection.nick(connection.nickname + ''.join(random.choice(string.digits) for _ in range(3)))    # Take temporary nick. Without this recovering via NickServ won't work.
+        NickServ.recover_nick(connection, self.password)
+    
+    def on_nick(self,  connection,  event):
+        if event.source.nick == connection.nickname:    # If the nick boing changes is the bots prefered nickname.
+            print("Assuming original nick.")
+            NickServ.recover_nick(connection, self.password)
+    
+    def on_welcome(self, connection, event):
+        print(event)
+        if self.password:
+            connection.privmsg("NickServ", "identify " + connection.nickname + " " + self.password) # Identify with NickServ.
+        print("Joining " + self.homechannel)
+        connection.join(self.homechannel)
+    
+    
+    def on_join(self, connection, event):
+        print(event)
+        events.on_join.process_event(self, connection, event)
+    
+    
     def on_privmsg(self, connection, event):
-        self.do_command(connection,  event)
+        print(str(event))
+        commands.public.do_command(self, connection, event)
+        commands.admin.do_command(self, connection, event)
+        commands.games.do_command(self, connection, event)
+    
+    def on_privnotice(self,  connection,  event):
+        print(str(event))
+        commands.public.do_command(self, connection, event)
+        commands.admin.do_command(self, connection, event)
+        commands.games.do_command(self, connection, event)
+        if event.source.nick == NickServ and event.arguments[0].startswith("This nickname is registered"):
+            connection.privmsg("NickServ", "identify " + connection.nickname + " " + connection.password) # Identify with NickServ.
 
     def on_pubmsg(self, connection,  event):
-        self.do_command(connection,  event)
-        
+        commands.public.do_command(self, connection, event)
+        commands.admin.do_command(self, connection, event)
+        try:
+            games = self.db.one("SELECT games FROM channels WHERE name='" + event.target + "' AND network='" + self.network + "'")
+        except:
+            pass
+        if games:
+            commands.games.do_command(self, connection, event)
         
-    #def on_pubmsg(self, c, e):
-    #    a = e.arguments[0].split(":", 1)
-    #    if len(a) > 1 and irc.strings.lower(a[0]) == irc.strings.lower(self.connection.get_nickname()):
-    #        self.do_command(e, a[1].strip())
-    #    return
-
+        if connection.get_nickname().lower() in event.arguments[0].lower() and event.source.nick is not connection.get_nickname(): # Bot's name was mentioned
+            
+            messages = [
+                "Hello " + event.source.nick + ".", 
+                "How are you today " + event.source.nick +  "?", 
+                "Piss off " + event.source.nick + "!", 
+                event.source.nick + ", what are you botherring me for?", 
+                "Go bother someone else...", 
+                "Is life treating you fair?", 
+                "What's up?", 
+                "Why are you talking to me?", 
+                "I'm not talking to you!", 
+                "What have you been up to?", 
+                "How is life?", 
+                "What do you wan't from me?", 
+                event.source.nick + ", why are you bothering me?", 
+                event.source.nick + ", when will you stop talking about me?", 
+                event.source.nick + " I hate you!", 
+                "Get bent!", 
+                "Go and cut yourself.", 
+                "Do you think i care about you?", 
+                "Stop nickalerting me " + event.source.nick + ", you wanker!", 
+            ]
+            actions = [
+                "hides!",
+                "dies.", 
+                "runs away.", 
+                "is ignoring that.", 
+                "is not feeling like caring.", 
+                "is away", 
+                "will be ignoring that.", 
+                "is faggaliciouz!! <333", 
+                "likes you! <3", 
+                "looks the other way...", 
+                "does a little dance with " + event.source.nick + ".", 
+                "makes a little love.", 
+                "get's down tonight.", 
+                "thinks SAM Broadcaster sucks raw cocks in hell!", 
+                "is secretly in love with " + event.source.nick + ".", 
+                "tosses " + event.source.nick + "'s salad.", 
+                "tortures " + event.source.nick + " horribly!", 
+                "is smelling like tuna when looking at " + event.source.nick + ".", 
+                "sniffing armpits.. Eew! Smells like " + event.source.nick + ".", 
+                "rapes " + event.source.nick + ".", 
+            ]
+            if random.randint(0, 1) == 0:
+                connection.privmsg(event.target, random.choice(messages))
+            else:
+                connection.action(event.target, random.choice(actions))
+    
+    
     def on_dccmsg(self, c, e):
         # non-chat DCC messages are raw bytes; decode as text
         text = e.arguments[0].decode('utf-8')
@@ -68,113 +153,27 @@ class RotBot(irc.bot.SingleServerIRCBot):
                 return
             self.dcc_connect(address, port)
 
-    def do_command(self, connection, event):
-        cmdchar = "!"
-        helpchar = "@"
-        trigger = event.arguments[0]
-        
-        #Check command prefix
-        if trigger.startswith(cmdchar) or trigger.startswith(helpchar):  #Do nothing if it's not a command.
-            command = trigger[1:]   #Without prefix.
-            help = False
-            if trigger.startswith(helpchar): #It's a help requist.
-                help = True
-            
-            # Set replyto variable for output to multiple protocols
-            if event.type == "pubmsg":
-                replyto = event.target
-            elif event.type == "privmsg":
-                replyto = event.source.nick
-            else:
-                return
-            
-            if command == "cmd":
-                if help:
-                    connection.privmsg(replyto, "Displays a list of commands.")
-                else:
-                    connection.privmsg(replyto, "!cmd, !test, !msg")
-                
-            elif command == "test":
-                if help:
-                    connection.privmsg(replyto, "Strictly for testing purposes only!")
-                else:
-                    connection.privmsg(replyto, str(connection) + " | " + str(event))
-            
-            #elif command == "channel":
-                #channel = Channel()
-                #print("aaa")
-                #print(replyto)
-                    #connection.privmsg(replyto,  event.source.nick)
-                #connection.privmsg(replyto,  str(channel.users(replyto)))
-                    #connection.privmsg(replyto,  is_admin(event.source.nick))
-                    #connection.privmsg(replyto, "A: " + self.channels[0].is_admin(event.source.nick))
-                    
-            elif command.split(maxsplit=1)[0] == "msg":
-                print(1)
-                if help:
-                    print(2)
-                    connection.privmsg(replyto, "Message a user or channel.")
-                    connection.privmsg(replyto, "\x0314Usage: \x0300!msg \x1Dname message")
-                else:
-                    try:
-                        connection.privmsg(command.split()[1], command.split(maxsplit=2)[2])
-                    except IndexError:
-                        connection.privmsg(replyto, "Nothing")
-            
-        #else:  #Parrot mode
-        #    connection.privmsg("#[CP]Specifer", str(trigger.arguments[0].split(maxsplit=1)[0]))
-#    def do_command(self, e, cmd):
-#        nick = e.source.nick
-#        c = self.connection
-#
-#        if cmd == "disconnect":
-#            self.disconnect()
-#        elif cmd == "die":
-#            self.die()
-#        elif cmd == "stats":
-#            for chname, chobj in self.channels.items():
-#                c.notice(nick, "--- Channel statistics ---")
-#                c.notice(nick, "Channel: " + chname)
-#                users = sorted(chobj.users())
-#                c.notice(nick, "Users: " + ", ".join(users))
-#                opers = sorted(chobj.opers())
-#                c.notice(nick, "Opers: " + ", ".join(opers))
-#                voiced = sorted(chobj.voiced())
-#                c.notice(nick, "Voiced: " + ", ".join(voiced))
-#        elif cmd == "dcc":
-#            dcc = self.dcc_listen()
-#            c.ctcp("DCC", nick, "CHAT chat %s %d" % (
-#                ip_quad_to_numstr(dcc.localaddress),
-#                dcc.localport))
-#        else:
-#            c.notice(nick, "Not understood: " + cmd)
-
 def main():
-    #import sys
-    #if len(sys.argv) != 4:
-    #    print(sys.argv)
-    #    print("Usage: testbot <server[:port]> <channel> <nickname>")
-    #    sys.exit(1)
-
-    #s = sys.argv[1].split(":", 1)
-    #server = s[0]
-    #if len(s) == 2:
-    #    try:
-    #        port = int(s[1])
-    #    except ValueError:
-    #        print("Error: Erroneous port.")
-    #        sys.exit(1)
-    #else:
-    #    port = 6667
-    #channel = sys.argv[2]
-    #nickname = sys.argv[3]
-
-    channel = "#[CP]Specifer"
-    nickname = "pyRotBot"
-    server = "irc.gtanet.com"
-    port = 6667
-    bot = RotBot(channel, nickname, server, port)
-    print("Starting bot")
+    
+    # Check system arguments.
+    if len(sys.argv) != 2:
+        print(sys.argv)
+        print("Usage: rotbot <server ID from database>")
+        sys.exit(1)
+    
+    # Check instance. Instance is the database network id.
+    instance = sys.argv[1]
+    db = Postgres("postgres://pyRot:4h8q(.@localhost/pyRot")
+    try:
+        network = db.one("SELECT * FROM networks WHERE id=" + str(instance))
+    except:
+        print("Invalid network ID.")
+        sys.exit(1)
+    if not network:
+        print("Invalid network ID.")
+        sys.exit(1)
+    
+    bot = PyRot(network.name, db, network.home_channel, network.nickname, network.username, network.password, network.host, network.port, network.use_ssl,  network.command_character, network.help_character)
     bot.start()
 
 if __name__ == "__main__":

+ 10 - 0
test.py

@@ -0,0 +1,10 @@
+import threading
+
+def worker():
+    """thread worker function"""
+    print ('Worker')
+    return
+
+for i in range(5):
+    t = threading.Thread(target=worker)
+    t.start()