Selaa lähdekoodia

Lots of rotbot refactoring

tBKwtWS 6 vuotta sitten
vanhempi
sitoutus
2c0f150310

+ 17 - 0
requirements.txt

@@ -1,6 +1,23 @@
 Django==2.2.6
 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
+postgres==3.0.0
 psycopg2==2.8.4
+psycopg2-binary==2.8.4
+psycopg2-pool==1.1
 pytz==2019.2
+six==1.13.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
 
-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 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
-#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.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):
 #         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):
 #         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):
 #         log.info(event)
@@ -98,10 +98,10 @@ grey = "\x0314"
 #         else:
 #             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):
 #         log.info(event)
@@ -109,134 +109,134 @@ grey = "\x0314"
 #         # Update last act.
 #         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.admin.do_command(self, connection, event)
 #         commands.statistics.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.admin.do_command(self, connection, event)
 #         commands.statistics.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):
 #         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
-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 AdminHelpers as AH
 
@@ -15,29 +15,32 @@ grey = "\x0314"
 
 def do_command(self, connection, event):
     cmdtype, trigger, command, replyto = CH.disect_command(self, event)
-    
+
     # Do nothing if there is no command.
     if not command:
-        return 
+        return
     try:
         command.split()[0]
     except:
         return
-    
+
     # Ignore channel commands from users that do not have at least voice in homechannel or operator status in target channel.
     if event.type == "pubmsg":   # It's a channel message.
-        if not 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
-    
-    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 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)
         elif len(command.split()) == 1: # 1 argument.
             for channel in self.channels:
                 connection.mode(channel, "+ohv " + event.source.nick + " " + event.source.nick + " " + event.source.nick)
-    
+
     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":
             message = grey + "Admin: "
             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.
                 return
             connection.privmsg(replyto, message[:-2] + ".")
-    
+
     elif command.split()[0] == "quit":
         if cmdtype == "help":    # Display help text.
             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,  grey + "Usage: " + blue + "!quit " + reset + italic + "reason")
         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
             if len(command.split()) == 1:
                 log.info("Killed by: " + event.source.nick)
@@ -80,10 +83,10 @@ def do_command(self, connection, event):
             else:
                 log.info("Killed by " + event.source.nick + " for " + trigger.split(maxsplit=1)[1])
                 self.die(msg = "[" + event.source.nick + "] " + trigger.split(maxsplit=1)[1])
-    
+
     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
         if cmdtype == "help":    # Display help text.
             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)
             else:
                 self.disconnect(msg = "[" + event.source.nick + "] " + command.split(maxsplit=1)[1])
-    
+
     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
         if cmdtype == "help":    # Display help text.
             if len(command.split()) is not 1:
@@ -110,10 +113,10 @@ def do_command(self, connection, event):
                 return
             from common.networkservices import NickServ
             NickServ.recover_nick(connection, self.password)
-    
+
     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
         if cmdtype == "help":    #Display help text.
             if len(command.split()) is not 1:
@@ -132,7 +135,7 @@ def do_command(self, connection, event):
                 do_everything_to.join(self, connection, channel)
                 return
             do_everything_to.join(self, connection, channel, key)
-    
+
     elif command.split()[0] == "part":
         if cmdtype == "help":    #Display help text.
             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, grey + "Usage: " + blue + self.cmdchar + "join " + red + italic + "channel " + reset + italic + "password")
         elif cmdtype == "cmd":
-            
+
             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
-            
+
             targetadmin = False
             if userstatus.atleast_oper(self, event.source.nick, event.target):
                 targetadmin = True
-            
+
             if len(command.split()) == 1:   # No arguments.
                 if event.target in self.channels:   # It's a channel message.
                     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 + ".")
                         return
-                    if event.target == self.homechannel:
+                    if event.target == self.network.home_channel:
                         connection.action(replyto, "shall not abandon it's home channel!")
                         return
                     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.
                     connection.privmsg(replyto, "Denied. You need to have at least operator status in " + red + self.homechan + reset + " or " + red + command.split()[1] + reset + ".")
                     return
-                if command.split()[1] == self.homechannel:
+                if command.split()[1] == self.network.home_channel:
                     connection.action("shall not abandon it's home channel!")
                     return
                 try:
                     connection.part(command.split()[1], command.split(maxsplit=2)[2])
                 except:
                     connection.part(command.split()[1], event.source.nick)
-    
+
     elif command.split()[0] == "msg" or  command.split(maxsplit=1)[0] == "act":
         if cmdtype == "help":    #Display help text.
             if len(command.split()) is not 1:
@@ -188,7 +191,7 @@ def do_command(self, connection, event):
             connection.privmsg(replyto, message)
             connection.privmsg(replyto, grey + "Usage: " + blue + "!" + command.split(maxsplit=1)[0] + reset + italic + " target " + arguments)
         elif cmdtype == "cmd":
-            
+
             # Parse user input.
             try:
                 destination = trigger.split()[1]
@@ -200,9 +203,9 @@ def do_command(self, connection, event):
             except IndexError:  # User did not specify a message.
                 connection.privmsg(replyto, "Message not specified. For help type: " + blue + self.helpchar + command.split(maxsplit=1)[0])
                 return
-            
+
             # Send the message if user has owner status in the home channel.
-            if self.channels[self.homechannel].is_owner(event.source.nick): 
+            if self.channels[self.network.home_channel].is_owner(event.source.nick):
                 if destination == connection.get_nickname():
                     connection.privmsg(replyto, "To prevent dying in a loop I shall not message myself.")
                     return
@@ -210,22 +213,22 @@ def do_command(self, connection, event):
                     connection.action(destination, message)
                 else:
                     connection.privmsg(destination, message)
-            
+
             # Reply error when bot does not inhabit destination channel.
             elif destination not in self.channels:
                 connection.action(replyto, "does not inhabit " + red + destination + reset + ".")
-            
+
             # Send message if user has at least operator status the home channel or the channel the message is intended for.
-            elif 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":
                     connection.action(destination, message)
                 else:
                     connection.privmsg(destination, message)
-            
+
             # Reply error if user is not operator of destination channel.
             else:
                 connection.privmsg(replyto, "Denied, you need to be an operator of " + red + destination + reset +".")
-    
+
     elif command.split()[0] == "channelfunctions":
         if cmdtype == "help":    #Display help text. # Help code block first, as it is impossible to predict for what channel a later command is going to be issued. Rights filtering after help test.
             if len(command.split()) is not 1:
@@ -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 describe " + reset + italic + "function")
         elif cmdtype == "cmd":
-            
+
             if len(command.split()) == 1:   # No arguments.
                 if event.target == connection.get_nickname():   # Command issued via PM.
                     connection.privmsg(replyto, "Nothing to display, Specify a channel.")
                 else:   # Command issued as channel message.
                     message = AH.get_channelfunctions(self, event.target)
                     connection.privmsg(replyto, message)
-            
+
             elif len(command.split()) == 2:   # One argument.
                 if command.split()[1] in self.channels: # Info requested on specific channel.
                     message = 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 + ".")
                     else:   # Help request.
                         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.
                 channel = event.target
                 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.
                             connection.privmsg(replyto, "The value of this channel function can only be \"on\" or \"off\". For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
                             return
-                    if not 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
-                    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() + "=%s WHERE name='" + event.target + "' AND network='" + self.network + "'", (command.split()[2].lower(), ))
-            
+
             elif len(command.split()) == 4: # Three arguments.
                 if not command.split()[1] in self.channels: # Bot does not inhabit channel to be altered.
                     connection.privmsg(replyto, command.split()[1] + " is not a channel I inhabit. For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
@@ -290,15 +293,15 @@ def do_command(self, connection, event):
                 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")
                     return
-                
+
                 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 + ".")
                     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
-                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:
                     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:
@@ -306,10 +309,10 @@ def do_command(self, connection, event):
                     return
             else:   # Too many arguments.
                 connection.privmsg(replyto, "Too many arguments. For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
-    
+
     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
         if cmdtype == "help":    # Display help text.
             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, grey + "Usage: " + blue + self.cmdchar + "registernick " + reset + italic + "email")
         elif cmdtype == "cmd":
-            
+
             if len(command.split()) == 1:
                 connection.privmsg(replyto, "Insufficient arguments. For help type " + blue + self.helpchar + "registernick" + reset + ".")
             elif len(command.split()) > 2:
@@ -329,10 +332,10 @@ def do_command(self, connection, event):
                 connection.privmsg("NickServ", "REGISTER " + password + " " + trigger.split()[1])
             else:
                 connection.privmsg("NickServ", "REGISTER " + self.db.one("SELECT password FROM networks WHERE name='" + self.network + "'") + " " + trigger.split()[1])
-    
+
     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
         if cmdtype == "help":    # Display help text.
             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, grey + "Example: " + blue + self.cmdchar + "banall " + reset + italic + "host")
         elif cmdtype == "cmd":
-            
+
             if len(command.split()) == 1:
                 connection.privmsg(replyto, "Insufficient arguments. For help type " + blue + self.helpchar + "banall" + reset + ".")
             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]:
                     connection.privmsg(replyto, "Only supply the host, all nicknames and usernames will be banned.")
                 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]):
                     connection.privmsg(replyto, "The toplevel domain can not containof only numbers.")
                 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):
         trigger = event.arguments[0]
         command = trigger[1:].lower()   #Command without prefix.
-        
+
         # 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:
             cmdtype = False
-        
+
         # Determine where to reply to.
-        if event.type == "pubmsg":
+        if event.type == 'pubmsg':
             replyto = event.target
-        elif event.type == "privmsg":
+        elif event.type == 'privmsg':
             replyto = event.source.nick
         else:
             replyto = False
-            
+
         return(cmdtype, trigger, command, replyto)
-    
+
     def ccc(self, command, rights=False, event=False):   # Commandlist colour coding and channel rights filter.
         if rights:
             show = False
-            
+
             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
             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
             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
             if not event.target == self.connection.get_nickname():   # Channel message.
                 if rights["chan"] == "owner":
@@ -84,13 +84,13 @@ class AdminHelpers():
         else:
             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 + ".")
-        
+
     def is_channelfunction(value):
         if value.lower() in ["autojoin", "aggressiveness", "join_greeting",  "statistics_commands",  "games", "chat"]:
             return True
         else:
             return False
-    
+
     def describe_channelfunction(function):
         if function == "autojoin":
             message = "Join the channel automaticly after connecting."
@@ -106,15 +106,15 @@ class AdminHelpers():
             message = "Respond to and initiate chat."
         else:
             message = "Sorry, this function has no description yet."
-        
+
         return message
-    
+
     def is_aggressiveness(value):
         if value.lower() in ["passive", "defense_only", "equal_retalliation", "battlebot"]:
             return True
         else:
             return False
-        
+
 
 class GameHelpers():
     def roll_dice(amount, type):
@@ -122,7 +122,7 @@ class GameHelpers():
         for iterations in range(amount):
             rolls.append(random.randint(1, type))
         return rolls
-    
+
     def get_info(self, user):
         user = user.lower()
         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]
         ap_spent = userrecord[5]
         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)
         kickxp = given * received
         total_xp = ((joins + kickxp + chatxp) / 120) + float(coin_spent + ap_spent / 8)
-        
+
         xp = total_xp - xp_spent
         ap = total_xp - float(ap_spent)
         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)
         if xp < 0:
             xp = 0
-        
+
         return level, xp, xp_spent, total_xp, karma, coin, coin_spent, coin_given, ap, ap_spent
-    
+
     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, ))
         if sort == "level":
@@ -213,7 +213,7 @@ class GameHelpers():
                 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 + "], "
         return message[:-2]
-    
+
     def player_info(self, user):
         level, xp, xpspent, totalxp, karma, coin, coinspent, coingiven, ap, apspent = GameHelpers.get_info(self, user)
         if ap < 0:

+ 21 - 21
rotbot/commands/public.py

@@ -13,7 +13,7 @@ grey = "\x0314"
 
 def do_command(self, connection, event):
     cmdtype, trigger, command, replyto = CH.disect_command(self, event)
-    
+
     # Do nothing if there is no command.
     if not command:
         return
@@ -21,17 +21,17 @@ def do_command(self, connection, event):
         command.split()[0]
     except:
         return
-    
+
     # 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":
         if cmdtype == "help":    #Display help text.
             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] + ".")
             else:
                 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":
         if cmdtype == "help":    #Display help text.
             if len(command.split()) is not 1:
@@ -51,8 +51,8 @@ def do_command(self, connection, event):
         elif cmdtype == "cmd":
             connection.privmsg(replyto, "Replace the " + italic + "! " + reset + "prefix of any comand with " + italic + self.helpchar + " " + reset + "for help with a specific command. Request the command list with: " + blue + "!cmd")
             connection.privmsg(replyto, grey + "Example: " + reset + blue + self.helpchar + "help")
-    
-    
+
+
     elif command.split()[0] == "stopgreet":
         if cmdtype == "help":    #Display help text.
             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, grey + "Usage: " + blue + "!stopgreet " + reset + italic + "resume " + red + "channel user")
         elif cmdtype == "cmd":
-            
+
             # Check for resume variation of command.
             resume = False
             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.
             except:
                 pass
-            
+
             if len(command.split()) == 1:    # No arguments.
                 if event.target == connection.get_nickname():   # PM.
                     connection.privmsg(replyto, "Specify at least a channel. For help type " + blue + self.helpchar + "stopgreet" + reset + ".")
                     return
                 user = event.source.nick
                 channel = event.target
-                
+
             if len(command.split()) == 2:    # One argument.
                 if command.split()[1] not in self.channels: # Argument is not a channel the bot inhabits.
                     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.
                 connection.privmsg(replyto, "Too many arguments. For help type " + blue + self.helpchar + "stopgreet" + reset + ".")
                 return
-            
+
             # 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 + "'")
             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 + ".")
                 return
-            
+
             if resume:
                 stopgreet = False
                 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 + "'")
             except:
                 connection.privmsg(replyto, "Failed to update database.")
-    
+
     elif command.split()[0] == "away":
         if cmdtype == "help":    #Display help text.
             if len(command.split()) is not 1:
                 return
             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")
-            
+
         elif cmdtype == "cmd":
             queries.create_ifnot_onrecord(self, "users", event.source.nick)
             if len(trigger.split()) == 1:

+ 13 - 13
rotbot/common/do_everything_to.py

@@ -1,19 +1,19 @@
 from common.networkservices import ChanServ
 
 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):
     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
 
 def info(message):
-    print("INFO " + str(datetime.now()) + " " + str(message))
+    print('%s INFO %s' % (datetime.now(), message))
 
 def notice(message):
-    print("NOTICE " + str(datetime.now()) + " " + str(message))
+    print('%s NOTICE %s' % (datetime.now(), message))
 
 def warning(message):
-    print("WARNING " + str(datetime.now()) + " " + str(message))
+    print('%s WARNING %s %s' % (datetime.now(), 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():
     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.
 
 class ChanServ():
     def invite(connection, channel):
         connection.privmsg("ChanServ", "INVITE " + channel)
-    
+
     def getkey(connection, channel):
         connection.privmsg("ChanServ", "GETKEY " + channel)
-    
+
     def ban(connection, channel, user, reason):
         connection.privmsg("ChanServ", "BAN " + channel + " " + user + " " + reason)
-    
+
     def tempban(connection, channel, user, duration, reason):
         connection.privmsg("ChanServ", "BAN " + channel + " +" + duration + " " + user + " " + reason)
-    
+
     def unban(connection, channel, user):
         connection.privmsg("ChanServ", "UNBAN " + channel + " " + user)
-    
+
     def akick_add(connection, channel, user):
         connection.privmsg("ChanServ", "AKICK " + channel + " ADD " + user)
-        
+
     def akick_del(connection, channel, user):
         connection.privmsg("ChanServ", "AKICK " + channel + " DEL " + user)
-    
+
     def kick(connection, channel, user, reason):
         connection.privmsg("ChanServ", "KICK " + channel + " " + user + " " + reason)
-    
+
     def give_mode(connection, channel, user, mode):
         if mode[1] == "q":
             modename = "OWNER"
@@ -42,7 +42,7 @@ class ChanServ():
         if mode[1] == "v":
             modename = "VOICE"
         connection.privmsg("ChanServ", modename + " " + channel + " " + user)
-    
+
     def take_all_modes(connection, channel, user):
         connection.privmsg("ChanServ", "DEVOICE " + 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):
-    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.
-        # 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():
     def name(connection, event):
         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 = [
             "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!"
         ]
-        
+
         # Reply with a random message or action.
         if random.randint(0, 1) == 0:
             connection.privmsg(event.target, random.choice(messages))
         else:
             connection.action(event.target, random.choice(actions))
-            
+
 class Aggressiveness():
     def retalliation_reason(self, connection, protectee, behaviour):
         if protectee == connection.get_nickname():  # Bot itself.
@@ -75,41 +75,47 @@ class Aggressiveness():
 
 class Lastact():
     def update(self, name, type, channel=False, lastact=False, auxiliary=False):
-        
+
         # Create records if not present.
         if channel:
             queries.create_ifnot_onrecord(self, "channels", channel)
         queries.create_ifnot_onrecord(self, "users", name)
-        
+
         # 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))
-        
+
         # Set user back from away, if user is active.
         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, ))
 
 class MessageStatistics():
     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():
         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)
-        
+
         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)
-        
+
         def operators(self, connection, message):
             if self.homechannel in self.channels:
                 for user in self.channels[self.homechannel].owners():
@@ -118,4 +124,3 @@ class Inform():
                     connection.privmsg(user, message)
                 for user in self.channels[self.homechannel].opers():
                     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
 
-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):
-    
-    # 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 events.common import Lastact
+#from events.common import Lastact
 
 bold = "\x02"
 italic = "\x1D"
@@ -13,57 +13,59 @@ grey = "\x0314"
 
 def process_event(self, connection, 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.
-        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.
-    
+
     # Promote 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 import userstatus, do_everything_to, log
+from common import userstatus, do_everything_to, log, font
 from events.common import Aggressiveness, Lastact
 
 bold = "\x02"
@@ -14,106 +15,96 @@ grey = "\x0314"
 
 def process_event(self, connection, 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]
-    
-    # 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:
-        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
 
 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.
-    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):
     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")
-    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',
             'username': '<i class="id card icon"></i>Username',
             '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',
             'command_character': '<i class="terminal icon"></i>Command character',
             'help_character': '<i class="help icon"></i>Help character',
@@ -29,7 +30,7 @@ class NetworkForm(ModelForm):
 class HostForm(ModelForm):
     class Meta:
         model=Host
-        exclude=['network']
+        exclude=['network', 'connection_attempts', 'connection_succeeds']
         labels={
             'address': '<i class="server icon"></i>Address',
             'port': '<i class="dungeon icon"></i>Port',

+ 106 - 0
website/rotbot/models.py

@@ -29,6 +29,7 @@ class Network(models.Model):
         max_length=31,
         validators=[MaxLengthValidator(31)],
     )
+    mail = models.EmailField()
     home_channel = models.CharField(
         max_length=64,
         default='#RotBot',
@@ -79,6 +80,12 @@ class Host(models.Model):
     ssl = models.BooleanField(
         default=True,
     )
+    connection_attempts = models.PositiveIntegerField(
+        default=0,
+    )
+    connection_succeeds = models.PositiveIntegerField(
+        default=0,
+    )
 
     class Meta:
         order_with_respect_to = 'network'
@@ -90,3 +97,102 @@ class Host(models.Model):
 
     def __str__(self):
         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" %}
 {% 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="content">
       <div class="header" title="Network">

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

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

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

@@ -1,11 +1,45 @@
 {% extends "base.html" %}
 {% 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 %}
     <a href="{% url 'rotbot:add_network' %}" class="ui inverted right floated button">Add</a>
   {% endif %}
-  {% if network_list %}
+  {% if networks %}
     <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 %}';">
           <a class ="header" href="{% url 'rotbot:network' network.slug %}">{{ network.name }}</a>
         </div>
@@ -15,15 +49,3 @@
     <p>No networks available.</p>
   {% endif %}
 {% 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'
 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/<str:network_slug>/', views.network, name='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('<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.http import HttpResponseRedirect
 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.forms import modelformset_factory, inlineformset_factory
 
 from website.settings import APPLICATION_NAME
-from .models import Network, Host
+from .models import Network, Host, Channel, User, Message
 from .forms import NetworkForm, HostForm
 
 def default_keywords(additional_keywords=None):
@@ -17,24 +17,32 @@ def default_keywords(additional_keywords=None):
     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',
         'icon': '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):
     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 = {
         'parent_title': 'Networks',
         'parent_url': 'rotbot:networks',
@@ -44,6 +52,9 @@ def network(request, network_slug):
         'description': 'Details of ' + network.name,
         'keywords': default_keywords() + 'network.name, display, details',
         'network': network,
+        'channel_amount': channels.count(),
+        'user_amount': users.count(),
+        'message_amount': messages.count(),
     }
     return render(request, 'rotbot/network.html', context)
 
@@ -90,6 +101,8 @@ def add_network(request):
             if formset.is_valid():
                 formset.save()
                 return HttpResponseRedirect(reverse('rotbot:network', args=(form.cleaned_data['slug'],)))
+        else:
+            formset = HostFormSet(queryset=Host.objects.none())
     else:   # Not a POST request.
         form = NetworkForm()
         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
 
 DATABASES = {
+    # 'default': {
+    #     'ENGINE': 'django.db.backends.sqlite3',
+    #     'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
+    # }
     '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',
     }
 }