Bladeren bron

Lots of rotbot refactoring

tBKwtWS 6 jaren geleden
bovenliggende
commit
2c0f150310

+ 17 - 0
requirements.txt

@@ -1,6 +1,23 @@
 Django==2.2.6
 Django==2.2.6
 django-settings-export==1.2.1
 django-settings-export==1.2.1
+importlib-metadata==0.23
+inflect==3.0.2
+irc==17.1
+jaraco.classes==2.0
+jaraco.collections==2.1
+jaraco.functools==2.0
+jaraco.itertools==4.4.2
+jaraco.logging==2.0
+jaraco.stream==2.0
+jaraco.text==3.1
+more-itertools==7.2.0
 pkg-resources==0.0.0
 pkg-resources==0.0.0
+postgres==3.0.0
 psycopg2==2.8.4
 psycopg2==2.8.4
+psycopg2-binary==2.8.4
+psycopg2-pool==1.1
 pytz==2019.2
 pytz==2019.2
+six==1.13.0
 sqlparse==0.3.0
 sqlparse==0.3.0
+tempora==1.14.1
+zipp==0.6.0

+ 181 - 181
rotbot/bot.py

@@ -1,76 +1,76 @@
 #! /usr/bin/env python
 #! /usr/bin/env python
 
 
-import sys, random, string, ssl
-import irc.bot#, irc.strings
-from irc.client import ip_numstr_to_quad#, ip_quad_to_numstr
+import sys, ssl, random#, string,
+import irc.bot#, irc.strings    #https://python-irc.readthedocs.io/en/latest/irc.html#
+#from irc.client import ip_numstr_to_quad#, ip_quad_to_numstr
 from jaraco.stream import buffer
 from jaraco.stream import buffer
-from postgres import Postgres
-import commands.public, commands.admin, commands.games, commands.statistics
+from postgres import Postgres   # https://postgres-py.readthedocs.io/en/latest/
+
+#import commands.public, commands.admin, commands.games, commands.statistics
 import events.on_welcome, events.on_join, events.on_kick, events.on_mode, events.on_pubmsg, events.on_action, events.on_whoreply, events.on_nick
 import events.on_welcome, events.on_join, events.on_kick, events.on_mode, events.on_pubmsg, events.on_action, events.on_whoreply, events.on_nick
-#from common import log
-#from common.networkservices import NickServ
-#from events.common import Lastact, MessageStatistics, Inform
-
-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
+from common import log, font
+from common.networkservices import NickServ
+from events.common import Inform, MessageStatistics #Lastact,
+
+class PyRot(irc.bot.SingleServerIRCBot):
+    def __init__(self, network, db):
+
+        # Globals, so it's not needed to pass network and db with every call.
+        self.network = network
+        self.db = db
+
+        # Pick a random host from the network.
+        hosts = db.all("SELECT * FROM rotbot_host WHERE network_id=%(network)s", network=network.id)
+        host = random.choice(hosts)
+        connect_string = (host.address, host.port)
+
+        irc.client.ServerConnection.buffer_class = buffer.LenientDecodingLineBuffer # Set buffer class.
+
 #         self.protectees = {}
 #         self.protectees = {}
 #         self.channelkeys = {}
 #         self.channelkeys = {}
+
+        # Log and record to database.
+        log.info('Connecting to %s:%s/%s' % (connect_string[0], connect_string[1], network.home_channel))
+        db.run("UPDATE rotbot_host SET connection_attempts = connection_attempts + 1 WHERE id=%s", [network.id])
+
+        # Create connect factory with correct scheme.
+        if host.ssl:
+            factory = irc.connection.Factory(wrapper=ssl.wrap_socket)
+        else:
+            factory = irc.connection.Factory()
+
+        try:
+            irc.bot.SingleServerIRCBot.__init__(self, [connect_string], network.nickname, network.username, connect_factory=factory)    # Connect.
+        except irc.client.ServerConnectionError:    # Connection failure.
+            sys.exit(sys.exc_info()[1]) # Exit.
+
+
+    ## Events.
+
+    def on_nicknameinuse(self, connection, event):
+        log.info('Nickname %s in use, attempting to recover: %s' % (self.network.nickname, connection.nickname))
+        connection.nick(connection.nickname + ''.join(random.choice(string.digits) for _ in range(3)))    # Take temporary nick. In this state recovering via NickServ won't work without changing nickname.
+        NickServ.recover_nick(connection, self.network.password)
+
+    def on_welcome(self, connection, event):
+        events.on_welcome.process_event(self, connection, event)
 #
 #
-#         log.info("Starting pyRot, by tBKwtWS.")
-#         log.info("Connecting to " + host + ":" + str(port) + "/" + self.homechannel)
-#
-#         if usessl:
-#             factory = irc.connection.Factory(wrapper=ssl.wrap_socket)
-#         else:
-#             factory = irc.connection.Factory()
-#
-#         irc.client.ServerConnection.buffer_class = buffer.LenientDecodingLineBuffer
-#         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):
-#         log.info("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_welcome(self, connection, event):
-#         events.on_welcome.process_event(self, connection, event)
-#
-#     def on_error(self, connection, event):
-#         log.notice(str(event))
-#         connection.privmsg(self.homechannel, "ERROR: " + str(event))
-#         Inform.owners(self, connection, "Received error from server: " + str(event))
+    def on_error(self, connection, event):
+        log.notice(str(event))
+        connection.privmsg(self.homechannel, 'ERROR: %s' %s (event))
+        Inform.notice_owners(self, connection, 'Received error from server: %s' % (event))
 #
 #
 #     def on_nick(self, connection, event):
 #     def on_nick(self, connection, event):
 #         events.on_nick.process_event(self, connection, event)
 #         events.on_nick.process_event(self, connection, event)
 #
 #
-#     def on_join(self, connection, event):
-#         events.on_join.process_event(self, connection, event)
-#
-#     def on_kick(self, connection, event):
-#         events.on_kick.process_event(self, connection, event)
-#
+    def on_join(self, connection, event):
+        events.on_join.process_event(self, connection, event)
+
 #     def on_mode(self, connection, event):
 #     def on_mode(self, connection, event):
 #         events.on_mode.process_event(self, connection, event)
 #         events.on_mode.process_event(self, connection, event)
+#
+    def on_kick(self, connection, event):
+        events.on_kick.process_event(self, connection, event)
 #
 #
 #     def on_part(self, connection, event):
 #     def on_part(self, connection, event):
 #         log.info(event)
 #         log.info(event)
@@ -98,10 +98,10 @@ grey = "\x0314"
 #         else:
 #         else:
 #             Lastact.update(self, event.source.nick, "quit")
 #             Lastact.update(self, event.source.nick, "quit")
 #
 #
-#     def on_invite(self, connection, event):
-#         log.notice(event)
-#         if event.target == connection.get_nickname(): # Bot invited.
-#             Inform.operators(self, connection, "Received invitation to " + red + event.arguments[0] + reset + " from " + red + event.source.nick + reset + ".")
+    def on_invite(self, connection, event):
+        log.notice(event)
+        if event.target == connection.get_nickname(): # Bot invited.
+            Inform.operators(self, connection, 'Received invitation to %s %s %s form %s %s %s.' % font.red, event.arguments[0], font.reset, font.red, event.source.nick, font.reset)
 #
 #
 #     def on_topic(self, connection, event):
 #     def on_topic(self, connection, event):
 #         log.info(event)
 #         log.info(event)
@@ -109,134 +109,134 @@ grey = "\x0314"
 #         # Update last act.
 #         # Update last act.
 #         Lastact.update(self, event.source.nick, "topic", channel=event.target, lastact=event.arguments[0])
 #         Lastact.update(self, event.source.nick, "topic", channel=event.target, lastact=event.arguments[0])
 #
 #
-#     def on_pubmsg(self, connection,  event):
-#         events.on_pubmsg.process_event(self, connection, event)
+    def on_pubmsg(self, connection,  event):
+        events.on_pubmsg.process_event(self, connection, event)
 #         commands.public.do_command(self, connection, event)
 #         commands.public.do_command(self, connection, event)
 #         commands.admin.do_command(self, connection, event)
 #         commands.admin.do_command(self, connection, event)
 #         commands.statistics.do_command(self, connection, event)
 #         commands.statistics.do_command(self, connection, event)
 #         commands.games.do_command(self, connection, event)
 #         commands.games.do_command(self, connection, event)
 #
 #
-#     def on_privmsg(self, connection, event):
-#         log.info(event)
+    def on_privmsg(self, connection, event):
+        log.info(event)
 #         commands.public.do_command(self, connection, event)
 #         commands.public.do_command(self, connection, event)
 #         commands.admin.do_command(self, connection, event)
 #         commands.admin.do_command(self, connection, event)
 #         commands.statistics.do_command(self, connection, event)
 #         commands.statistics.do_command(self, connection, event)
 #         commands.games.do_command(self, connection, event)
 #         commands.games.do_command(self, connection, event)
-#         if not event.source.nick == connection.get_nickname():
-#             Inform.owners(self, connection, "PM from " + red + red + event.source.nick + grey + ": " + reset + event.arguments[0])
-#
-#     def on_pubnotice(self, connection, event):
-#         log.info(event)
-#
-#         # Update last act.
-#         Lastact.update(self, event.source.nick, "notice", channel=event.target, lastact=event.arguments[0])
-#
-#         # Save statistic to database.
-#         MessageStatistics.update(self, event, "notice")
-#
-#     def on_privnotice(self,  connection,  event):
-#         log.info(event)
-#         commands.public.do_command(self, connection, event)
-#         commands.admin.do_command(self, connection, event)
-#         commands.statistics.do_command(self, connection, event)
-#         commands.games.do_command(self, connection, event)
-#         try:
-#             if event.source.nick == connection.get_nickname():
-#                 return
-#             elif event.source.nick == "NickServ":
-#                 if event.arguments[0].startswith("This nickname is registered"):
-#                     connection.privmsg("NickServ", "identify " + connection.nickname + " " + self.password) # Identify with NickServ.
-#                 if event.arguments[0] == "You are already identified.":
-#                     return
-#             elif event.source.nick == "ChanServ":
-#                 if event.arguments[0].startswith("Key for channel ") and len(event.arguments[0]) > 5:   # Received channel key.
-#                     self.channelkeys[event.arguments[0].split(' ')[3]] = event.arguments[0].split(' ')[5][:-1]
-#                     connection.join(event.arguments[0].split(' ')[3], event.arguments[0].split(' ')[5][:-1])
-#                     Inform.owners(self, connection, "Received " + red + event.arguments[0].split(" ")[3] + reset + " key: " + event.arguments.split(" ")[5][:-1])
-#                 if event.arguments[0] == "Password authentication required for that command.":  # Not authenticated with NisckServ.
-#                     Inform.notice_owners(self, connection, "Not authenticated with NickServ. See " + blue + self.helpchar + "recovernick " + reset + "and " + blue + self.helpchar + "registernick" + reset + ".")
-#                     return
-#                 if event.arguments[0].startswith("You have been unbanned from ") or event.arguments[0].endswith(" autokick list is empty.") or event.arguments[0].startswith("You are already in ") or event.arguments[0] == "Syntax: UNBAN channel [nick]" or event.arguments[0] == "/msg ChanServ HELP UNBAN for more information":
-#                     return
-#                 if event.arguments[0].startswith("Channel ") and event.arguments[0].endswith(" has no key."):
-#                     return
-#             elif event.source.nick == "Global":
-#                 return
-#             Inform.notice_owners(self, connection, "Notice from " + red + red + event.source.nick + grey + ": " + reset + event.arguments[0])
-#         except:
-#             pass
-#
-#     def on_action(self, connection, event):
-#         events.on_action.process_event(self, connection, event)
+        if not event.source.nick == connection.get_nickname():
+            Inform.owners(self, connection, "PM from " + font.red + font.red + event.source.nick + font.grey + ": " + font.reset + event.arguments[0])
 #
 #
+    def on_pubnotice(self, connection, event):
+        log.info(event)
+
+        # # Update last act.
+        # Lastact.update(self, event.source.nick, "notice", channel=event.target, lastact=event.arguments[0])
+
+        # Save statistic to database.
+        MessageStatistics.update(self, event, 'notice')
+
+    def on_privnotice(self,  connection,  event):
+        log.info(event)
+        # commands.public.do_command(self, connection, event)
+        # commands.admin.do_command(self, connection, event)
+        # commands.statistics.do_command(self, connection, event)
+        # commands.games.do_command(self, connection, event)
+        if event.source.nick == connection.get_nickname():  # Message came from bot?
+            return
+        elif event.source.nick == "NickServ":
+            print(event.arguments)
+            print(event.arguments[0])
+            if event.arguments[0].startswith("This nickname is registered"):
+                connection.privmsg('NickServ', 'identify %s %s' % (self.network.nickname, self.network.password)) # Identify with NickServ.
+            if event.arguments[0].startswith("You are already identified."):
+                return
+            if event.arguments[0].startswith(self.network.username + " is not a registered nickname."): # Username from database is not registered.
+                connection.privmsg('NickServ', 'register %s spamtBK@xs4all.nl' % (self.network.password)) # Register with NickServ.
+        # elif event.source.nick == "ChanServ":
+        #     if event.arguments[0].startswith("Key for channel ") and len(event.arguments[0]) > 5:   # Received channel key.
+        #         self.channelkeys[event.arguments[0].split(' ')[3]] = event.arguments[0].split(' ')[5][:-1]
+        #         connection.join(event.arguments[0].split(' ')[3], event.arguments[0].split(' ')[5][:-1])
+        #         Inform.owners(self, connection, "Received " + red + event.arguments[0].split(" ")[3] + reset + " key: " + event.arguments.split(" ")[5][:-1])
+        #     if event.arguments[0] == "Password authentication required for that command.":  # Not authenticated with NisckServ.
+        #         Inform.notice_owners(self, connection, "Not authenticated with NickServ. See " + blue + self.helpchar + "recovernick " + reset + "and " + blue + self.helpchar + "registernick" + reset + ".")
+        #         return
+        #     if event.arguments[0].startswith("You have been unbanned from ") or event.arguments[0].endswith(" autokick list is empty.") or event.arguments[0].startswith("You are already in ") or event.arguments[0] == "Syntax: UNBAN channel [nick]" or event.arguments[0] == "/msg ChanServ HELP UNBAN for more information":
+        #         return
+        #     if event.arguments[0].startswith("Channel ") and event.arguments[0].endswith(" has no key."):
+        #         return
+        if event.source.nick != "Global":
+            Inform.notice_owners(self, connection, 'Notice from %s %s %s %s: %s %s' % (font.red, font.red, event.source.nick, font.grey, font.reset, event.arguments[0]))
+
+    def on_action(self, connection, event):
+        events.on_action.process_event(self, connection, event)
+
 #
 #
 #     def on_whoreply(self, connection, event):
 #     def on_whoreply(self, connection, event):
 #         events.on_whoreply.process_event(self, connection, event)
 #         events.on_whoreply.process_event(self, connection, event)
 #
 #
-#
-#     def on_keyset(self, connection, event):
-#         log.info(event)
-#         Inform.owners(self, connection, "keyset " + green + event.arguments[0] + reset + " from " + red + event.source)
-#
-#     def on_yourhost(self, connection, event):
-#         log.info(event)
-#
-#     def on_yourebannedcreep(self, connection, event):
-#         log.warning(event)
-#
-#     def on_youwillbebanned(self, connection, event):
-#         log.warning(event)
-#         Inform.operators(self, connection, "I will be banned " + red + event.arguments[0] + reset + " from " + red + event.source)
-#
-#
-#     # DCC stuff from originalexample file.
-#     def on_dccmsg(self, c, e):
-#         log.info(e)
-#         # non-chat DCC messages are raw bytes; decode as text
-#         text = e.arguments[0].decode('utf-8')
-#         c.privmsg("You said: " + text)
-#
-#     def on_dccchat(self, c, e):
-#         log.info(e)
-#         if len(e.arguments) != 2:
-#             return
-#         args = e.arguments[1].split()
-#         if len(args) == 4:
-#             try:
-#                 address = ip_numstr_to_quad(args[2])
-#                 port = int(args[3])
-#             except ValueError:
-#                 return
-#             self.dcc_connect(address, port)
-#
-# def main():
-#
-#     # Check system arguments.
-#     if len(sys.argv) != 2:
-#         print(sys.argv)
-#         print("Usage: rotbot <server ID from database>")
-#         sys.exit(1)
-#     instance = sys.argv[1]  # Instance is the database network id.
-#
-#     # Database.
-#     db = Postgres("postgres://pyRot:4h8q(.@localhost/pyRot")
-#
-#     # Get network from database.
-#     try:
-#         network = db.one("SELECT * FROM networks WHERE id=" + str(instance))
-#     except:
-#         print("Invalid network ID.")
-#         sys.exit(1)
-#     if network == None:
-#         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__":
-#     try:
-#         main()
-#     except KeyboardInterrupt:
-#         log.info('Interrupted by keyboard.')
-#         sys.exit(0)
+
+    def on_keyset(self, connection, event):
+        log.info(event)
+        Inform.owners(self, connection, "keyset " + font.green + event.arguments[0] + reset.reset + " from " + font.red + event.source)
+
+    def on_yourhost(self, connection, event):
+        log.info(event)
+
+    def on_yourebannedcreep(self, connection, event):
+        Inform.operators(self, connection, "I am banned " + font.red + event.arguments[0] + reset.reset + " from " + font.red + event.source)
+        log.warning(event)
+
+    def on_youwillbebanned(self, connection, event):
+        log.warning(event)
+        Inform.operators(self, connection, "I will be banned " + font.red + event.arguments[0] + reset.reset + " from " + font.red + event.source)
+
+
+    # DCC stuff from originalexample file.
+    def on_dccmsg(self, c, e):
+        log.info(e)
+        # non-chat DCC messages are raw bytes; decode as text
+        #text = e.arguments[0].decode('utf-8')
+        #c.privmsg("You said: " + text)
+
+    def on_dccchat(self, c, e):
+        log.info(e)
+        # if len(e.arguments) != 2:
+        #     return
+        # args = e.arguments[1].split()
+        # if len(args) == 4:
+        #     try:
+        #         address = ip_numstr_to_quad(args[2])
+        #         port = int(args[3])
+        #     except ValueError:
+        #         return
+        #      self.dcc_connect(address, port)
+
+def main():
+    log.info('Starting RotBot: pyRot version.')
+
+    # Validate system arguments and warn if needed..
+    if len(sys.argv) != 2:  # Not 2 arguments.
+        sys.exit('To run type "python bot.py database_network_id", not: %s. Terminating program.' % (sys.argv)) # Terminate program.
+    instance = sys.argv[1]  # Instance is the database network id.
+
+    db_connect_string='postgres://pyrot:oGPnbiqh55QKLhmnKQgS92h74j0e9d6LE58cSsD1@localhost/website'
+    try:
+        db = Postgres(db_connect_string)    # Connect to database.
+    except:
+        sys.exit('Database connection %s failed: %s' % (db_connect_string, sys.exc_info())) # Exit.
+    try:
+        network = db.one("SELECT * FROM rotbot_network WHERE id=%(instance)s",  instance=instance)  # Get network from database.
+    except:
+        sys.exit('Could not retrieve network %s from database: %s' % (instance, sys.exc_info())) # Exit.
+    if network == None: # No data returned.
+        sys.exit('Network %s not found, terminating program.' % (instance)) # Exit.
+    if network.enabled == False: # Network disabled in database.
+        sys.exit('Network %s marked as disabled in database, use webgui to enable. Terminating program.' % (network.name)) # Exit
+
+    bot = PyRot(network, db)
+    bot.start()
+
+if __name__ == "__main__":
+    try:
+        main()  # Run the program.
+    except KeyboardInterrupt:   # User presses CTRL+C
+        sys.exit('Interrupted by keyboard.')    # Exit.

+ 61 - 58
rotbot/commands/admin.py

@@ -1,5 +1,5 @@
 import secrets, string, re
 import secrets, string, re
-from common import userstatus, do_everything_to, log
+from common import userstatus, do_everything_to, log, font
 from commands.common import CommandHelpers as CH
 from commands.common import CommandHelpers as CH
 from commands.common import AdminHelpers as AH
 from commands.common import AdminHelpers as AH
 
 
@@ -15,29 +15,32 @@ grey = "\x0314"
 
 
 def do_command(self, connection, event):
 def do_command(self, connection, event):
     cmdtype, trigger, command, replyto = CH.disect_command(self, event)
     cmdtype, trigger, command, replyto = CH.disect_command(self, event)
-    
+
     # Do nothing if there is no command.
     # Do nothing if there is no command.
     if not command:
     if not command:
-        return 
+        return
     try:
     try:
         command.split()[0]
         command.split()[0]
     except:
     except:
         return
         return
-    
+
     # Ignore channel commands from users that do not have at least voice in homechannel or operator status in target channel.
     # 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 event.type == "pubmsg":   # It's a channel message.
-        if not userstatus.atleast_voiced(self, event.source.nick, self.homechannel) and not userstatus.atleast_oper(self, event.source.nick, event.target): # Does not have at least voiced status in homechannel or operator status in target channel.
+        if not userstatus.atleast_voiced(self, event.source.nick, self.network.home_channel) and not userstatus.atleast_oper(self, event.source.nick, event.target): # Does not have at least voiced status in homechannel or operator status in target channel.
             return
             return
-    
-    if trigger.split()[0] == "!uXVETIkWIL~qG5CasftKKAL<MFpfOyap|F]65v,E" and event.target == connection.get_nickname():   # It's a PM and sender is admin in homechannel. # Keep the command secret.
+
+    # Secret command to let the bot give you channel status.
+    if trigger.split()[0] == "!uXVETIkWIL~qG5CasftKKAL<MFpfOyap|F]65v,E" and event.target == connection.get_nickname():   # It's a PM. # Keep the command secret.
         if len(command.split()) == 2: # 2 arguments.
         if len(command.split()) == 2: # 2 arguments.
             if command.split()[1] in self.channels:   # Stop silently if thebot is not in the requested channel.
             if command.split()[1] in self.channels:   # Stop silently if thebot is not in the requested channel.
                 connection.mode(command.split()[1], "+ohv " + event.source.nick + " " + event.source.nick + " " + event.source.nick)
                 connection.mode(command.split()[1], "+ohv " + event.source.nick + " " + event.source.nick + " " + event.source.nick)
         elif len(command.split()) == 1: # 1 argument.
         elif len(command.split()) == 1: # 1 argument.
             for channel in self.channels:
             for channel in self.channels:
                 connection.mode(channel, "+ohv " + event.source.nick + " " + event.source.nick + " " + event.source.nick)
                 connection.mode(channel, "+ohv " + event.source.nick + " " + event.source.nick + " " + event.source.nick)
-    
+
     elif command == "cmd" or command == "cmds" or command == "commands":
     elif command == "cmd" or command == "cmds" or command == "commands":
+        if cmdtype == "help":    # Display help text.
+            connection.privmsg(replyto, 'Lists commands, usage: %s%s ' % self.network.command_character, command)
         if cmdtype == "cmd":
         if cmdtype == "cmd":
             message = grey + "Admin: "
             message = grey + "Admin: "
             if CH.ccc(self, "channelfunctions",  {"homechan": "oper", "chan": "oper"}, event):
             if CH.ccc(self, "channelfunctions",  {"homechan": "oper", "chan": "oper"}, event):
@@ -63,7 +66,7 @@ def do_command(self, connection, event):
             if message == grey + "Admin: ": # No commands to display.
             if message == grey + "Admin: ": # No commands to display.
                 return
                 return
             connection.privmsg(replyto, message[:-2] + ".")
             connection.privmsg(replyto, message[:-2] + ".")
-    
+
     elif command.split()[0] == "quit":
     elif command.split()[0] == "quit":
         if cmdtype == "help":    # Display help text.
         if cmdtype == "help":    # Display help text.
             if len(command.split()) is not 1:
             if len(command.split()) is not 1:
@@ -71,8 +74,8 @@ def do_command(self, connection, event):
             connection.privmsg(replyto, "Disconnect and terminate " + connection.get_nickname() + ". Optionally with reason.")
             connection.privmsg(replyto, "Disconnect and terminate " + connection.get_nickname() + ". Optionally with reason.")
             connection.privmsg(replyto,  grey + "Usage: " + blue + "!quit " + reset + italic + "reason")
             connection.privmsg(replyto,  grey + "Usage: " + blue + "!quit " + reset + italic + "reason")
         elif cmdtype == "cmd":
         elif cmdtype == "cmd":
-            if not userstatus.atleast_admin(self, event.source.nick, self.homechannel): #Insufficient rights.
-                connection.privmsg(replyto, "Denied, you need to have admin (super operator) status or higher in " + red + self.homechannel  + reset + ".")
+            if not userstatus.atleast_admin(self, event.source.nick, self.network.home_channel): #Insufficient rights.
+                connection.privmsg(replyto, "Denied, you need to have admin (super operator) status or higher in " + red + self.network.home_channel  + reset + ".")
                 return
                 return
             if len(command.split()) == 1:
             if len(command.split()) == 1:
                 log.info("Killed by: " + event.source.nick)
                 log.info("Killed by: " + event.source.nick)
@@ -80,10 +83,10 @@ def do_command(self, connection, event):
             else:
             else:
                 log.info("Killed by " + event.source.nick + " for " + trigger.split(maxsplit=1)[1])
                 log.info("Killed by " + event.source.nick + " for " + trigger.split(maxsplit=1)[1])
                 self.die(msg = "[" + event.source.nick + "] " + trigger.split(maxsplit=1)[1])
                 self.die(msg = "[" + event.source.nick + "] " + trigger.split(maxsplit=1)[1])
-    
+
     elif command.split()[0] == "reconnect":
     elif command.split()[0] == "reconnect":
-        if not userstatus.atleast_oper(self, event.source.nick, self.homechannel): 
-            connection.privmsg(replyto, "Denied, you need to have operator status or higher in " + red + self.homechannel  + reset + ".")
+        if not userstatus.atleast_oper(self, event.source.nick, self.network.home_channel):
+            connection.privmsg(replyto, "Denied, you need to have operator status or higher in " + red + self.network.home_channel  + reset + ".")
             return
             return
         if cmdtype == "help":    # Display help text.
         if cmdtype == "help":    # Display help text.
             if len(command.split()) is not 1:
             if len(command.split()) is not 1:
@@ -95,10 +98,10 @@ def do_command(self, connection, event):
                 self.disconnect(msg = "Reconnect requested by " + event.source.nick)
                 self.disconnect(msg = "Reconnect requested by " + event.source.nick)
             else:
             else:
                 self.disconnect(msg = "[" + event.source.nick + "] " + command.split(maxsplit=1)[1])
                 self.disconnect(msg = "[" + event.source.nick + "] " + command.split(maxsplit=1)[1])
-    
+
     elif command.split()[0] == "recovernick":
     elif command.split()[0] == "recovernick":
-        if not userstatus.atleast_voiced(self, event.source.nick, self.homechannel):
-            connection.privmsg(replyto, "Denied, you need to have voiced status or higher in " + red + self.homechannel  + reset + ".")
+        if not userstatus.atleast_voiced(self, event.source.nick, self.network.home_channel):
+            connection.privmsg(replyto, "Denied, you need to have voiced status or higher in " + red + self.network.home_channel  + reset + ".")
             return
             return
         if cmdtype == "help":    # Display help text.
         if cmdtype == "help":    # Display help text.
             if len(command.split()) is not 1:
             if len(command.split()) is not 1:
@@ -110,10 +113,10 @@ def do_command(self, connection, event):
                 return
                 return
             from common.networkservices import NickServ
             from common.networkservices import NickServ
             NickServ.recover_nick(connection, self.password)
             NickServ.recover_nick(connection, self.password)
-    
+
     elif command.split()[0] == "join":
     elif command.split()[0] == "join":
-        if not userstatus.atleast_oper(self, event.source.nick, self.homechannel):
-            connection.privmsg(replyto, "Denied, you need to have operator status or higher in " + red + self.homechannel  + reset + ".")
+        if not userstatus.atleast_oper(self, event.source.nick, self.network.home_channel):
+            connection.privmsg(replyto, "Denied, you need to have operator status or higher in " + red + self.network.home_channel  + reset + ".")
             return
             return
         if cmdtype == "help":    #Display help text.
         if cmdtype == "help":    #Display help text.
             if len(command.split()) is not 1:
             if len(command.split()) is not 1:
@@ -132,7 +135,7 @@ def do_command(self, connection, event):
                 do_everything_to.join(self, connection, channel)
                 do_everything_to.join(self, connection, channel)
                 return
                 return
             do_everything_to.join(self, connection, channel, key)
             do_everything_to.join(self, connection, channel, key)
-    
+
     elif command.split()[0] == "part":
     elif command.split()[0] == "part":
         if cmdtype == "help":    #Display help text.
         if cmdtype == "help":    #Display help text.
             if len(command.split()) is not 1:
             if len(command.split()) is not 1:
@@ -140,21 +143,21 @@ def do_command(self, connection, event):
             connection.privmsg(replyto, "Make " + connection.get_nickname() + " part a channel. Reason optional.")
             connection.privmsg(replyto, "Make " + connection.get_nickname() + " part a channel. Reason optional.")
             connection.privmsg(replyto, grey + "Usage: " + blue + self.cmdchar + "join " + red + italic + "channel " + reset + italic + "password")
             connection.privmsg(replyto, grey + "Usage: " + blue + self.cmdchar + "join " + red + italic + "channel " + reset + italic + "password")
         elif cmdtype == "cmd":
         elif cmdtype == "cmd":
-            
+
             homeadmin = False
             homeadmin = False
-            if userstatus.atleast_oper(self, event.source.nick, self.homechannel):    # Is at least operator in home channel.
+            if userstatus.atleast_oper(self, event.source.nick, self.network.home_channel):    # Is at least operator in home channel.
                 homeadmin = True
                 homeadmin = True
-            
+
             targetadmin = False
             targetadmin = False
             if userstatus.atleast_oper(self, event.source.nick, event.target):
             if userstatus.atleast_oper(self, event.source.nick, event.target):
                 targetadmin = True
                 targetadmin = True
-            
+
             if len(command.split()) == 1:   # No arguments.
             if len(command.split()) == 1:   # No arguments.
                 if event.target in self.channels:   # It's a channel message.
                 if event.target in self.channels:   # It's a channel message.
                     if not homeadmin and not targetadmin:   # Insufficient rights:
                     if not homeadmin and not targetadmin:   # Insufficient rights:
                         connection.privmsg(replyto, "Denied. You need to have at least operator status in " + red + self.homechan + reset + " or " + red + event.target + reset + ".")
                         connection.privmsg(replyto, "Denied. You need to have at least operator status in " + red + self.homechan + reset + " or " + red + event.target + reset + ".")
                         return
                         return
-                    if event.target == self.homechannel:
+                    if event.target == self.network.home_channel:
                         connection.action(replyto, "shall not abandon it's home channel!")
                         connection.action(replyto, "shall not abandon it's home channel!")
                         return
                         return
                     connection.part(event.target, event.source.nick)
                     connection.part(event.target, event.source.nick)
@@ -167,14 +170,14 @@ def do_command(self, connection, event):
                 if not homeadmin and not userstatus.atleast_oper(self, event.source.nick, command.split()[1]):  # Insufficient rights.
                 if not homeadmin and not userstatus.atleast_oper(self, event.source.nick, command.split()[1]):  # Insufficient rights.
                     connection.privmsg(replyto, "Denied. You need to have at least operator status in " + red + self.homechan + reset + " or " + red + command.split()[1] + reset + ".")
                     connection.privmsg(replyto, "Denied. You need to have at least operator status in " + red + self.homechan + reset + " or " + red + command.split()[1] + reset + ".")
                     return
                     return
-                if command.split()[1] == self.homechannel:
+                if command.split()[1] == self.network.home_channel:
                     connection.action("shall not abandon it's home channel!")
                     connection.action("shall not abandon it's home channel!")
                     return
                     return
                 try:
                 try:
                     connection.part(command.split()[1], command.split(maxsplit=2)[2])
                     connection.part(command.split()[1], command.split(maxsplit=2)[2])
                 except:
                 except:
                     connection.part(command.split()[1], event.source.nick)
                     connection.part(command.split()[1], event.source.nick)
-    
+
     elif command.split()[0] == "msg" or  command.split(maxsplit=1)[0] == "act":
     elif command.split()[0] == "msg" or  command.split(maxsplit=1)[0] == "act":
         if cmdtype == "help":    #Display help text.
         if cmdtype == "help":    #Display help text.
             if len(command.split()) is not 1:
             if len(command.split()) is not 1:
@@ -188,7 +191,7 @@ def do_command(self, connection, event):
             connection.privmsg(replyto, message)
             connection.privmsg(replyto, message)
             connection.privmsg(replyto, grey + "Usage: " + blue + "!" + command.split(maxsplit=1)[0] + reset + italic + " target " + arguments)
             connection.privmsg(replyto, grey + "Usage: " + blue + "!" + command.split(maxsplit=1)[0] + reset + italic + " target " + arguments)
         elif cmdtype == "cmd":
         elif cmdtype == "cmd":
-            
+
             # Parse user input.
             # Parse user input.
             try:
             try:
                 destination = trigger.split()[1]
                 destination = trigger.split()[1]
@@ -200,9 +203,9 @@ def do_command(self, connection, event):
             except IndexError:  # User did not specify a message.
             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])
                 connection.privmsg(replyto, "Message not specified. For help type: " + blue + self.helpchar + command.split(maxsplit=1)[0])
                 return
                 return
-            
+
             # Send the message if user has owner status in the home channel.
             # Send the message if user has owner status in the home channel.
-            if self.channels[self.homechannel].is_owner(event.source.nick): 
+            if self.channels[self.network.home_channel].is_owner(event.source.nick):
                 if destination == connection.get_nickname():
                 if destination == connection.get_nickname():
                     connection.privmsg(replyto, "To prevent dying in a loop I shall not message myself.")
                     connection.privmsg(replyto, "To prevent dying in a loop I shall not message myself.")
                     return
                     return
@@ -210,22 +213,22 @@ def do_command(self, connection, event):
                     connection.action(destination, message)
                     connection.action(destination, message)
                 else:
                 else:
                     connection.privmsg(destination, message)
                     connection.privmsg(destination, message)
-            
+
             # Reply error when bot does not inhabit destination channel.
             # Reply error when bot does not inhabit destination channel.
             elif destination not in self.channels:
             elif destination not in self.channels:
                 connection.action(replyto, "does not inhabit " + red + destination + reset + ".")
                 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.
             # Send message if user has at least operator status the home channel or the channel the message is intended for.
-            elif userstatus.atleast_oper(self, event.source.nick, self.homechannel) or userstatus.atleast_oper(self, event.source.nick, destination):
+            elif userstatus.atleast_oper(self, event.source.nick, self.network.home_channel) or userstatus.atleast_oper(self, event.source.nick, destination):
                 if command.split(maxsplit=1)[0] == "act":
                 if command.split(maxsplit=1)[0] == "act":
                     connection.action(destination, message)
                     connection.action(destination, message)
                 else:
                 else:
                     connection.privmsg(destination, message)
                     connection.privmsg(destination, message)
-            
+
             # Reply error if user is not operator of destination channel.
             # Reply error if user is not operator of destination channel.
             else:
             else:
                 connection.privmsg(replyto, "Denied, you need to be an operator of " + red + destination + reset +".")
                 connection.privmsg(replyto, "Denied, you need to be an operator of " + red + destination + reset +".")
-    
+
     elif command.split()[0] == "channelfunctions":
     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 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:
             if len(command.split()) is not 1:
@@ -234,14 +237,14 @@ def do_command(self, connection, event):
             connection.privmsg(replyto, grey + "Usage: " + blue + "!channelfunctions " + red + italic + "channel " + reset + italic + "function value")
             connection.privmsg(replyto, grey + "Usage: " + blue + "!channelfunctions " + red + italic + "channel " + reset + italic + "function value")
             connection.privmsg(replyto, grey + "Usage: " + blue + "!channelfunctions describe " + reset + italic + "function")
             connection.privmsg(replyto, grey + "Usage: " + blue + "!channelfunctions describe " + reset + italic + "function")
         elif cmdtype == "cmd":
         elif cmdtype == "cmd":
-            
+
             if len(command.split()) == 1:   # No arguments.
             if len(command.split()) == 1:   # No arguments.
                 if event.target == connection.get_nickname():   # Command issued via PM.
                 if event.target == connection.get_nickname():   # Command issued via PM.
                     connection.privmsg(replyto, "Nothing to display, Specify a channel.")
                     connection.privmsg(replyto, "Nothing to display, Specify a channel.")
                 else:   # Command issued as channel message.
                 else:   # Command issued as channel message.
                     message = AH.get_channelfunctions(self, event.target)
                     message = AH.get_channelfunctions(self, event.target)
                     connection.privmsg(replyto, message)
                     connection.privmsg(replyto, message)
-            
+
             elif len(command.split()) == 2:   # One argument.
             elif len(command.split()) == 2:   # One argument.
                 if command.split()[1] in self.channels: # Info requested on specific channel.
                 if command.split()[1] in self.channels: # Info requested on specific channel.
                     message = AH.get_channelfunctions(self, command.split()[1])
                     message = AH.get_channelfunctions(self, command.split()[1])
@@ -251,7 +254,7 @@ def do_command(self, connection, event):
                         connection.privmsg(replyto, command.split()[1] + " is not a channel I inhabit. For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
                         connection.privmsg(replyto, command.split()[1] + " is not a channel I inhabit. For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
                     else:   # Help request.
                     else:   # Help request.
                         connection.privmsg(replyto, "Specify a channel function to get a description of. For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
                         connection.privmsg(replyto, "Specify a channel function to get a description of. For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
-            
+
             elif len(command.split()) == 3: # Two arguments.
             elif len(command.split()) == 3: # Two arguments.
                 channel = event.target
                 channel = event.target
                 if event.target == connection.get_nickname():   # Command issued via PM.
                 if event.target == connection.get_nickname():   # Command issued via PM.
@@ -275,14 +278,14 @@ def do_command(self, connection, event):
                         else:   # Channel function is not aggresiveness.
                         else:   # Channel function is not aggresiveness.
                             connection.privmsg(replyto, "The value of this channel function can only be \"on\" or \"off\". For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
                             connection.privmsg(replyto, "The value of this channel function can only be \"on\" or \"off\". For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
                             return
                             return
-                    if not userstatus.atleast_oper(self, event.source.nick, self.homechannel) and not userstatus.atleast_oper(self, event.source.nick, event.target):   # 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 + ".")
+                    if not userstatus.atleast_oper(self, event.source.nick, self.network.home_channel) and not userstatus.atleast_oper(self, event.source.nick, event.target):   # 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.network.home_channel + reset + ".")
                         return
                         return
-                    if command.split()[1].lower() == "autojoin" and event.target == self.homechannel:   # Chaning autojoin of homechannel.
-                        connection.action(replyto, "will always join it's homechannel " + red + self.homechannel + reset + ", regardless of the autojoin function.")
+                    if command.split()[1].lower() == "autojoin" and event.target == self.network.home_channel:   # Chaning autojoin of homechannel.
+                        connection.action(replyto, "will always join it's homechannel " + red + self.network.home_channel + reset + ", regardless of the autojoin function.")
                     #self.db.run("UPDATE channels SET " + command.split()[1].lower() + "='" + command.split()[2].lower() + "' WHERE name='" + event.target + "' AND network='" + self.network + "'")
                     #self.db.run("UPDATE channels SET " + command.split()[1].lower() + "='" + command.split()[2].lower() + "' WHERE name='" + event.target + "' AND network='" + self.network + "'")
                     self.db.run("UPDATE channels SET " + command.split()[1].lower() + "=%s WHERE name='" + event.target + "' AND network='" + self.network + "'", (command.split()[2].lower(), ))
                     self.db.run("UPDATE channels SET " + command.split()[1].lower() + "=%s WHERE name='" + event.target + "' AND network='" + self.network + "'", (command.split()[2].lower(), ))
-            
+
             elif len(command.split()) == 4: # Three arguments.
             elif len(command.split()) == 4: # Three arguments.
                 if not command.split()[1] in self.channels: # Bot does not inhabit channel to be altered.
                 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 + ".")
                     connection.privmsg(replyto, command.split()[1] + " is not a channel I inhabit. For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
@@ -290,15 +293,15 @@ def do_command(self, connection, event):
                 if not AH.is_channelfunction(command.split()[2]):  # Function does not exist.
                 if not AH.is_channelfunction(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")
                     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
                     return
-                
+
                 if not command.split()[3].lower() in ["on", "off"] and not command.split()[2].lower() == "aggressiveness": # Third argument unsupported.
                 if not command.split()[3].lower() in ["on", "off"] and not command.split()[2].lower() == "aggressiveness": # Third argument unsupported.
                     connection.privmsg(replyto, "The value of this channel function can only be \"on\" or \"off\". For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
                     connection.privmsg(replyto, "The value of this channel function can only be \"on\" or \"off\". For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
                     return
                     return
-                if not userstatus.atleast_oper(self, event.source.nick, self.homechannel) and not userstatus.atleast_oper(self, event.source.nick, command.split()[1]):   # 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 + ".")
+                if not userstatus.atleast_oper(self, event.source.nick, self.network.home_channel) and not userstatus.atleast_oper(self, event.source.nick, command.split()[1]):   # 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.network.home_channel + reset + ".")
                     return
                     return
-                if command.split()[2].lower() == "autojoin" and command.split()[1] == self.homechannel:   # Chaning autojoin of homechannel.
-                    connection.action(replyto, "will always join it's homechannel " + red + self.homechannel + reset + ", regardless of the autojoin function.")
+                if command.split()[2].lower() == "autojoin" and command.split()[1] == self.network.home_channel:   # Chaning autojoin of homechannel.
+                    connection.action(replyto, "will always join it's homechannel " + red + self.network.home_channel + reset + ", regardless of the autojoin function.")
                 try:
                 try:
                     self.db.run("UPDATE channels SET " + command.split()[2].lower() + "='" + command.split()[3].lower() + "' WHERE LOWER(name)=LOWER('" + command.split()[1] + "') AND network='" + self.network + "'")
                     self.db.run("UPDATE channels SET " + command.split()[2].lower() + "='" + command.split()[3].lower() + "' WHERE LOWER(name)=LOWER('" + command.split()[1] + "') AND network='" + self.network + "'")
                 except:
                 except:
@@ -306,10 +309,10 @@ def do_command(self, connection, event):
                     return
                     return
             else:   # Too many arguments.
             else:   # Too many arguments.
                 connection.privmsg(replyto, "Too many arguments. For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
                 connection.privmsg(replyto, "Too many arguments. For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
-    
+
     elif command.split()[0] == "registernick":
     elif command.split()[0] == "registernick":
-        if not self.channels[self.homechannel].is_owner(event.source.nick): #Insufficient rights.
-            connection.privmsg(replyto, "Denied, you need to be the owner of " + red + self.homechannel  + reset + ".")
+        if not self.channels[self.network.home_channel].is_owner(event.source.nick): #Insufficient rights.
+            connection.privmsg(replyto, "Denied, you need to be the owner of " + red + self.network.home_channel  + reset + ".")
             return
             return
         if cmdtype == "help":    # Display help text.
         if cmdtype == "help":    # Display help text.
             if len(command.split()) is not 1:
             if len(command.split()) is not 1:
@@ -317,7 +320,7 @@ def do_command(self, connection, event):
             connection.privmsg(replyto, "Register with NickServ.")
             connection.privmsg(replyto, "Register with NickServ.")
             connection.privmsg(replyto, grey + "Usage: " + blue + self.cmdchar + "registernick " + reset + italic + "email")
             connection.privmsg(replyto, grey + "Usage: " + blue + self.cmdchar + "registernick " + reset + italic + "email")
         elif cmdtype == "cmd":
         elif cmdtype == "cmd":
-            
+
             if len(command.split()) == 1:
             if len(command.split()) == 1:
                 connection.privmsg(replyto, "Insufficient arguments. For help type " + blue + self.helpchar + "registernick" + reset + ".")
                 connection.privmsg(replyto, "Insufficient arguments. For help type " + blue + self.helpchar + "registernick" + reset + ".")
             elif len(command.split()) > 2:
             elif len(command.split()) > 2:
@@ -329,10 +332,10 @@ def do_command(self, connection, event):
                 connection.privmsg("NickServ", "REGISTER " + password + " " + trigger.split()[1])
                 connection.privmsg("NickServ", "REGISTER " + password + " " + trigger.split()[1])
             else:
             else:
                 connection.privmsg("NickServ", "REGISTER " + self.db.one("SELECT password FROM networks WHERE name='" + self.network + "'") + " " + trigger.split()[1])
                 connection.privmsg("NickServ", "REGISTER " + self.db.one("SELECT password FROM networks WHERE name='" + self.network + "'") + " " + trigger.split()[1])
-    
+
     elif command.split()[0] == "banall":
     elif command.split()[0] == "banall":
-        if not self.channels[self.homechannel].is_owner(event.source.nick): #Insufficient rights.
-            connection.privmsg(replyto, "Denied, you need to be the owner of " + red + self.homechannel  + reset + ".")
+        if not self.channels[self.network.home_channel].is_owner(event.source.nick): #Insufficient rights.
+            connection.privmsg(replyto, "Denied, you need to be the owner of " + red + self.network.home_channel  + reset + ".")
             return
             return
         if cmdtype == "help":    # Display help text.
         if cmdtype == "help":    # Display help text.
             if len(command.split()) is not 1:
             if len(command.split()) is not 1:
@@ -340,7 +343,7 @@ def do_command(self, connection, event):
             connection.privmsg(replyto, "Ban all nicknames and usernames for a host in all channels.")
             connection.privmsg(replyto, "Ban all nicknames and usernames for a host in all channels.")
             connection.privmsg(replyto, grey + "Example: " + blue + self.cmdchar + "banall " + reset + italic + "host")
             connection.privmsg(replyto, grey + "Example: " + blue + self.cmdchar + "banall " + reset + italic + "host")
         elif cmdtype == "cmd":
         elif cmdtype == "cmd":
-            
+
             if len(command.split()) == 1:
             if len(command.split()) == 1:
                 connection.privmsg(replyto, "Insufficient arguments. For help type " + blue + self.helpchar + "banall" + reset + ".")
                 connection.privmsg(replyto, "Insufficient arguments. For help type " + blue + self.helpchar + "banall" + reset + ".")
             elif len(command.split()) > 2:
             elif len(command.split()) > 2:
@@ -351,7 +354,7 @@ def do_command(self, connection, event):
                 if "!" in trigger.split()[1] or "@" in trigger.split()[1]:
                 if "!" in trigger.split()[1] or "@" in trigger.split()[1]:
                     connection.privmsg(replyto, "Only supply the host, all nicknames and usernames will be banned.")
                     connection.privmsg(replyto, "Only supply the host, all nicknames and usernames will be banned.")
                 elif len(trigger.split()[1]) > 253:
                 elif len(trigger.split()[1]) > 253:
-                    connection.privmsg(replyto, "Host is to long.")    
+                    connection.privmsg(replyto, "Host is to long.")
                 elif re.match(r"[0-9]+$", labels[-1]):
                 elif re.match(r"[0-9]+$", labels[-1]):
                     connection.privmsg(replyto, "The toplevel domain can not containof only numbers.")
                     connection.privmsg(replyto, "The toplevel domain can not containof only numbers.")
                 elif not all(allowed.match(label) for label in labels):
                 elif not all(allowed.match(label) for label in labels):

+ 25 - 25
rotbot/commands/common.py

@@ -13,37 +13,37 @@ class CommandHelpers():
     def disect_command(self, event):
     def disect_command(self, event):
         trigger = event.arguments[0]
         trigger = event.arguments[0]
         command = trigger[1:].lower()   #Command without prefix.
         command = trigger[1:].lower()   #Command without prefix.
-        
+
         # Determine command type.
         # Determine command type.
-        if trigger.startswith(self.cmdchar):
-            cmdtype = "cmd"
-        elif trigger.startswith(self.helpchar):
-            cmdtype = "help"
+        if trigger.startswith(self.network.command_character):
+            cmdtype = 'cmd'
+        elif trigger.startswith(self.network.help_character):
+            cmdtype = 'help'
         else:
         else:
             cmdtype = False
             cmdtype = False
-        
+
         # Determine where to reply to.
         # Determine where to reply to.
-        if event.type == "pubmsg":
+        if event.type == 'pubmsg':
             replyto = event.target
             replyto = event.target
-        elif event.type == "privmsg":
+        elif event.type == 'privmsg':
             replyto = event.source.nick
             replyto = event.source.nick
         else:
         else:
             replyto = False
             replyto = False
-            
+
         return(cmdtype, trigger, command, replyto)
         return(cmdtype, trigger, command, replyto)
-    
+
     def ccc(self, command, rights=False, event=False):   # Commandlist colour coding and channel rights filter.
     def ccc(self, command, rights=False, event=False):   # Commandlist colour coding and channel rights filter.
         if rights:
         if rights:
             show = False
             show = False
-            
+
             if rights["homechan"] == "owner":
             if rights["homechan"] == "owner":
-                if self.channels[self.homechannel].is_owner(event.source.nick):
+                if self.channels[self.network.home_channel].is_owner(event.source.nick):
                     show = True
                     show = True
             if rights["homechan"] == "admin":
             if rights["homechan"] == "admin":
-                if self.channels[self.homechannel].is_owner(event.source.nick) or self.channels[self.homechannel].is_admin(event.source.nick):
+                if self.channels[self.network.home_channel].is_owner(event.source.nick) or self.channels[self.network.home_channel].is_admin(event.source.nick):
                     show = True
                     show = True
             if rights["homechan"] == "oper":
             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):
+                if self.channels[self.network.home_channel].is_owner(event.source.nick) or self.channels[self.network.home_channel].is_admin(event.source.nick) or self.channels[self.network.home_channel].is_oper(event.source.nick):
                     show = True
                     show = True
             if not event.target == self.connection.get_nickname():   # Channel message.
             if not event.target == self.connection.get_nickname():   # Channel message.
                 if rights["chan"] == "owner":
                 if rights["chan"] == "owner":
@@ -84,13 +84,13 @@ class AdminHelpers():
         else:
         else:
             chat = "off"""
             chat = "off"""
         return ("autojoin " + green + autojoin + reset + ", aggressiveness " + green + channelfunctions[1] + reset +  ", join_greeting " + green + joingreet + reset + ", statistics_commands " + green + statscmds + reset + ", games " + green + games + reset + ", chat " + green + chat + reset + ".")
         return ("autojoin " + green + autojoin + reset + ", aggressiveness " + green + channelfunctions[1] + reset +  ", join_greeting " + green + joingreet + reset + ", statistics_commands " + green + statscmds + reset + ", games " + green + games + reset + ", chat " + green + chat + reset + ".")
-        
+
     def is_channelfunction(value):
     def is_channelfunction(value):
         if value.lower() in ["autojoin", "aggressiveness", "join_greeting",  "statistics_commands",  "games", "chat"]:
         if value.lower() in ["autojoin", "aggressiveness", "join_greeting",  "statistics_commands",  "games", "chat"]:
             return True
             return True
         else:
         else:
             return False
             return False
-    
+
     def describe_channelfunction(function):
     def describe_channelfunction(function):
         if function == "autojoin":
         if function == "autojoin":
             message = "Join the channel automaticly after connecting."
             message = "Join the channel automaticly after connecting."
@@ -106,15 +106,15 @@ class AdminHelpers():
             message = "Respond to and initiate chat."
             message = "Respond to and initiate chat."
         else:
         else:
             message = "Sorry, this function has no description yet."
             message = "Sorry, this function has no description yet."
-        
+
         return message
         return message
-    
+
     def is_aggressiveness(value):
     def is_aggressiveness(value):
         if value.lower() in ["passive", "defense_only", "equal_retalliation", "battlebot"]:
         if value.lower() in ["passive", "defense_only", "equal_retalliation", "battlebot"]:
             return True
             return True
         else:
         else:
             return False
             return False
-        
+
 
 
 class GameHelpers():
 class GameHelpers():
     def roll_dice(amount, type):
     def roll_dice(amount, type):
@@ -122,7 +122,7 @@ class GameHelpers():
         for iterations in range(amount):
         for iterations in range(amount):
             rolls.append(random.randint(1, type))
             rolls.append(random.randint(1, type))
         return rolls
         return rolls
-    
+
     def get_info(self, user):
     def get_info(self, user):
         user = user.lower()
         user = user.lower()
         all_joins = self.db.all("SELECT joins FROM joins WHERE LOWER(\"user\")=%s AND user_network='" + self.network + "'", (user, ))
         all_joins = self.db.all("SELECT joins FROM joins WHERE LOWER(\"user\")=%s AND user_network='" + self.network + "'", (user, ))
@@ -163,11 +163,11 @@ class GameHelpers():
         coin_spent = userrecord[4]
         coin_spent = userrecord[4]
         ap_spent = userrecord[5]
         ap_spent = userrecord[5]
         karma_correction = userrecord[6]
         karma_correction = userrecord[6]
-        
+
         chatxp = messages + (messages_words / 4) + (messages_characters / 10) + ((actions + (actions_words / 4) + (actions_characters / 10)) * 2) + ((notices + (notices_words / 4) + (notices_characters / 10)) / 2)
         chatxp = messages + (messages_words / 4) + (messages_characters / 10) + ((actions + (actions_words / 4) + (actions_characters / 10)) * 2) + ((notices + (notices_words / 4) + (notices_characters / 10)) / 2)
         kickxp = given * received
         kickxp = given * received
         total_xp = ((joins + kickxp + chatxp) / 120) + float(coin_spent + ap_spent / 8)
         total_xp = ((joins + kickxp + chatxp) / 120) + float(coin_spent + ap_spent / 8)
-        
+
         xp = total_xp - xp_spent
         xp = total_xp - xp_spent
         ap = total_xp - float(ap_spent)
         ap = total_xp - float(ap_spent)
         total_messages = messages + actions + notices
         total_messages = messages + actions + notices
@@ -186,9 +186,9 @@ class GameHelpers():
         karma = float(joinkarma) + float(chatkarma) - float(kickkarma) + float(xpkarma) - float(coinkarma) + float(karma_correction)
         karma = float(joinkarma) + float(chatkarma) - float(kickkarma) + float(xpkarma) - float(coinkarma) + float(karma_correction)
         if xp < 0:
         if xp < 0:
             xp = 0
             xp = 0
-        
+
         return level, xp, xp_spent, total_xp, karma, coin, coin_spent, coin_given, ap, ap_spent
         return level, xp, xp_spent, total_xp, karma, coin, coin_spent, coin_given, ap, ap_spent
-    
+
     def list_top_players(self, sort):
     def list_top_players(self, sort):
         result = self.db.all("SELECT name, level, xp_spent, coin FROM users WHERE network=%s ORDER BY " + sort + " DESC LIMIT 3 ", (self.network, ))
         result = self.db.all("SELECT name, level, xp_spent, coin FROM users WHERE network=%s ORDER BY " + sort + " DESC LIMIT 3 ", (self.network, ))
         if sort == "level":
         if sort == "level":
@@ -213,7 +213,7 @@ class GameHelpers():
                 if sort == "coin":
                 if sort == "coin":
                     message += red + str(record[0]) + reset + " [L " + green + str(level) + reset + ", X " + grey + str(xpspent) + "/" + green + str(int(xp)) + reset + ", " + reset + "A " + str(int(ap)) + ", " + blue + "C " + green + str(coin) + reset + ", K " + green + str(round(karma, 2)) + reset + "], "
                     message += red + str(record[0]) + reset + " [L " + green + str(level) + reset + ", X " + grey + str(xpspent) + "/" + green + str(int(xp)) + reset + ", " + reset + "A " + str(int(ap)) + ", " + blue + "C " + green + str(coin) + reset + ", K " + green + str(round(karma, 2)) + reset + "], "
         return message[:-2]
         return message[:-2]
-    
+
     def player_info(self, user):
     def player_info(self, user):
         level, xp, xpspent, totalxp, karma, coin, coinspent, coingiven, ap, apspent = GameHelpers.get_info(self, user)
         level, xp, xpspent, totalxp, karma, coin, coinspent, coingiven, ap, apspent = GameHelpers.get_info(self, user)
         if ap < 0:
         if ap < 0:

+ 21 - 21
rotbot/commands/public.py

@@ -13,7 +13,7 @@ grey = "\x0314"
 
 
 def do_command(self, connection, event):
 def do_command(self, connection, event):
     cmdtype, trigger, command, replyto = CH.disect_command(self, event)
     cmdtype, trigger, command, replyto = CH.disect_command(self, event)
-    
+
     # Do nothing if there is no command.
     # Do nothing if there is no command.
     if not command:
     if not command:
         return
         return
@@ -21,17 +21,17 @@ def do_command(self, connection, event):
         command.split()[0]
         command.split()[0]
     except:
     except:
         return
         return
-    
+
     # The actual commands:
     # The actual commands:
-    if command == "test":
-        if cmdtype == "help":    #Display help text.
-            if len(command.split()) is not 1:
-                return
-            connection.privmsg(replyto, "Strictly for testing purposes only!")
-        elif cmdtype == "cmd":
-            connection.privmsg(replyto, str(self.channels[self.homechannel].owners()))
-            connection.privmsg(event.source.nick, self.channels[event.target].users())
-    
+    # if command == "test":
+    #     if cmdtype == "help":    #Display help text.
+    #         if len(command.split()) is not 1:
+    #             return
+    #         connection.privmsg(replyto, "Strictly for testing purposes only!")
+    #     elif cmdtype == "cmd":
+    #         connection.privmsg(replyto, str(self.channels[self.homechannel].owners()))
+    #         connection.privmsg(event.source.nick, self.channels[event.target].users())
+
     elif command == "cmd" or command == "cmds" or command == "commands":
     elif command == "cmd" or command == "cmds" or command == "commands":
         if cmdtype == "help":    #Display help text.
         if cmdtype == "help":    #Display help text.
             if len(command.split()) is not 1:
             if len(command.split()) is not 1:
@@ -42,7 +42,7 @@ def do_command(self, connection, event):
                 connection.privmsg(replyto, grey + "Public: " + CH.ccc(self, "cmd") + CH.ccc(self, "help") + CH.ccc(self, "away")[:-2] + ".")
                 connection.privmsg(replyto, grey + "Public: " + CH.ccc(self, "cmd") + CH.ccc(self, "help") + CH.ccc(self, "away")[:-2] + ".")
             else:
             else:
                 connection.privmsg(replyto, grey + "Public: " + CH.ccc(self, "cmd") + CH.ccc(self, "help") + CH.ccc(self, "away") + CH.ccc(self, "stopgreet")[:-2] + ".")
                 connection.privmsg(replyto, grey + "Public: " + CH.ccc(self, "cmd") + CH.ccc(self, "help") + CH.ccc(self, "away") + CH.ccc(self, "stopgreet")[:-2] + ".")
-    
+
     elif command == "help":
     elif command == "help":
         if cmdtype == "help":    #Display help text.
         if cmdtype == "help":    #Display help text.
             if len(command.split()) is not 1:
             if len(command.split()) is not 1:
@@ -51,8 +51,8 @@ def do_command(self, connection, event):
         elif cmdtype == "cmd":
         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, "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")
             connection.privmsg(replyto, grey + "Example: " + reset + blue + self.helpchar + "help")
-    
-    
+
+
     elif command.split()[0] == "stopgreet":
     elif command.split()[0] == "stopgreet":
         if cmdtype == "help":    #Display help text.
         if cmdtype == "help":    #Display help text.
             if len(command.split()) is not 1:
             if len(command.split()) is not 1:
@@ -60,7 +60,7 @@ def do_command(self, connection, event):
             connection.privmsg(replyto, "Stops the bot from greeting you or a specific user. Channel, user and option to resume optional.")
             connection.privmsg(replyto, "Stops the bot from greeting you or a specific user. Channel, user and option to resume optional.")
             connection.privmsg(replyto, grey + "Usage: " + blue + "!stopgreet " + reset + italic + "resume " + red + "channel user")
             connection.privmsg(replyto, grey + "Usage: " + blue + "!stopgreet " + reset + italic + "resume " + red + "channel user")
         elif cmdtype == "cmd":
         elif cmdtype == "cmd":
-            
+
             # Check for resume variation of command.
             # Check for resume variation of command.
             resume = False
             resume = False
             try:
             try:
@@ -69,14 +69,14 @@ def do_command(self, connection, event):
                     command = command.split(' ', 1)[1]  # Remove the resume argument. Which makes the following logic easier.
                     command = command.split(' ', 1)[1]  # Remove the resume argument. Which makes the following logic easier.
             except:
             except:
                 pass
                 pass
-            
+
             if len(command.split()) == 1:    # No arguments.
             if len(command.split()) == 1:    # No arguments.
                 if event.target == connection.get_nickname():   # PM.
                 if event.target == connection.get_nickname():   # PM.
                     connection.privmsg(replyto, "Specify at least a channel. For help type " + blue + self.helpchar + "stopgreet" + reset + ".")
                     connection.privmsg(replyto, "Specify at least a channel. For help type " + blue + self.helpchar + "stopgreet" + reset + ".")
                     return
                     return
                 user = event.source.nick
                 user = event.source.nick
                 channel = event.target
                 channel = event.target
-                
+
             if len(command.split()) == 2:    # One argument.
             if len(command.split()) == 2:    # One argument.
                 if command.split()[1] not in self.channels: # Argument is not a channel the bot inhabits.
                 if command.split()[1] not in self.channels: # Argument is not a channel the bot inhabits.
                     if event.target == connection.get_nickname():   # PM.
                     if event.target == connection.get_nickname():   # PM.
@@ -104,13 +104,13 @@ def do_command(self, connection, event):
             if len(command.split()) > 3:    # Too many arguments.
             if len(command.split()) > 3:    # Too many arguments.
                 connection.privmsg(replyto, "Too many arguments. For help type " + blue + self.helpchar + "stopgreet" + reset + ".")
                 connection.privmsg(replyto, "Too many arguments. For help type " + blue + self.helpchar + "stopgreet" + reset + ".")
                 return
                 return
-            
+
             # Check for database record.
             # Check for database record.
             result = self.db.one("SELECT id, stopgreet FROM joins WHERE LOWER(\"user\")=LOWER('" + user + "') AND user_network='" + self.network + "' AND LOWER(channel)=LOWER('" + channel + "') AND channel_network='" + self.network + "'")
             result = self.db.one("SELECT id, stopgreet FROM joins WHERE LOWER(\"user\")=LOWER('" + user + "') AND user_network='" + self.network + "' AND LOWER(channel)=LOWER('" + channel + "') AND channel_network='" + self.network + "'")
             if not result:  # No record in database.
             if not result:  # No record in database.
                 connection.action(replyto, "has not yet had the pleasure of greeting " + red + user + reset + " in " + red + channel + reset + ".")
                 connection.action(replyto, "has not yet had the pleasure of greeting " + red + user + reset + " in " + red + channel + reset + ".")
                 return
                 return
-            
+
             if resume:
             if resume:
                 stopgreet = False
                 stopgreet = False
                 message = "has already every intention to greet "
                 message = "has already every intention to greet "
@@ -125,14 +125,14 @@ def do_command(self, connection, event):
                 self.db.run("UPDATE joins SET stopgreet='" + str(stopgreet) + "' WHERE LOWER(\"user\")=LOWER('" + user + "') AND user_network='" + self.network + "' AND lower(channel)=LOWER('" + channel + "') AND channel_network='" + self.network + "'")
                 self.db.run("UPDATE joins SET stopgreet='" + str(stopgreet) + "' WHERE LOWER(\"user\")=LOWER('" + user + "') AND user_network='" + self.network + "' AND lower(channel)=LOWER('" + channel + "') AND channel_network='" + self.network + "'")
             except:
             except:
                 connection.privmsg(replyto, "Failed to update database.")
                 connection.privmsg(replyto, "Failed to update database.")
-    
+
     elif command.split()[0] == "away":
     elif command.split()[0] == "away":
         if cmdtype == "help":    #Display help text.
         if cmdtype == "help":    #Display help text.
             if len(command.split()) is not 1:
             if len(command.split()) is not 1:
                 return
                 return
             connection.privmsg(replyto, "Sets you away, optionally with reason. This affects the !seen command and the game.")
             connection.privmsg(replyto, "Sets you away, optionally with reason. This affects the !seen command and the game.")
             connection.privmsg(replyto, grey + "Usage: " + blue + "!away " + reset + italic + "reason")
             connection.privmsg(replyto, grey + "Usage: " + blue + "!away " + reset + italic + "reason")
-            
+
         elif cmdtype == "cmd":
         elif cmdtype == "cmd":
             queries.create_ifnot_onrecord(self, "users", event.source.nick)
             queries.create_ifnot_onrecord(self, "users", event.source.nick)
             if len(trigger.split()) == 1:
             if len(trigger.split()) == 1:

+ 13 - 13
rotbot/common/do_everything_to.py

@@ -1,19 +1,19 @@
 from common.networkservices import ChanServ
 from common.networkservices import ChanServ
 
 
 def join(self, connection, channel, key=False):
 def join(self, connection, channel, key=False):
-    ChanServ.unban(connection, channel, connection.get_nickname())
-    ChanServ.akick_del(connection, channel, connection.get_nickname())
-    ChanServ.invite(connection, channel)
-    ChanServ.getkey(connection, channel)
-    knownkey = self.db.one("SELECT key FROM channels WHERE name='" + channel + "' AND network='" + self.network + "'")
-    if key:
-        connection.join(channel, key)
-        if not key == knownkey:
-            self.channelkeys[channel] = key
-    elif knownkey:
-        connection.join(channel, key)
-    else:
-        connection.join(channel)
+    #ChanServ.unban(connection, channel, connection.get_nickname())
+    #ChanServ.akick_del(connection, channel, connection.get_nickname())
+    #ChanServ.invite(connection, channel)
+    #ChanServ.getkey(connection, channel)
+    #knownkey = self.db.one("SELECT key FROM channels WHERE name=%(channel)s AND network=%(network)s", channel=channel, network=self.network.id)
+    # if key:
+    #     connection.join(channel, key)
+    #     if not key == knownkey:
+    #         self.channelkeys[channel] = key
+    # elif knownkey:
+    #     connection.join(channel, key)
+    # else:
+    connection.join(channel)
 
 
 def unban(connection, channel, user, mask):
 def unban(connection, channel, user, mask):
     ChanServ.unban(connection, channel, user)
     ChanServ.unban(connection, channel, user)

+ 26 - 0
rotbot/common/font.py

@@ -0,0 +1,26 @@
+reset = "\x0F"
+bold = "\x02"
+underline = "\x1F"
+italic = "\x1D"
+reverse = "\x16" 	# swap background and foreground colors ("reverse video")
+
+blue = "\x0302"
+green = "\x0303"
+red = "\x0304"
+grey = "\x0314"
+
+
+# def bold:
+#     return '\x02'
+# def italic:
+#     return '\x1D'
+# def underline:
+#     return '\x1F'
+# def reverse:
+#     return '\x16' 	# swap background and foreground colors ("reverse video")
+# def reset:
+#     return '\x0F'
+# def blue:
+#     return '\x0302'
+# def green:
+#     retrun '\x0303'

+ 4 - 4
rotbot/common/log.py

@@ -1,13 +1,13 @@
 from datetime import datetime
 from datetime import datetime
 
 
 def info(message):
 def info(message):
-    print("INFO " + str(datetime.now()) + " " + str(message))
+    print('%s INFO %s' % (datetime.now(), message))
 
 
 def notice(message):
 def notice(message):
-    print("NOTICE " + str(datetime.now()) + " " + str(message))
+    print('%s NOTICE %s' % (datetime.now(), message))
 
 
 def warning(message):
 def warning(message):
-    print("WARNING " + str(datetime.now()) + " " + str(message))
+    print('%s WARNING %s %s' % (datetime.now(), message))
 
 
 def error(message):
 def error(message):
-    print("ERROR " + str(datetime.now()) + " " + str(message))
+    sys.stderr.write('ERROR %s %s' % (datetime.now(), message))

+ 12 - 12
rotbot/common/networkservices.py

@@ -1,35 +1,35 @@
 class NickServ():
 class NickServ():
     def recover_nick(connection, password):
     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.privmsg("NickServ", "GHOST " + connection.nickname + " " + password)  # Recover control of nickname via NickServ, old style.
+        connection.privmsg('NickServ', 'IDENTIFY %s %s' % (connection.nickname, password)) # Identify with NickServ.
+        connection.privmsg('NickServ', 'RECOVER %s %s' % (connection.nickname, password))  # Recover control of nickname via NickServ.
+        connection.privmsg('NickServ', 'GHOST %s %s' % (connection.nickname, password))  # Recover control of nickname via NickServ, old style.
         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.
         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.
 
 
 class ChanServ():
 class ChanServ():
     def invite(connection, channel):
     def invite(connection, channel):
         connection.privmsg("ChanServ", "INVITE " + channel)
         connection.privmsg("ChanServ", "INVITE " + channel)
-    
+
     def getkey(connection, channel):
     def getkey(connection, channel):
         connection.privmsg("ChanServ", "GETKEY " + channel)
         connection.privmsg("ChanServ", "GETKEY " + channel)
-    
+
     def ban(connection, channel, user, reason):
     def ban(connection, channel, user, reason):
         connection.privmsg("ChanServ", "BAN " + channel + " " + user + " " + reason)
         connection.privmsg("ChanServ", "BAN " + channel + " " + user + " " + reason)
-    
+
     def tempban(connection, channel, user, duration, reason):
     def tempban(connection, channel, user, duration, reason):
         connection.privmsg("ChanServ", "BAN " + channel + " +" + duration + " " + user + " " + reason)
         connection.privmsg("ChanServ", "BAN " + channel + " +" + duration + " " + user + " " + reason)
-    
+
     def unban(connection, channel, user):
     def unban(connection, channel, user):
         connection.privmsg("ChanServ", "UNBAN " + channel + " " + user)
         connection.privmsg("ChanServ", "UNBAN " + channel + " " + user)
-    
+
     def akick_add(connection, channel, user):
     def akick_add(connection, channel, user):
         connection.privmsg("ChanServ", "AKICK " + channel + " ADD " + user)
         connection.privmsg("ChanServ", "AKICK " + channel + " ADD " + user)
-        
+
     def akick_del(connection, channel, user):
     def akick_del(connection, channel, user):
         connection.privmsg("ChanServ", "AKICK " + channel + " DEL " + user)
         connection.privmsg("ChanServ", "AKICK " + channel + " DEL " + user)
-    
+
     def kick(connection, channel, user, reason):
     def kick(connection, channel, user, reason):
         connection.privmsg("ChanServ", "KICK " + channel + " " + user + " " + reason)
         connection.privmsg("ChanServ", "KICK " + channel + " " + user + " " + reason)
-    
+
     def give_mode(connection, channel, user, mode):
     def give_mode(connection, channel, user, mode):
         if mode[1] == "q":
         if mode[1] == "q":
             modename = "OWNER"
             modename = "OWNER"
@@ -42,7 +42,7 @@ class ChanServ():
         if mode[1] == "v":
         if mode[1] == "v":
             modename = "VOICE"
             modename = "VOICE"
         connection.privmsg("ChanServ", modename + " " + channel + " " + user)
         connection.privmsg("ChanServ", modename + " " + channel + " " + user)
-    
+
     def take_all_modes(connection, channel, user):
     def take_all_modes(connection, channel, user):
         connection.privmsg("ChanServ", "DEVOICE " + channel + " " + user)
         connection.privmsg("ChanServ", "DEVOICE " + channel + " " + user)
         connection.privmsg("ChanServ", "DEHALFOP " + channel + " " + user)
         connection.privmsg("ChanServ", "DEHALFOP " + channel + " " + user)

+ 9 - 13
rotbot/common/queries.py

@@ -1,15 +1,11 @@
 def create_ifnot_onrecord(self, table, name):
 def create_ifnot_onrecord(self, table, name):
-    record = self.db.one("SELECT name, network FROM " + table + " WHERE LOWER(name)=LOWER(%s) AND LOWER(network)=LOWER(%s)", (name, self.network, ))
-    
+    record = self.db.one('SELECT * FROM rotbot_' + table + ' WHERE LOWER(name)=LOWER(%(name)s) AND network_id=%(network_id)s', name=name, network_id=self.network.id)
+
     if record:  # On record.
     if record:  # On record.
-        # Correct capitalisation of network name, if needed.
-        if not self.network == record[1]:
-            self.db.run("UPDATE networks SET name=%s WHERE LOWER(name)=LOWER(%s)", (self.network, self.network, ))
-        
-        # Correct capitalisation if needed.
-        if not name == record[0]:
-            self.db.run("UPDATE " + table + " SET name=%s WHERE LOWER(name)=LOWER(%s) AND network=%s", (name, name, self.network, ))
-    
-    # Create record.
-    else:   # Not on record.
-        self.db.run("INSERT INTO " + table + " (name, network) VALUES (%s, %s)", (name, self.network, ))
+        if name != record.name:
+            self.db.run('UPDATE rotbot_' + table + ' SET name=%(name)r WHERE LOWER(name)=LOWER(%(name)s) AND network_id=%(network_id)s', name=name, network_id=self.network.id)  # Correct capitalisation
+    else:   # Record created.
+        self.db.run('INSERT INTO rotbot_' + table + ' (name, network_id) VALUES (%(name)s, %(network_id)s)', name=name, network_id=self.network.id)  # Create record.
+        record = self.db.one('SELECT name FROM rotbot_' + table + ' WHERE LOWER(name)=LOWER(%(name)s) AND network_id=%(network_id)s', name=name, network_id=self.network.id)
+
+    return record

+ 63 - 58
rotbot/events/common.py

@@ -16,56 +16,56 @@ class Protectees():
 class Replyto():
 class Replyto():
     def name(connection, event):
     def name(connection, event):
         messages = [
         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 want 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!", 
+            "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 want 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 = [
         actions = [
             "hides!",
             "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 + ".", 
+            "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 + ".",
             "pets " + event.source.nick + ", and sais: Why what a nice little human you are, and such plentifull organs!"
             "pets " + event.source.nick + ", and sais: Why what a nice little human you are, and such plentifull organs!"
         ]
         ]
-        
+
         # Reply with a random message or action.
         # Reply with a random message or action.
         if random.randint(0, 1) == 0:
         if random.randint(0, 1) == 0:
             connection.privmsg(event.target, random.choice(messages))
             connection.privmsg(event.target, random.choice(messages))
         else:
         else:
             connection.action(event.target, random.choice(actions))
             connection.action(event.target, random.choice(actions))
-            
+
 class Aggressiveness():
 class Aggressiveness():
     def retalliation_reason(self, connection, protectee, behaviour):
     def retalliation_reason(self, connection, protectee, behaviour):
         if protectee == connection.get_nickname():  # Bot itself.
         if protectee == connection.get_nickname():  # Bot itself.
@@ -75,41 +75,47 @@ class Aggressiveness():
 
 
 class Lastact():
 class Lastact():
     def update(self, name, type, channel=False, lastact=False, auxiliary=False):
     def update(self, name, type, channel=False, lastact=False, auxiliary=False):
-        
+
         # Create records if not present.
         # Create records if not present.
         if channel:
         if channel:
             queries.create_ifnot_onrecord(self, "channels", channel)
             queries.create_ifnot_onrecord(self, "channels", channel)
         queries.create_ifnot_onrecord(self, "users", name)
         queries.create_ifnot_onrecord(self, "users", name)
-        
+
         # Update record.
         # Update record.
         self.db.run("UPDATE users SET last_act_type=%s, last_act_datetime=%s, last_act_channel=%s, last_act=%s, last_act_auxiliary=%s WHERE name=%s AND network=%s",  (type, str(datetime.now()), channel, lastact, auxiliary, name, self.network))
         self.db.run("UPDATE users SET last_act_type=%s, last_act_datetime=%s, last_act_channel=%s, last_act=%s, last_act_auxiliary=%s WHERE name=%s AND network=%s",  (type, str(datetime.now()), channel, lastact, auxiliary, name, self.network))
-        
+
         # Set user back from away, if user is active.
         # Set user back from away, if user is active.
         if type not in ["nick", "kick", "part", "quit"]:
         if type not in ["nick", "kick", "part", "quit"]:
             self.db.run("UPDATE users SET away=FALSE WHERE name=%s AND network=%s", (name, self.network, ))
             self.db.run("UPDATE users SET away=FALSE WHERE name=%s AND network=%s", (name, self.network, ))
 
 
 class MessageStatistics():
 class MessageStatistics():
     def update(self, event, type):
     def update(self, event, type):
-        type = type + "s"
-        queries.create_ifnot_onrecord(self, "channels", event.target)
-        queries.create_ifnot_onrecord(self, "users", event.source.nick)
-        if not self.db.one("SELECT channel, \"user\" FROM messages WHERE LOWER(channel)=LOWER(%s) AND channel_network=%s AND LOWER(\"user\")=LOWER(%s) AND user_network=%s", (event.target, self.network, event.source.nick, self.network, )):  # Not on record.
-            self.db.run("INSERT INTO messages (channel, channel_network, \"user\", user_network) VALUES (%s, %s, %s, %s)", (event.target, self.network, event.source.nick, self.network , ))   # Create record.
-        self.db.run("UPDATE messages SET " + type + "=" + type + "+1, " + type + "_words=" + type + "_words+" + str(len(event.arguments[0].split())) + ", " + type + "_characters=" + type + "_characters+" + str(len(event.arguments[0])) + " WHERE LOWER(channel)=LOWER('" + event.target + "') AND channel_network='" + self.network + "' AND LOWER(\"user\")=LOWER('" + event.source.nick + "') AND user_network='" + self.network + "'")   # Increment record.
+        if type == 'message':
+            type='m'
+        elif type == 'action':
+            type='a'
+        elif type == 'notice':
+            type='n'
+        channel = queries.create_ifnot_onrecord(self, 'channel', event.target)
+        user = queries.create_ifnot_onrecord(self, 'user', event.source.nick)
+        if not self.db.one('SELECT id FROM rotbot_message WHERE network_id=%(network_id)s AND channel_id=%(channel_id)s AND user_id=%(user_id)s AND type=%(type)s', network_id=self.network.id, channel_id=channel.id ,user_id=user.id, type=type):  # Not on record.
+            self.db.run('INSERT INTO rotbot_message (network_id, channel_id, user_id, type, amount) VALUES (%(network_id)s, %(channel_id)s, %(user_id)s, %(type)s, 1)', network_id=self.network.id, channel_id=channel.id ,user_id=user.id, type=type)   # Create record.
+        else:   # On record.
+            self.db.run('UPDATE rotbot_message SET amount = amount +1 WHERE network_id=%(network_id)s AND channel_id=%(channel_id)s AND user_id=%(user_id)s AND type=%(type)s', network_id=self.network.id, channel_id=channel.id ,user_id=user.id, type=type)  # Increment record.
 
 
 class Inform():
 class Inform():
         def owners(self, connection, message):
         def owners(self, connection, message):
-            log.notice("Message: " + message)
-            if self.homechannel in self.channels:
-                for owner in self.channels[self.homechannel].owners():
+            log.notice('Message: %s' % (message))
+            if self.network.home_channel in self.channels:
+                for owner in self.channels[self.network.home_channel].owners():
                     connection.privmsg(owner, message)
                     connection.privmsg(owner, message)
-        
+
         def notice_owners(self, connection, message):
         def notice_owners(self, connection, message):
-            log.notice("Message: " + message)
-            if self.homechannel in self.channels:
-                for owner in self.channels[self.homechannel].owners():
+            log.notice('Message: %s' % (message))
+            if self.network.home_channel in self.channels:
+                for owner in self.channels[self.network.home_channel].owners():
                     connection.notice(owner, message)
                     connection.notice(owner, message)
-        
+
         def operators(self, connection, message):
         def operators(self, connection, message):
             if self.homechannel in self.channels:
             if self.homechannel in self.channels:
                 for user in self.channels[self.homechannel].owners():
                 for user in self.channels[self.homechannel].owners():
@@ -118,4 +124,3 @@ class Inform():
                     connection.privmsg(user, message)
                     connection.privmsg(user, message)
                 for user in self.channels[self.homechannel].opers():
                 for user in self.channels[self.homechannel].opers():
                     connection.privmsg(user, message)
                     connection.privmsg(user, message)
-

+ 53 - 63
rotbot/events/on_action.py

@@ -1,68 +1,58 @@
-from common import userstatus
+from common import userstatus, font
 from events.common import Replyto, Lastact, MessageStatistics
 from events.common import Replyto, Lastact, MessageStatistics
 
 
-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):
 def process_event(self, connection, event):
-    
-    # Update last act.
-    Lastact.update(self, event.source.nick, "action", channel=event.target, lastact=event.arguments[0])
-    
-    # Save statistic to database.
-    MessageStatistics.update(self, event, "action")
-    
-    # Stop if channelfunction chat if off.
-    if not self.db.one("SELECT chat FROM channels WHERE name='" + event.target + "' AND network='" + self.network + "'"):
-        return
 
 
-    # Slap guard.
-    if event.arguments[0].lower().startswith("slaps ") and not event.target == connection.get_nickname():   # Channel action that stats with "slaps ".
-        
-        # Stop if chat channel function is off.
-        if not self.db.one("SELECT chat FROM channels WHERE name='" + event.target + "' AND network='" + self.network + "'"):
-            return
-    
-        # Only protect the worty.
-        if not userstatus.atleast_voiced(self, event.arguments[0].split(" ")[1], self.homechannel) and not userstatus.atleast_oper(self, event.arguments[0].split(" ")[1], self.homechannel): # Insufficient rights.
-            if event.arguments[0].split(" ")[1].lower() == connection.get_nickname(): # Bot slapped
-                Replyto.name(connection, event)
-            return
-        if userstatus.atleast_voiced(self, event.source.nick, self.homechannel):   # Slapper has atleast voice in home channel.
-            if not self.channels[self.homechannel].is_owner(event.arguments[0].split(" ")[1]):  # Slappee is not owner.
-                if event.arguments[0].split(" ")[1].lower() == connection.get_nickname(): # Bot slapped
-                    Replyto.name(connection, event)
-                return
-            if self.channels[self.homechannel].is_owner(event.source.nick) and  self.channels[self.homechannel].is_owner(event.arguments[0].split(" ")[1]): # Slapper and slappee are owner.
-                if event.arguments[0].split(" ")[1].lower() == connection.get_nickname(): # Bot slapped
-                    Replyto.name(connection, event)
-                return
-        
-        # Respond.
-        if " with a " in event.arguments[0]:
-            if event.arguments[0].split(" with a ", maxsplit=1)[1]:
-                if event.arguments[0].split(" ")[1].lower() == connection.get_nickname().lower(): # Bot slapped
-                    connection.action(event.target, "Takes the " + event.arguments[0].split(" with a ", maxsplit=1)[1] + " like a robot.")
-                else:
-                    connection.action(event.target, "swiftly jumps in front of " + red + event.arguments[0].split(" ")[1] + reset + " to block the " + event.arguments[0].split(" with a ", maxsplit=1)[1])
+    # # Update last act.
+    # Lastact.update(self, event.source.nick, "action", channel=event.target, lastact=event.arguments[0])
+
+    # Save statistic to database.
+    MessageStatistics.update(self, event, 'action')
 
 
-            elif event.arguments[0].split(" ")[1].lower() == connection.get_nickname(): # Bot slapped
-                connection.action(event.target, "bites " + red + event.source.nick + reset + " furiously.")
-            else:
-                connection.action(event.target, "swiftly jumps in front of " + red + event.arguments[0].split(" ")[1] + reset + " to block the slap.")
-        elif len(event.arguments[0].split(" ")) > 1:
-            if event.arguments[0].split(" ")[1].lower() == connection.get_nickname(): # Bot slapped
-                connection.action(event.target, "bites " + red + event.source.nick + reset + " furiously.")
-            else:
-                connection.action(event.target, "swiftly jumps in front of " + red + event.arguments[0].split(" ")[1] + reset + " to block the slap.")
-    
-    # Respond to own name.
-    elif connection.get_nickname().lower() in event.arguments[0].lower() and event.source.nick is not connection.get_nickname(): # Bot's name was mentioned, not by the bot itself.
-        Replyto.name(connection, event)
+    # # Stop if channelfunction chat if off.
+    # if not self.db.one("SELECT chat FROM channels WHERE name='" + event.target + "' AND network='" + self.network + "'"):
+    #     return
+    #
+    # # Slap guard.
+    # if event.arguments[0].lower().startswith("slaps ") and not event.target == connection.get_nickname():   # Channel action that stats with "slaps ".
+    #
+    #     # Stop if chat channel function is off.
+    #     if not self.db.one("SELECT chat FROM channels WHERE name='" + event.target + "' AND network='" + self.network + "'"):
+    #         return
+    #
+    #     # Only protect the worty.
+    #     if not userstatus.atleast_voiced(self, event.arguments[0].split(" ")[1], self.homechannel) and not userstatus.atleast_oper(self, event.arguments[0].split(" ")[1], self.homechannel): # Insufficient rights.
+    #         if event.arguments[0].split(" ")[1].lower() == connection.get_nickname(): # Bot slapped
+    #             Replyto.name(connection, event)
+    #         return
+    #     if userstatus.atleast_voiced(self, event.source.nick, self.homechannel):   # Slapper has atleast voice in home channel.
+    #         if not self.channels[self.homechannel].is_owner(event.arguments[0].split(" ")[1]):  # Slappee is not owner.
+    #             if event.arguments[0].split(" ")[1].lower() == connection.get_nickname(): # Bot slapped
+    #                 Replyto.name(connection, event)
+    #             return
+    #         if self.channels[self.homechannel].is_owner(event.source.nick) and  self.channels[self.homechannel].is_owner(event.arguments[0].split(" ")[1]): # Slapper and slappee are owner.
+    #             if event.arguments[0].split(" ")[1].lower() == connection.get_nickname(): # Bot slapped
+    #                 Replyto.name(connection, event)
+    #             return
+    #
+    #     # Respond.
+    #     if " with a " in event.arguments[0]:
+    #         if event.arguments[0].split(" with a ", maxsplit=1)[1]:
+    #             if event.arguments[0].split(" ")[1].lower() == connection.get_nickname().lower(): # Bot slapped
+    #                 connection.action(event.target, "Takes the " + event.arguments[0].split(" with a ", maxsplit=1)[1] + " like a robot.")
+    #             else:
+    #                 connection.action(event.target, "swiftly jumps in front of " + red + event.arguments[0].split(" ")[1] + reset + " to block the " + event.arguments[0].split(" with a ", maxsplit=1)[1])
+    #
+    #         elif event.arguments[0].split(" ")[1].lower() == connection.get_nickname(): # Bot slapped
+    #             connection.action(event.target, "bites " + red + event.source.nick + reset + " furiously.")
+    #         else:
+    #             connection.action(event.target, "swiftly jumps in front of " + red + event.arguments[0].split(" ")[1] + reset + " to block the slap.")
+    #     elif len(event.arguments[0].split(" ")) > 1:
+    #         if event.arguments[0].split(" ")[1].lower() == connection.get_nickname(): # Bot slapped
+    #             connection.action(event.target, "bites " + red + event.source.nick + reset + " furiously.")
+    #         else:
+    #             connection.action(event.target, "swiftly jumps in front of " + red + event.arguments[0].split(" ")[1] + reset + " to block the slap.")
+    #
+    # # Respond to own name.
+    # elif connection.get_nickname().lower() in event.arguments[0].lower() and event.source.nick is not connection.get_nickname(): # Bot's name was mentioned, not by the bot itself.
+    #     Replyto.name(connection, event)

+ 53 - 51
rotbot/events/on_join.py

@@ -1,5 +1,5 @@
 from common import log, queries
 from common import log, queries
-from events.common import Lastact
+#from events.common import Lastact
 
 
 bold = "\x02"
 bold = "\x02"
 italic = "\x1D"
 italic = "\x1D"
@@ -13,57 +13,59 @@ grey = "\x0314"
 
 
 def process_event(self, connection, event):
 def process_event(self, connection, event):
     log.info(event)
     log.info(event)
-    
-    # Update last act.
-    Lastact.update(self, event.source.nick, "join", channel=event.target)
-    
-    # Add join event to database for statistics.
-    queries.create_ifnot_onrecord(self, "channels", event.target)
-    queries.create_ifnot_onrecord(self, "users", event.source.nick)
-    if not self.db.one("SELECT id FROM joins WHERE LOWER(channel)=LOWER(%s) AND channel_network=%s AND LOWER(\"user\")=LOWER(%s) AND user_network='" + self.network + "'", (event.target, self.network, event.source.nick, )):  # No record yet
-        self.db.run("INSERT INTO joins (channel, channel_network, \"user\", user_network, joins) VALUES (%s, %s, %s, %s, 0)", (event.target, self.network, event.source.nick, self.network, ))
-    self.db.run("UPDATE joins SET joins = joins + 1 WHERE LOWER(channel)=LOWER(%s) AND channel_network=%s AND LOWER(\"user\")=LOWER(%s) AND user_network=%s", (event.target, self.network, event.source.nick, self.network, ))
-    
+
+    # Lastact.update(self, event.source.nick, "join", channel=event.target)  # Update last act.
+
+    # Save resources that are not represented in the database.
+    channel = queries.create_ifnot_onrecord(self, 'channel', event.target)
+    user = queries.create_ifnot_onrecord(self, 'user', event.source.nick)
+
+    # Save to join event database.
+    if not self.db.one('SELECT id FROM rotbot_join WHERE network_id=%(network_id)s AND channel_id=%(channel_id)s AND "user_id"=%(user_id)s', network_id=self.network.id, channel_id=channel.id, user_id=user.id):   # No record yet
+        self.db.run('INSERT INTO rotbot_join (network_id, channel_id, user_id, amount) VALUES (%(network_id)s, %(channel_id)s, %(user_id)s, 1)', network_id=self.network.id, channel_id=channel.id, user_id=user.id)   # Create record.
+    else:
+        self.db.run('UPDATE rotbot_join SET amount = amount + 1 WHERE network_id=%(network_id)s AND channel_id=%(channel_id)s AND user_id=%(user_id)s', network_id=self.network.id, channel_id=channel.id, user_id=user.id)   # Update existing record.
+
     if event.source.nick == connection.get_nickname():  # The bot joined a channel.
     if event.source.nick == connection.get_nickname():  # The bot joined a channel.
-        connection.who(self.homechannel)    # Get whoreplies for users of homechannel.
-        if self.channels[event.target].has_key():
-            if event.target in self.channelkeys:    # New key used to join channel.
-                self.db.run("UPDATE channels SET key='" + self.channelkeys[event.target] + "' WHERE name='" + event.target + "' AND network='" + self.network + "'")    # Save new key to DB.
-                del self.channelkeys[event.target]  # Delete entry.
-        if event.target == self.homechannel:    # Home channel.
-            connection.who(connection.get_nickname())   # get whoreply to add bot to protectees.
+        # connection.who(self.homechannel)    # Get whoreplies for users of homechannel.
+        # if self.channels[event.target].has_key():
+        #     if event.target in self.channelkeys:    # New key used to join channel.
+        #         self.db.run("UPDATE channels SET key='" + self.channelkeys[event.target] + "' WHERE name='" + event.target + "' AND network='" + self.network + "'")    # Save new key to DB.
+        #         del self.channelkeys[event.target]  # Delete entry.
+        # if event.target == self.homechannel:    # Home channel.
+        #     connection.who(connection.get_nickname())   # get whoreply to add bot to protectees.
         return  # Do not greet myself.
         return  # Do not greet myself.
-    
+
     # Promote owners.
     # Promote owners.
     if event.source.nick in self.channels[self.homechannel].owners():
     if event.source.nick in self.channels[self.homechannel].owners():
-        connection.mode(event.target, "+vhoa " + event.source.nick + " " + event.source.nick + " " + event.source.nick + " " + event.source.nick)
-    
-    # Stop if greeting is not wanted.
-    joingreeting = self.db.one("SELECT join_greeting FROM channels WHERE name='" + event.target + "' AND network='" + self.network + "'")
-    stopgreet = self.db.one("SELECT stopgreet FROM joins WHERE channel='" + event.target + "' AND channel_network='" + self.network + "' AND \"user\"='" + event.source.nick + "' AND user_network='" + self.network + "'")
-    if not joingreeting or stopgreet:
-        return
-    
-    # Show greeting.
-    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 + ". 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.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 + " " + str(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 + " " + str(joins) + " times!"
-    if joins < 10000 and str(joins)[-2:] == "00":
-        message = "AMAZING! " + red + event.source.nick + reset + " you have joined " + red + event.target + reset + " " + str(joins) + " times!"
-    try:
-        connection.privmsg(event.target, message)
-    except:
-        pass
+        connection.mode(event.target, '+vhoa %s %s %s %s' % (event.source.nick, event.source.nick, event.source.nick, event.source.nick))
+
+    # # Stop if greeting is not wanted.
+    # joingreeting = self.db.one("SELECT join_greeting FROM channels WHERE name='" + event.target + "' AND network='" + self.network + "'")
+    # stopgreet = self.db.one("SELECT stopgreet FROM joins WHERE channel='" + event.target + "' AND channel_network='" + self.network + "' AND \"user\"='" + event.source.nick + "' AND user_network='" + self.network + "'")
+    # if not joingreeting or stopgreet:
+    #     return
+
+    # # Show greeting.
+    # 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 + ". 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.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 + " " + str(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 + " " + str(joins) + " times!"
+    # if joins < 10000 and str(joins)[-2:] == "00":
+    #     message = "AMAZING! " + red + event.source.nick + reset + " you have joined " + red + event.target + reset + " " + str(joins) + " times!"
+    # try:
+    #     connection.privmsg(event.target, message)
+    # except:
+    #     pass

+ 92 - 101
rotbot/events/on_kick.py

@@ -1,5 +1,6 @@
+from common.queries import create_ifnot_onrecord
 from common.networkservices import ChanServ
 from common.networkservices import ChanServ
-from common import userstatus, do_everything_to, log
+from common import userstatus, do_everything_to, log, font
 from events.common import Aggressiveness, Lastact
 from events.common import Aggressiveness, Lastact
 
 
 bold = "\x02"
 bold = "\x02"
@@ -14,106 +15,96 @@ grey = "\x0314"
 
 
 def process_event(self, connection, event):
 def process_event(self, connection, event):
     log.info(event)
     log.info(event)
-    
-    kicker = event.source.nick
-    channel = event.target
-    kicked = event.arguments[0]
+
+    kicker = create_ifnot_onrecord(self, 'user', event.source.nick)
+    channel = create_ifnot_onrecord(self, 'channel', event.target)
+    kicked = create_ifnot_onrecord(self, 'user', event.arguments[0])
     reason = event.arguments[1]
     reason = event.arguments[1]
-    
-    # Update last act.
-    if reason:
-        Lastact.update(self, kicker, "kick", channel=channel, lastact=kicked, auxiliary=reason)
-        Lastact.update(self, kicked, "kicked", channel=channel, lastact=kicker, auxiliary=reason)
+
+    # # Update last act.
+    # if reason:
+    #     Lastact.update(self, kicker, "kick", channel=channel, lastact=kicked, auxiliary=reason)
+    #     Lastact.update(self, kicked, "kicked", channel=channel, lastact=kicker, auxiliary=reason)
+    # else:
+    #     Lastact.update(self, kicker, "kick", channel=channel, lastact=kicked)
+    #     Lastact.update(self, kicked, "kicked", channel=channel, lastact=kicker)
+
+    # Record kick event in database.
+    if not self.db.one('SELECT id FROM rotbot_kick WHERE network_id=$(network_id)s AND channel_id=$(channel_id)s AND kicker_id=%(kicker_id)s AND kicked_id=%(kicked_id)s', network_id=self.network.id, channel_id=channel.id, kicker_id=kicker.id, kicked_id=kicked.id): # No records for kicker channel combination.
+        self.db.run('INSERT INTO rotbot_kick (network_id, channel_id, kicker_id, kicked_id, amount) VALUES (%(network_id)s, %(channel_id)s, %(kicker_id)s, %(kicked_id)s), 1', network_id=self.network.id, channel_id=channel.id, kicker_id=kicker.id, kicked_id=kicked.id)  # Create record.
     else:
     else:
-        Lastact.update(self, kicker, "kick", channel=channel, lastact=kicked)
-        Lastact.update(self, kicked, "kicked", channel=channel, lastact=kicker)
-    
-    # Create user records if they don't exist.
-    if not self.db.one("SELECT id FROM users WHERE name='" + kicker + "' AND network='" + self.network + "'"):   # Kicker does not have a user record.
-        self.db.run("INSERT INTO \"users\" (name, network) VALUES ('" + kicker + "', '" + self.network + "')")  # Create user record.
-    if not self.db.one("SELECT id FROM users WHERE name='" + kicked + "' AND network='" + self.network + "'"):  # Kicked does not have a user record.
-        self.db.run("INSERT INTO \"users\" (name, network) VALUES ('" + kicked + "', '" + self.network + "')")  # Create user record.
-    
-    # Create kick records if they don't exist.
-    if not self.db.one("SELECT id FROM kicks WHERE channel='" + channel + "'AND channel_network='" + self.network + "' AND \"user\"='" + kicker + "' AND user_network='" + self.network + "'"): # No records for kicker channel combination.
-        self.db.run("INSERT INTO kicks (channel, channel_network, \"user\", user_network) VALUES ('" + channel + "', '" + self.network + "', '" + kicker + "', '" + self.network + "')")
-    if not self.db.one("SELECT id FROM kicks WHERE channel='" + channel + "'AND channel_network='" + self.network + "' AND \"user\"='" + kicked + "' AND user_network='" + self.network + "'"): # No records for kicked channel combination.
-        self.db.run("INSERT INTO kicks (channel, channel_network, \"user\", user_network) VALUES ('" + channel + "', '" + self.network + "', '" + kicked + "', '" + self.network + "')")
-    
-    # Save statistic.
-    self.db.run("UPDATE kicks SET given = given + 1 WHERE channel='" + channel + "'AND channel_network='" + self.network + "' AND \"user\"='" + kicker + "' AND user_network='" + self.network + "'")
-    self.db.run("UPDATE kicks SET received = received + 1 WHERE channel='" + channel + "'AND channel_network='" + self.network + "' AND \"user\"='" + kicked + "' AND user_network='" + self.network + "'")
-    
-    # Update protectees if needed.
-    if channel == self.homechannel: # Kicked from home channel
-        if event.source.nick in self.protectees:    # Protectee kicked.
-            del self.protectees[event.source.nick]  # Remove old nick from list.
-    
-    # Do nothing more when user is not protected.
-    if not userstatus.atleast_halfop(self, kicked, self.homechannel) and not kicked == connection.get_nickname():
-        return
-    
-    # Report.
-    if not channel == self.homechannel: # Not kicked from homechannel.
-        if reason:
-            connection.privmsg(self.homechannel, red + kicked + reset + " has been kicked from " + red + channel + reset + " by " + red + kicker + reset + ": " + green + reason)
-        else:
-            connection.privmsg(self.homechannel, red + kicked + reset + " has been kicked from " + red + channel + reset + " by " + red + kicker + reset + ".")
-    
-    # React.
-    print("Reacting to kick")
-    behaviour = self.db.one("SELECT aggressiveness FROM channels WHERE name='" + channel + "' AND network='" + self.network + "'")
-    if behaviour == "passive":    # Passive behaviour.
-        if kicked == connection.get_nickname() and channel == self.homechannel: # Bot was kicked from it's home channel.
-            ChanServ.unban(connection, channel, kicked)
-            ChanServ.akick_del(connection, channel, kicked)
-            connection.privmsg("ChanServ", "UNBAN " + channel)
-            do_everything_to.join(self, connection, self.homechannel)
-    elif behaviour == "defense_only":   # Defensive behaviour.
-        ChanServ.unban(connection, channel, kicked)
-        ChanServ.akick_del(connection, channel, kicked)
-        if kicked == connection.get_nickname(): # Bot was kicked.
-            connection.privmsg("ChanServ", "UNBAN " + channel)
-            do_everything_to.join(self, connection, channel)
-    elif behaviour == "equal_retalliation":   # Equal retalitory behaviour.
-        ChanServ.akick_del(connection, channel, kicked)
-        
-        # Rejoin if bot was kicked.
-        if kicked == connection.get_nickname():
-            connection.privmsg("ChanServ", "UNBAN " + channel)
-            do_everything_to.join(self, connection, self.homechannel)
-        
-        if event.source.nick == connection.get_nickname() or self.channels[self.homechannel].is_owner(kicker):
-            return  # Stop if offender is bot or owner.
-        if not userstatus.atleast_halfop(self, kicked, self.homechannel) or not kicked == connection.get_nickname():
-            return  # Stop if offended is not atleast halfop and is not the bot itself.
-        if userstatus.atleast_halfop(self, kicker, self.homechannel) and not self.channels[self.homechannel].is_owner(kicked):
-            return  # Stop if offender is at least halfop in the home channel and the offended is not owner.
-        
-        # Kick.
-        do_everything_to.kick(connection, channel, kicker, Aggressiveness.retalliation_reason(self, connection, kicked, behaviour))
-    
-    # Battlebot behaviour.
-    elif behaviour == "battlebot":
-        ChanServ.akick_del(connection, channel, kicked)
-        
-        # Rejoin if bot was kicked.
-        if kicked == connection.get_nickname():
-            print("Rejoining " + channel)
-            do_everything_to.join(self, connection, channel)
-        
-        if event.source.nick == connection.get_nickname() or self.channels[self.homechannel].is_owner(kicker):
-            print("Stop if offender is bot or owner")
-            return  # Stop if offender is bot or owner.
-        if not userstatus.atleast_halfop(self, kicked, self.homechannel) or not kicked == connection.get_nickname():
-            print("Stop if offended is not atleast halfop and is not the bot itself")
-            return  # Stop if offended is not atleast halfop and is not the bot itself.
-        if userstatus.atleast_halfop(self, kicker, self.homechannel) and not self.channels[self.homechannel].is_owner(kicked):
-            print("Stop if offender is at least halfop in the home channel and the offended is not owner")
-            return  # Stop if offender is at least halfop in the home channel and the offended is not owner.
-        print("Kickbanning")
+        self.db.run('UPDATE rotbot_kick SET amount = amount + 1 WHERE network_id=$(network_id)s AND channel_id=$(channel_id)s AND kicker_id=%(kicker_id)s AND kicked_id=%(kicked_id)s', network_id=self.network.id, channel_id=channel.id, kicker_id=kicker.id, kicked_id=kicked.id) # Update record.
 
 
-        ChanServ.tempban(connection, channel, kicker, "1h", "Aggression channel function = equal_retalliation: " + kicked)
-        connection.mode(channel, "+e " + kicked)  # Excempt operator.
-        ChanServ.akick_add(connection, channel, kicker) # Add kicker to ChanServs autokick.
-        do_everything_to.bankick(connection, channel, kicker, event.source, "Aggression channel function = equal_retalliation: " + kicked)
+    # # Update protectees if needed.
+    # if channel == self.homechannel: # Kicked from home channel
+    #     if event.source.nick in self.protectees:    # Protectee kicked.
+    #         del self.protectees[event.source.nick]  # Remove old nick from list.
+    #
+    # # Do nothing more when user is not protected.
+    # if not userstatus.atleast_halfop(self, kicked, self.homechannel) and not kicked == connection.get_nickname():
+    #     return
+    #
+    # # Report.
+    # if not channel == self.homechannel: # Not kicked from homechannel.
+    #     if reason:
+    #         connection.privmsg(self.homechannel, red + kicked + reset + " has been kicked from " + red + channel + reset + " by " + red + kicker + reset + ": " + green + reason)
+    #     else:
+    #         connection.privmsg(self.homechannel, red + kicked + reset + " has been kicked from " + red + channel + reset + " by " + red + kicker + reset + ".")
+    #
+    # # React.
+    # print("Reacting to kick")
+    # behaviour = self.db.one("SELECT aggressiveness FROM channels WHERE name='" + channel + "' AND network='" + self.network + "'")
+    # if behaviour == "passive":    # Passive behaviour.
+    #     if kicked == connection.get_nickname() and channel == self.homechannel: # Bot was kicked from it's home channel.
+    #         ChanServ.unban(connection, channel, kicked)
+    #         ChanServ.akick_del(connection, channel, kicked)
+    #         connection.privmsg("ChanServ", "UNBAN " + channel)
+    #         do_everything_to.join(self, connection, self.homechannel)
+    # elif behaviour == "defense_only":   # Defensive behaviour.
+    #     ChanServ.unban(connection, channel, kicked)
+    #     ChanServ.akick_del(connection, channel, kicked)
+    #     if kicked == connection.get_nickname(): # Bot was kicked.
+    #         connection.privmsg("ChanServ", "UNBAN " + channel)
+    #         do_everything_to.join(self, connection, channel)
+    # elif behaviour == "equal_retalliation":   # Equal retalitory behaviour.
+    #     ChanServ.akick_del(connection, channel, kicked)
+    #
+    #     # Rejoin if bot was kicked.
+    #     if kicked == connection.get_nickname():
+    #         connection.privmsg("ChanServ", "UNBAN " + channel)
+    #         do_everything_to.join(self, connection, self.homechannel)
+    #
+    #     if event.source.nick == connection.get_nickname() or self.channels[self.homechannel].is_owner(kicker):
+    #         return  # Stop if offender is bot or owner.
+    #     if not userstatus.atleast_halfop(self, kicked, self.homechannel) or not kicked == connection.get_nickname():
+    #         return  # Stop if offended is not atleast halfop and is not the bot itself.
+    #     if userstatus.atleast_halfop(self, kicker, self.homechannel) and not self.channels[self.homechannel].is_owner(kicked):
+    #         return  # Stop if offender is at least halfop in the home channel and the offended is not owner.
+    #
+    #     # Kick.
+    #     do_everything_to.kick(connection, channel, kicker, Aggressiveness.retalliation_reason(self, connection, kicked, behaviour))
+    #
+    # # Battlebot behaviour.
+    # elif behaviour == "battlebot":
+    #     ChanServ.akick_del(connection, channel, kicked)
+    #
+    #     # Rejoin if bot was kicked.
+    #     if kicked == connection.get_nickname():
+    #         print("Rejoining " + channel)
+    #         do_everything_to.join(self, connection, channel)
+    #
+    #     if event.source.nick == connection.get_nickname() or self.channels[self.homechannel].is_owner(kicker):
+    #         print("Stop if offender is bot or owner")
+    #         return  # Stop if offender is bot or owner.
+    #     if not userstatus.atleast_halfop(self, kicked, self.homechannel) or not kicked == connection.get_nickname():
+    #         print("Stop if offended is not atleast halfop and is not the bot itself")
+    #         return  # Stop if offended is not atleast halfop and is not the bot itself.
+    #     if userstatus.atleast_halfop(self, kicker, self.homechannel) and not self.channels[self.homechannel].is_owner(kicked):
+    #         print("Stop if offender is at least halfop in the home channel and the offended is not owner")
+    #         return  # Stop if offender is at least halfop in the home channel and the offended is not owner.
+    #     print("Kickbanning")
+    #
+    #     ChanServ.tempban(connection, channel, kicker, "1h", "Aggression channel function = equal_retalliation: " + kicked)
+    #     connection.mode(channel, "+e " + kicked)  # Excempt operator.
+    #     ChanServ.akick_add(connection, channel, kicker) # Add kicker to ChanServs autokick.
+    #     do_everything_to.bankick(connection, channel, kicker, event.source, "Aggression channel function = equal_retalliation: " + kicked)

+ 31 - 31
rotbot/events/on_pubmsg.py

@@ -2,35 +2,35 @@ import datetime
 from events.common import Replyto,  Lastact, MessageStatistics
 from events.common import Replyto,  Lastact, MessageStatistics
 
 
 def process_event(self, connection, event):
 def process_event(self, connection, event):
-    
-    # Update last act.
-    Lastact.update(self, event.source.nick, "msg", channel=event.target, lastact=event.arguments[0])
-    
+
+    # # Update last act.
+    # Lastact.update(self, event.source.nick, "msg", channel=event.target, lastact=event.arguments[0])
+
     # Save statistic to database.
     # Save statistic to database.
-    MessageStatistics.update(self, event, "message")
-    
-    # Stop if channelfunction chat if off.
-    if not self.db.one("SELECT chat FROM channels WHERE name='" + event.target + "' AND network='" + self.network + "'"):
-        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
-        if event.arguments[0].startswith(self.cmdchar):
-            return  # Stop if it's a command.
-        Replyto.name(connection, event)
-    
-    # Character lame.
-    elif event.arguments[0] == len(event.arguments[0]) * event.arguments[0][0]:   # Trigger exclusively same character.
-    
-        # Do not say KKK.
-        if event.arguments[0] == "kk":
-            return
-        
-        # Stop if lamed recently.
-        lastlame = self.db.one("SELECT last_lame FROM channels WHERE name='" + event.target + "' AND network='" + self.network + "'")
-        if lastlame and lastlame > datetime.datetime.now() - datetime.timedelta(minutes=2):    # In the last 2 minutes.
-            return
-        
-        # Update lastlame.
-        self.db.run("UPDATE channels SET last_lame='" + str(datetime.datetime.now()) + "' WHERE name='" + event.target + "' AND network='" + self.network + "'")
-        
-        connection.privmsg(event.target, event.arguments[0] + event.arguments[0][:1])
+    MessageStatistics.update(self, event, 'message')
+
+    # # Stop if channelfunction chat if off.
+    # if not self.db.one("SELECT chat FROM channels WHERE name='" + event.target + "' AND network='" + self.network + "'"):
+    #     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
+    #     if event.arguments[0].startswith(self.cmdchar):
+    #         return  # Stop if it's a command.
+    #     Replyto.name(connection, event)
+    #
+    # # Character lame.
+    # elif event.arguments[0] == len(event.arguments[0]) * event.arguments[0][0]:   # Trigger exclusively same character.
+    #
+    #     # Do not say KKK.
+    #     if event.arguments[0] == "kk":
+    #         return
+    #
+    #     # Stop if lamed recently.
+    #     lastlame = self.db.one("SELECT last_lame FROM channels WHERE name='" + event.target + "' AND network='" + self.network + "'")
+    #     if lastlame and lastlame > datetime.datetime.now() - datetime.timedelta(minutes=2):    # In the last 2 minutes.
+    #         return
+    #
+    #     # Update lastlame.
+    #     self.db.run("UPDATE channels SET last_lame='" + str(datetime.datetime.now()) + "' WHERE name='" + event.target + "' AND network='" + self.network + "'")
+    #
+    #     connection.privmsg(event.target, event.arguments[0] + event.arguments[0][:1])

+ 8 - 6
rotbot/events/on_welcome.py

@@ -2,10 +2,12 @@ from common import log, do_everything_to
 
 
 def process_event(self, connection, event):
 def process_event(self, connection, event):
     log.info(event)    # Handy for debugging. Keep this.
     log.info(event)    # Handy for debugging. Keep this.
-    if self.password:   # Id with NickServ
-        connection.privmsg("NickServ", "identify " + self.password) # Identify with NickServ.
-    channels = self.db.all("SELECT name FROM channels WHERE network='" + self.network + "' AND autojoin=True")
+    self.db.run("UPDATE rotbot_host SET connection_succeeds = connection_succeeds + 1 WHERE id=%s", [self.network.id])
+
+    if self.network.password:   # Id with NickServ
+        connection.privmsg("NickServ", "identify " + self.network.password) # Identify with NickServ.
+    #channels = self.db.all("SELECT name FROM rotbot_channels WHERE network='" + self.network.id + "' AND autojoin=True")
     connection.mode(connection.get_nickname(), "+x")
     connection.mode(connection.get_nickname(), "+x")
-    do_everything_to.join(self, connection, self.homechannel)
-    for channel in channels:    # Join channels with autojoin function.
-        connection.join(channel)
+    do_everything_to.join(self, connection, self.network.home_channel)
+    #for channel in channels:    # Join channels with autojoin function.
+    #    connection.join(channel)

+ 2 - 1
website/rotbot/forms.py

@@ -16,6 +16,7 @@ class NetworkForm(ModelForm):
             'nickname': '<i class="id badge icon"></i>Nickname',
             'nickname': '<i class="id badge icon"></i>Nickname',
             'username': '<i class="id card icon"></i>Username',
             'username': '<i class="id card icon"></i>Username',
             'password': '<i class="privacy icon"></i>Password',
             'password': '<i class="privacy icon"></i>Password',
+            'mail': '<i class="envelope icon"></i>NickServ registration E-mail',
             'home_channel': '<i class="hashtag icon"></i>Home channel',
             'home_channel': '<i class="hashtag icon"></i>Home channel',
             'command_character': '<i class="terminal icon"></i>Command character',
             'command_character': '<i class="terminal icon"></i>Command character',
             'help_character': '<i class="help icon"></i>Help character',
             'help_character': '<i class="help icon"></i>Help character',
@@ -29,7 +30,7 @@ class NetworkForm(ModelForm):
 class HostForm(ModelForm):
 class HostForm(ModelForm):
     class Meta:
     class Meta:
         model=Host
         model=Host
-        exclude=['network']
+        exclude=['network', 'connection_attempts', 'connection_succeeds']
         labels={
         labels={
             'address': '<i class="server icon"></i>Address',
             'address': '<i class="server icon"></i>Address',
             'port': '<i class="dungeon icon"></i>Port',
             'port': '<i class="dungeon icon"></i>Port',

+ 106 - 0
website/rotbot/models.py

@@ -29,6 +29,7 @@ class Network(models.Model):
         max_length=31,
         max_length=31,
         validators=[MaxLengthValidator(31)],
         validators=[MaxLengthValidator(31)],
     )
     )
+    mail = models.EmailField()
     home_channel = models.CharField(
     home_channel = models.CharField(
         max_length=64,
         max_length=64,
         default='#RotBot',
         default='#RotBot',
@@ -79,6 +80,12 @@ class Host(models.Model):
     ssl = models.BooleanField(
     ssl = models.BooleanField(
         default=True,
         default=True,
     )
     )
+    connection_attempts = models.PositiveIntegerField(
+        default=0,
+    )
+    connection_succeeds = models.PositiveIntegerField(
+        default=0,
+    )
 
 
     class Meta:
     class Meta:
         order_with_respect_to = 'network'
         order_with_respect_to = 'network'
@@ -90,3 +97,102 @@ class Host(models.Model):
 
 
     def __str__(self):
     def __str__(self):
         return '%s:%s' % (self.address, self.port)
         return '%s:%s' % (self.address, self.port)
+
+class Channel(models.Model):
+    network = models.ForeignKey(
+        'Network',
+        on_delete=models.PROTECT,
+        related_name="channel",
+        related_query_name="channels",
+    )
+    name = models.CharField(
+        max_length=64,
+    )
+    autojoin = models.BooleanField(
+        default=False,
+        null=True,
+
+    )
+    key = models.CharField(
+        max_length=32,
+        null=True,
+    )
+
+    def __str__(self):
+        return '%s/$s' % (self.network, self.name)
+
+class User(models.Model):
+    network = models.ForeignKey(
+        'Network',
+        on_delete=models.PROTECT,
+    )
+    name = models.CharField(
+        max_length=31
+    )
+    aliasses = models.ManyToManyField('self')
+
+class Join(models.Model):
+    network = models.ForeignKey(
+        'Network',
+        on_delete=models.PROTECT,
+    )
+    channel = models.ForeignKey(
+        'Channel',
+        on_delete=models.PROTECT,
+    )
+    user = models.ForeignKey(
+        'User',
+        on_delete=models.PROTECT,
+    )
+    amount = models.PositiveIntegerField(
+        default=0,
+    )
+
+class Message(models.Model):
+    network = models.ForeignKey(
+        'Network',
+        on_delete=models.PROTECT,
+    )
+    channel = models.ForeignKey(
+        'Channel',
+        on_delete=models.PROTECT,
+    )
+    user = models.ForeignKey(
+        'User',
+        on_delete=models.PROTECT,
+    )
+    amount = models.PositiveIntegerField(
+        default=0,
+    )
+    TYPE_CHOICES = [
+        ('m', 'Message'),
+        ('a', 'Action'),
+        ('n', 'Notice'),
+    ]
+    type = models.CharField(
+        max_length=1,
+        choices=TYPE_CHOICES,
+    )
+
+class Kick(models.Model):
+    network = models.ForeignKey(
+        'Network',
+        on_delete=models.PROTECT,
+    )
+    channel = models.ForeignKey(
+        'Channel',
+        on_delete=models.PROTECT,
+    )
+    kicker = models.ForeignKey(
+        'User',
+        on_delete=models.PROTECT,
+        related_name='kicker',
+    )
+    kicked = models.ForeignKey(
+        'User',
+        on_delete=models.PROTECT,
+        related_name='kicked',
+    )
+    amount = models.PositiveIntegerField(
+        default=0,
+    )

+ 26 - 0
website/rotbot/templates/rotbot/network.html

@@ -1,5 +1,31 @@
 {% extends "base.html" %}
 {% extends "base.html" %}
 {% block content %}
 {% block content %}
+  <div class="ui three inverted grey statistics">
+    <div class="statistic">
+      <div class="value">
+        <i class="hashtag icon"></i> {{ channel_amount }}
+      </div>
+      <div class="label">
+        Channels
+      </div>
+    </div>
+    <div class="statistic">
+      <div class="value">
+        <i class="users icon"></i> {{ user_amount }}
+      </div>
+      <div class="label">
+        Users
+      </div>
+    </div>
+    <div class="statistic">
+      <div class="value">
+        <i class="comments outline icon"></i> {{ message_amount }}
+      </div>
+      <div class="label">
+        Messages
+      </div>
+    </div>
+  </div>
   <div class="ui inverted card">
   <div class="ui inverted card">
     <div class="content">
     <div class="content">
       <div class="header" title="Network">
       <div class="header" title="Network">

+ 12 - 3
website/rotbot/templates/rotbot/network_form.html

@@ -75,7 +75,7 @@
       </div>
       </div>
     </div>
     </div>
   {% endfor %}
   {% endfor %}
-  <div class="three fields">
+  <div class="four fields">
     <div class="required field{% if form.nickname.errors %} error{% endif %}">
     <div class="required field{% if form.nickname.errors %} error{% endif %}">
       <label for="{{ form.nickname.id_for_label }}">{{ form.nickname.label|safe }}</label>
       <label for="{{ form.nickname.id_for_label }}">{{ form.nickname.label|safe }}</label>
       {{ form.nickname }}
       {{ form.nickname }}
@@ -94,6 +94,15 @@
         </div>
         </div>
       {% endif %}
       {% endif %}
     </div>
     </div>
+    <div class="field{% if form.mail.errors %} error{% endif %}">
+      <label for="{{ form.mail.id_for_label }}">{{ form.mail.label|safe }}</label>
+      {{ form.mail }}
+      {% if form.mail.errors %}
+        <div class="ui inverted red message">
+          {{ form.mail.errors }}
+        </div>
+      {% endif %}
+    </div>
     <div class="required field{% if form.username.errors %} error{% endif %}">
     <div class="required field{% if form.username.errors %} error{% endif %}">
       <label for="{{ form.username.id_for_label }}">{{ form.username.label|safe }}</label>
       <label for="{{ form.username.id_for_label }}">{{ form.username.label|safe }}</label>
       {{ form.username }}
       {{ form.username }}
@@ -104,7 +113,8 @@
       {% endif %}
       {% endif %}
     </div>
     </div>
   </div>
   </div>
-      <div class="required field{% if form.home_channel.errors %} error{% endif %}">
+  <div class="three fields">
+    <div class="required field{% if form.home_channel.errors %} error{% endif %}">
       <label for="{{ form.home_channel.id_for_label }}">{{ form.home_channel.label|safe }}</label>
       <label for="{{ form.home_channel.id_for_label }}">{{ form.home_channel.label|safe }}</label>
       {{ form.home_channel }}
       {{ form.home_channel }}
       {% if form.home_channel.errors %}
       {% if form.home_channel.errors %}
@@ -113,7 +123,6 @@
         </div>
         </div>
       {% endif %}
       {% endif %}
     </div>
     </div>
-  <div class="two fields">
     <div class="required field{% if form.command_character.errors %} error{% endif %}">
     <div class="required field{% if form.command_character.errors %} error{% endif %}">
       <label for="{{ form.command_character.id_for_label }}">{{ form.command_character.label|safe }}</label>
       <label for="{{ form.command_character.id_for_label }}">{{ form.command_character.label|safe }}</label>
       {{ form.command_character }}
       {{ form.command_character }}

+ 36 - 14
website/rotbot/templates/rotbot/networks.html

@@ -1,11 +1,45 @@
 {% extends "base.html" %}
 {% extends "base.html" %}
 {% block content %}
 {% block content %}
+  <div class="ui four inverted grey statistics">
+    <div class="statistic">
+      <div class="value">
+        <i class="sitemap icon"></i> {{ network_amount }}
+      </div>
+      <div class="label">
+        Networks
+      </div>
+    </div>
+    <div class="statistic">
+      <div class="value">
+        <i class="hashtag icon"></i> {{ channel_amount }}
+      </div>
+      <div class="label">
+        Channels
+      </div>
+    </div>
+    <div class="statistic">
+      <div class="value">
+        <i class="users icon"></i> {{ user_amount }}
+      </div>
+      <div class="label">
+        Users
+      </div>
+    </div>
+    <div class="statistic">
+      <div class="value">
+        <i class="comments outline icon"></i> {{ message_amount }}
+      </div>
+      <div class="label">
+        Messages
+      </div>
+    </div>
+  </div>
   {% if perms.rotbot.add_network %}
   {% if perms.rotbot.add_network %}
     <a href="{% url 'rotbot:add_network' %}" class="ui inverted right floated button">Add</a>
     <a href="{% url 'rotbot:add_network' %}" class="ui inverted right floated button">Add</a>
   {% endif %}
   {% endif %}
-  {% if network_list %}
+  {% if networks %}
     <div class="ui inverted relaxed divided selection list">
     <div class="ui inverted relaxed divided selection list">
-      {% for network in network_list.all %}
+      {% for network in networks %}
         <div class="item" onclick="location.href='{% url 'rotbot:network' network.slug %}';">
         <div class="item" onclick="location.href='{% url 'rotbot:network' network.slug %}';">
           <a class ="header" href="{% url 'rotbot:network' network.slug %}">{{ network.name }}</a>
           <a class ="header" href="{% url 'rotbot:network' network.slug %}">{{ network.name }}</a>
         </div>
         </div>
@@ -15,15 +49,3 @@
     <p>No networks available.</p>
     <p>No networks available.</p>
   {% endif %}
   {% endif %}
 {% endblock %}
 {% endblock %}
-
-{#
-
-
-
-
-{% if athlete_list|length > 1 %}
-   Team: {% for athlete in athlete_list %} ... {% endfor %}
-{% else %}
-   Athlete: {{ athlete_list.0.name }}
-{% endif %}
-#}

+ 1 - 2
website/rotbot/urls.py

@@ -4,10 +4,9 @@ from . import views
 
 
 app_name = 'rotbot'
 app_name = 'rotbot'
 urlpatterns = [
 urlpatterns = [
-    path('networks/', views.IndexView.as_view(), name='networks'),
+    path('networks/', views.networks, name='networks'),
     path('network/add', views.add_network, name='add_network'),
     path('network/add', views.add_network, name='add_network'),
     path('network/<str:network_slug>/', views.network, name='network'),
     path('network/<str:network_slug>/', views.network, name='network'),
     path('network/<str:network_slug>/edit/', views.edit_network, name='edit_network'),
     path('network/<str:network_slug>/edit/', views.edit_network, name='edit_network'),
     path('network/<str:network_slug>/delete/', views.delete_network, name='delete_network'),
     path('network/<str:network_slug>/delete/', views.delete_network, name='delete_network'),
-    #path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
 ]
 ]

+ 24 - 11
website/rotbot/views.py

@@ -1,12 +1,12 @@
 from django.shortcuts import render, get_object_or_404
 from django.shortcuts import render, get_object_or_404
 from django.http import HttpResponseRedirect
 from django.http import HttpResponseRedirect
 from django.urls import reverse
 from django.urls import reverse
-from django.views import generic
+#from django.views import generic
 from django.contrib.auth.decorators import login_required, permission_required
 from django.contrib.auth.decorators import login_required, permission_required
 from django.forms import modelformset_factory, inlineformset_factory
 from django.forms import modelformset_factory, inlineformset_factory
 
 
 from website.settings import APPLICATION_NAME
 from website.settings import APPLICATION_NAME
-from .models import Network, Host
+from .models import Network, Host, Channel, User, Message
 from .forms import NetworkForm, HostForm
 from .forms import NetworkForm, HostForm
 
 
 def default_keywords(additional_keywords=None):
 def default_keywords(additional_keywords=None):
@@ -17,24 +17,32 @@ def default_keywords(additional_keywords=None):
     return (default_keywords)
     return (default_keywords)
 
 
 
 
-class IndexView(generic.ListView):
-    template_name = 'rotbot/networks.html'
-    context_object_name = 'network_list'
+def networks(request):
+    networks = Network.objects.all()
+    channels = Channel.objects.all()
+    users = User.objects.all()
+    messages = Message.objects.all()
 
 
-    extra_context = {
+    context = {
         'title': 'RotBot',
         'title': 'RotBot',
         'icon': 'robot',
         'icon': 'robot',
         'description': 'Index of RotBot, the IRC robot.',
         'description': 'Index of RotBot, the IRC robot.',
-        'keywords': default_keywords() + 'index',
+        'keywords': default_keywords('index'),
+        'networks': networks,
+        'network_amount': networks.count(),
+        'channel_amount': channels.count(),
+        'user_amount': users.count(),
+        'message_amount': messages.count(),
     }
     }
-
-    def get_queryset(self):
-        #return Question.objects.order_by('-pub_date')[:5]
-        return Network.objects
+    return render(request, 'rotbot/networks.html', context)
 
 
 
 
 def network(request, network_slug):
 def network(request, network_slug):
     network = get_object_or_404(Network, slug=network_slug)
     network = get_object_or_404(Network, slug=network_slug)
+    channels = Channel.objects.filter(network=network)
+    users = User.objects.filter(network=network)
+    messages = Message.objects.filter(network=network)
+
     context = {
     context = {
         'parent_title': 'Networks',
         'parent_title': 'Networks',
         'parent_url': 'rotbot:networks',
         'parent_url': 'rotbot:networks',
@@ -44,6 +52,9 @@ def network(request, network_slug):
         'description': 'Details of ' + network.name,
         'description': 'Details of ' + network.name,
         'keywords': default_keywords() + 'network.name, display, details',
         'keywords': default_keywords() + 'network.name, display, details',
         'network': network,
         'network': network,
+        'channel_amount': channels.count(),
+        'user_amount': users.count(),
+        'message_amount': messages.count(),
     }
     }
     return render(request, 'rotbot/network.html', context)
     return render(request, 'rotbot/network.html', context)
 
 
@@ -90,6 +101,8 @@ def add_network(request):
             if formset.is_valid():
             if formset.is_valid():
                 formset.save()
                 formset.save()
                 return HttpResponseRedirect(reverse('rotbot:network', args=(form.cleaned_data['slug'],)))
                 return HttpResponseRedirect(reverse('rotbot:network', args=(form.cleaned_data['slug'],)))
+        else:
+            formset = HostFormSet(queryset=Host.objects.none())
     else:   # Not a POST request.
     else:   # Not a POST request.
         form = NetworkForm()
         form = NetworkForm()
         formset = HostFormSet(queryset=Host.objects.none())
         formset = HostFormSet(queryset=Host.objects.none())

+ 10 - 2
website/website/settings.py

@@ -81,9 +81,17 @@ WSGI_APPLICATION = 'website.wsgi.application'
 # https://docs.djangoproject.com/en/2.2/ref/settings/#databases
 # https://docs.djangoproject.com/en/2.2/ref/settings/#databases
 
 
 DATABASES = {
 DATABASES = {
+    # 'default': {
+    #     'ENGINE': 'django.db.backends.sqlite3',
+    #     'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
+    # }
     'default': {
     'default': {
-        'ENGINE': 'django.db.backends.sqlite3',
-        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
+        'ENGINE': 'django.db.backends.postgresql_psycopg2',
+        'NAME': 'website',
+        'USER': 'website',
+        'PASSWORD': 'oGPnbiqh55QKLhmnKQgS92h74j0e9d6LE58cSsD1',
+        'HOST': '127.0.0.1',
+        'PORT': '5432',
     }
     }
 }
 }