فهرست منبع

Load of rotbot work

tBKwtWS 6 سال پیش
والد
کامیت
f460575d4b
36فایلهای تغییر یافته به همراه1657 افزوده شده و 1053 حذف شده
  1. 39 116
      rotbot/bot.py
  2. 2 2
      rotbot/commands/admin.py
  3. 89 0
      rotbot/commands/chat.py
  4. 122 147
      rotbot/commands/common.py
  5. 165 189
      rotbot/commands/games.py
  6. 1 1
      rotbot/commands/statistics.py
  7. 137 31
      rotbot/common/queries.py
  8. 111 111
      rotbot/events/common.py
  9. 4 1
      rotbot/events/on_action.py
  10. 9 0
      rotbot/events/on_error.py
  11. 13 0
      rotbot/events/on_invite.py
  12. 7 2
      rotbot/events/on_join.py
  13. 6 0
      rotbot/events/on_keyset.py
  14. 7 0
      rotbot/events/on_nicknameinuse.py
  15. 12 0
      rotbot/events/on_part.py
  16. 19 0
      rotbot/events/on_privmsg.py
  17. 46 0
      rotbot/events/on_privnotice.py
  18. 4 2
      rotbot/events/on_pubmsg.py
  19. 9 0
      rotbot/events/on_pubnotice.py
  20. 11 0
      rotbot/events/on_quit.py
  21. 8 0
      rotbot/events/on_topic.py
  22. 11 6
      rotbot/events/on_welcome.py
  23. 2 3
      rotbot/events/on_whoreply.py
  24. 39 1
      website/rotbot/forms.py
  25. 43 2
      website/rotbot/migrations/0001_initial.py
  26. 97 4
      website/rotbot/models.py
  27. 27 0
      website/rotbot/templates/rotbot/add_curseword.html
  28. 90 0
      website/rotbot/templates/rotbot/index.html
  29. 128 125
      website/rotbot/templates/rotbot/network_form.html
  30. 13 10
      website/rotbot/urls.py
  31. 0 300
      website/rotbot/views.py
  32. 0 0
      website/rotbot/views/__init__.py
  33. 99 0
      website/rotbot/views/channel.py
  34. 31 0
      website/rotbot/views/common.py
  35. 107 0
      website/rotbot/views/general.py
  36. 149 0
      website/rotbot/views/network.py

+ 39 - 116
rotbot/bot.py

@@ -1,21 +1,17 @@
 #! /usr/bin/env python
 
-import sys, ssl, random#, string,
+import sys, ssl, random
 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   # 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, font, queries
-from common.networkservices import NickServ
-from events.common import Inform #, MessageStatistics #Lastact,
+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, events.on_part, events.on_quit, events.on_topic, events.on_privmsg, events.on_pubnotice, events.on_nicknameinuse, events.on_error, events.on_keyset, events.on_privnotice
+from common import log, font
+from events.common import Inform
 
 class PyRot(irc.bot.SingleServerIRCBot):
     def __init__(self, network, db, webgui):
-
-        # Globals, so it's not needed to pass network and db with every call.
         self.network = network
         self.db = db
         self.webgui = webgui
@@ -48,132 +44,59 @@ class PyRot(irc.bot.SingleServerIRCBot):
 
     ## 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_nicknameinuse(self, connection, event):  # Bots nickname is already taken.
+        events.on_nicknameinuse.process_event(self, connection, event)
 
-    def on_welcome(self, connection, event):
+    def on_welcome(self, connection, event):    # IRC server welcomes bot after connecting.
         events.on_welcome.process_event(self, connection, 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_error.process_event(self, connection, event)
+
+    def on_nick(self, connection, event):   # User changes nickname.
+        events.on_nick.process_event(self, connection, event)
+
+    def on_join(self, connection, event):   # User joins a channel.
         events.on_join.process_event(self, connection, event)
 
-    def on_mode(self, connection, event):
+    def on_mode(self, connection, event):   # Mode change.
         events.on_mode.process_event(self, connection, event)
 
-    def on_kick(self, connection, event):
+    def on_kick(self, connection, event):   # User kicks user from channel.
         events.on_kick.process_event(self, connection, event)
-#
-#     def on_part(self, connection, event):
-#         log.info(event)
-#
-#         # Update protectees.
-#         if event.target == self.homechannel and event.source.nick in self.protectees:    # Protectee parted home channel.
-#             del self.protectees[event.source.nick] # Delete from protectees.
-#
-#
-#     def on_quit(self, connection, event):
-#         log.info(event)
-#
-#         # Update protectees.
-#         if event.source.nick in self.protectees:    # Protectee parted home channel.
-#             del self.protectees[event.source.nick] # Delete from protectees.
-#
-    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)
-#
-#
-    def on_pubmsg(self, connection,  event):    # Channel message
-        events.on_pubmsg.process_event(self, connection, event)
-
-    def on_action(self, connection, event): # Is this both channel and private actions?
-        events.on_action.process_event(self, connection, event)
-
-    def on_privmsg(self, connection, event):    # Private message
-        log.info(event)
 
-        user = queries.create_or_get_and_update_last_event(self, 'user', 'pm', user_name=event.source.nick, event_subject_name=event.target)
-        channel = None
-        if not event.source.nick == connection.get_nickname():  # Message is not from myself.
-            Inform.owners(self, connection, 'PM from %s%s%s: %s%s' % (font.red, event.source.nick, font.grey, font.green, event.arguments[0]))  # Forward message to users with owner rights of home channel.
+    def on_part(self, connection, event):   # User parts channel.
+        events.on_part.process_event(self, connection, event)
 
-        commands.public.do_command(self, connection, event, user, channel)
-        commands.admin.do_command(self, connection, event, user, channel)
-        commands.statistics.do_command(self, connection, event, user, channel)
-        commands.games.do_command(self, connection, event, user, channel)
+    def on_quit(self, connection, event):   # User quit irc server.
+        events.on_quit.process_event(self, connection, event)
 
+    def on_invite(self, connection, event): # Channel invite
+        events.on_invite.process_event(self, connection, event)
 
-    def on_pubnotice(self, connection, event):  # Channel notice
-        log.info(event)
+    def on_topic(self, connection, event):  # Channel topic change.
+        events.on_topic.process_event(self,connection, event)
 
-        user = queries.create_or_get_and_update_last_event(self, 'user', 'cn', channel_name=event.target, user_name=event.source.nick, event_content=event.arguments[0])
-        channel = queries.create_or_get_and_update_last_event(self, 'channel', 'cn', channel_name=event.target, user_name=event.source.nick, event_content=event.arguments[0])
+    def on_pubmsg(self, connection,  event):    # Channel message
+        events.on_pubmsg.process_event(self, connection, event)
 
+    def on_action(self, connection, event): # Is this both channel and private actions?
+        events.on_action.process_event(self, connection, event)
 
-    def on_privnotice(self,  connection,  event):   # Private notice.
-        log.info(event)
+    def on_privmsg(self, connection, event):    # Private message.
+        events.on_privmsg.process_event(self, connection, event)
 
-        user = queries.create_or_get_and_update_last_event(self, 'user', 'pn', user_name=event.source.nick, event_subject_name=event.target)
-        channel = None
-
-        commands.public.do_command(self, connection, event, user, channel)
-        commands.admin.do_command(self, connection, event, user, channel)
-        commands.statistics.do_command(self, connection, event, user, channel)
-        commands.games.do_command(self, connection, event, user, channel)
-
-        if event.source.nick == connection.get_nickname():  # Message came from myself.
-            return
-        elif event.source.nick == "NickServ":
-            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
-
-                # Nick \x02RotBot\x02 isn't registered.
-            if event.arguments[0].endswith(' is not a registered nickname.') or event.arguments[0].startswith('Nick ') and event.arguments[0].endswith(' isn\'t registered.') or event.arguments[0] == 'Your nick isn\'t registered.': # Username from database is not registered.
-                connection.privmsg('NickServ', 'register %s spamtBK@xs4all.nl' % (self.network.password)) # Register with NickServ.
-                connection.privmsg(self.network.home_channel, 'Regisring %s%s%s with %sNickServ%s.' % (font.red, self.network.nickname, font.reset, font.red, font.reset))  # Recover control of nickname via NickServ, old style.
-                log.info('Registerring with NickServ.')
-            if event.arguments[0].startswith('Nickname ') and event.arguments[0].endswith(' registered.'):
-                Inform.home_channel(seld, connection, 'Registerred nickname %s%s%s with NickServ.' % font.red, self.network.nickname, font.reset)
-        # 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_pubnotice(self, connection, event):  # Channel notice
+        events.on_pubnotice.process_event(self, connection, event)
 
+    def on_privnotice(self, connection, event):   # Private notice.
+        events.on_privnotice.process_event(self, connection, event)
 
-#
-#     def on_whoreply(self, connection, event):
-#         events.on_whoreply.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 " + font.green + event.arguments[0] + reset.reset + " from " + font.red + event.source)
+        events.on_keyset.process_event(self, connection, event)
 
     def on_yourhost(self, connection, event):
         log.info(event)
@@ -189,13 +112,13 @@ class PyRot(irc.bot.SingleServerIRCBot):
 
     # DCC stuff from originalexample file.
     def on_dccmsg(self, c, e):
-        log.info(e)
+        log.info(c, 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)
+        log.info(c, e)
         # if len(e.arguments) != 2:
         #     return
         # args = e.arguments[1].split()
@@ -225,7 +148,7 @@ def main():
     # Database credentials
     username = 'pyrot'
     password = 'oGPnbiqh55QKLhmnKQgS92h74j0e9d6LE58cSsD1'
-
+    ### End of SETTINGS
 
 
     db_connect_string = 'postgres://%s:%s@localhost/website' % (username, password)

+ 2 - 2
rotbot/commands/admin.py

@@ -1,7 +1,7 @@
 import string, re
 from common import userstatus, do_everything_to, log, font, queries
 from commands.common import CommandHelpers as CH
-from commands.common import AdminHelpers as AH
+#from commands.common import AdminHelpers as AH
 
 
 def do_command(self, connection, event, user, channel):
@@ -28,7 +28,7 @@ def do_command(self, connection, event, user, channel):
                 connection.mode(command.split()[1], "+ohv %s %s %s" % 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 %s %s %s" % event.source.nick, event.source.nick, event.source.nick)
+                connection.mode(channel, "+aohv %s %s %s %s" % event.source.nick, event.source.nick, event.source.nick, event.source.nick)
 
 
     # Do not even consider to run the latter admin commands for users who do not atleast have operator status in the concerning channel or halfop in the home_channel.

+ 89 - 0
rotbot/commands/chat.py

@@ -0,0 +1,89 @@
+from commands.common import CommandHelpers as CH
+from common import queries, font
+
+def do_command(self, connection, event, user, channel):
+    cmdtype, trigger, command, replyto = CH.disect_command(self, event)
+
+    # Do nothing if it's not a type of command.
+    if not cmdtype:
+        return
+
+    # Do nothing if there is no command.
+    if not command:
+        return
+
+    # The first word of the command sting with arguments, is just the command without arguments.
+    try:
+        one = command.split()[0]    # Get raw command.
+    except:
+        return
+
+    # Do noting if the chat channel setting is off and it's a channel message.
+    if event.target != connection.get_nickname():   # Command issued to channel.
+        if not queries.get_channel_setting_chat:   # Games are turned off in channel settigns.
+            return  # Do nothing.
+
+    if command == 'cmd' or command == 'cmds' or command == 'commands':
+        if cmdtype == 'cmd':
+            connection.privmsg(replyto, '%sChat: %s %s' % (font.grey, CH.ccc(self, "curse")[:-2]))
+
+    elif one == 'curse':
+        curse = queries.random_curse(self)
+        if command.split() == 1:    # Command without arguments
+            if cmdtype == 'help':
+                if curse:
+                    connection.privmsg(replyto, 'Add, lookup, or display a random %s curse word. The word argument is optional, to add a new word%s %s%s add%s %sword%s, or an adjective: %s%s%s adj' % (curse, font.blue, self.network.command_character, one, font.reset, font.italic, font.reset, font.blue, self.network.command_character, one))
+                else:
+                    connection.privmsg(replyto, 'Add, lookup, or display random curse word. The word argument is optional, to add a new word%s %s%s add%s %sword%s, or an adjective: %s%s%s add adj' % (font.blue, self.network.command_character, one, font.reset, font.italic, font.reset, font.blue, self.network.command_character, one))
+                connection.privmsg(replyto, '%sUsage:%s %s%s%s %sword' % (font.grey, font.blue, self.network.command_character, one, font.reset, font.italic))
+            else:   # Actual command without arguments.
+                curseword = queries.random_curseword(self)
+                connection.privmsg(replyto, curseword)
+        elif command.split() == 2:   # Command with one argument.
+            if cmdtype == 'help':
+                if swear:
+                    connection.privmsg(replyto, 'Look up a %s swear word: %s%s' % (swear, font.italic, command.split()[1]))
+                else:
+                    connection.privmsg(replyto, 'Look up swear word: %s%s' % (font.italic, command.split()[1]))
+            else:   # Actual command with one argument.
+                curseword = quiries.get_curseword(self, command.split()[1])
+                if curseword.banned:
+                    if curse:
+                        connection.privmsg(replyto, 'Sorry, %s this word is banned!' % curse)
+                    else:
+                        connection.privmsg(replyto, 'Sorry, this word is banned.')
+                elif curseword.irc_user:
+                    user_name = quiries.get_user_name(self, curseword.irc_user.id)
+                    connection.privmsg(replyto, 'Added %s by %s%s%s.' % (curseword.created, font.red, user_name, font.reset))
+                elif curseword.web_user:
+                    #user_name = quiries.get_user_name(self, curseword.irc_user.id)
+                    connection.privmsg(replyto, 'Added %s by %s%s%s.' % (curseword.created, font.red, 'a web user', font.reset))
+        elif command.split() == 3:  # Command with two arguments.
+            if command.split()[1] == 'add': # Add a curse word.
+                if cmdtype == 'help':
+                    connection.privmsg(replyto, 'Adds curse word %s to my vocabulary.' % (command.split()[2]))
+                else:   # Actual command.
+                    if command.split()[2] == 'adj':
+                        curse = queries.random_curse(self)
+                        if curse:
+                            connection.privmsg(replyto, '%sAdj%s is not a %s swearword!' % (font.italic, font.reset, curse))
+                        else:
+                            connection.privmsg(replyto, '%sAdj%s is not a swearword.' % (font.italic, font.reset))
+                        return
+                    queries.add_curseword(self, command.split()[2], user.id)
+                    if curse:
+                        connection.privmsg(replyto, '%s!' % curse)
+            elif command.split()[1] == 'adj':   # Add an adjective.
+                if cmdtype == 'help':
+                    connection.privmsg(replyto, 'Adds adjective %s to my vocabulary.' % (command.split()[2]))
+                else:   # Actual command.
+                    queries.add_adjective(self, command.split()[2], user.id)
+                    if curse:
+                        connection.privmsg(replyto, '%s!'curse)
+            else:   # Syntax error.
+                if curse:
+                    connection.privmsg(replyto, 'Invalid syntax, for %s help type: %s%s%s' % (curse, font.blue, self.network.command_character, one))
+                else:
+                    connection.privmsg(replyto, 'Invalid syntax, for help type: %s%s%s' % (font.blue, self.network.command_character, one))
+        elif command.split() >= 4:  # Too many arguments.
+            connection.privmsg(replyto, 'Too many arguments, for help type: %s%s%s' % (font.blue, self.network.command_character, one))

+ 122 - 147
rotbot/commands/common.py

@@ -1,5 +1,5 @@
 import random
-from common import font
+from common import font, queries
 
 
 class CommandHelpers():
@@ -53,62 +53,62 @@ class CommandHelpers():
         else:
             return font.blue + self.network.command_character + command + font.grey + ", "
 
-class AdminHelpers():
-    def get_channelfunctions(self, channel):
-        #channelfunctions = self.db.one("SELECT autojoin, aggressiveness, join_greeting, statistics_commands, games, chat FROM channels WHERE LOWER(name)=LOWER('" + channel + "') AND network='" + self.network + "'")
-        channelfunctions = self.db.one("SELECT autojoin FROM rotbot_channels WHERE LOWER(name)=LOWER('" + channel + "') AND network='" + self.network.id + "'")
-        if channelfunctions[0]:
-            autojoin = "on"
-        else:
-            autojoin = "off"
-        # if channelfunctions[2]:
-        #     joingreet = "on"
-        # else:
-        #     joingreet = "off"
-        # if channelfunctions[3]:
-        #     statscmds = "on"
-        # else:
-        #     statscmds = "off"
-        # if channelfunctions[4]:
-        #     games = "on"
-        # else:
-        #     games = "off"""
-        # if channelfunctions[5]:
-        #     chat = "on"
-        # else:
-        #     chat = "off"""
-        #return ("autojoin " + font.green + autojoin + font.reset + ", aggressiveness " + font.green + channelfunctions[1] + font.reset +  ", join_greeting " + font.green + joingreet + font.reset + ", statistics_commands " + font.green + statscmds + font.reset + ", games " + font.green + games + font.reset + ", chat " + font.green + chat + font.reset + ".")
-        return ('autojoin %s %s %s.' % font.green, autojoin, font.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."
-        elif function == "aggressiveness":
-            message = "How to respond to kick, ban and mode events. Options: " + font.blue + "passive" + font.reset + ", " + font.blue + "defense_only" + font.reset + ", " + font.blue + "equal_retalliation" + font.reset + ", " + font.blue + "battlebot" + font.reset + "."
-        elif function == "join_greeting":
-            message = "Greet users joining the channel on the first few joins, and later on large amounts."
-        elif function == "statistics_commands":
-            message = "Enable use of statistics commands."
-        elif function == "games":
-            message = "Enable use of game commands."
-        elif function == "chat":
-            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 AdminHelpers():
+#     def get_channelfunctions(self, channel):
+#         #channelfunctions = self.db.one("SELECT autojoin, aggressiveness, join_greeting, statistics_commands, games, chat FROM channels WHERE LOWER(name)=LOWER('" + channel + "') AND network='" + self.network + "'")
+#         channelfunctions = self.db.one("SELECT autojoin FROM rotbot_channels WHERE LOWER(name)=LOWER('" + channel + "') AND network='" + self.network.id + "'")
+#         if channelfunctions[0]:
+#             autojoin = "on"
+#         else:
+#             autojoin = "off"
+#         # if channelfunctions[2]:
+#         #     joingreet = "on"
+#         # else:
+#         #     joingreet = "off"
+#         # if channelfunctions[3]:
+#         #     statscmds = "on"
+#         # else:
+#         #     statscmds = "off"
+#         # if channelfunctions[4]:
+#         #     games = "on"
+#         # else:
+#         #     games = "off"""
+#         # if channelfunctions[5]:
+#         #     chat = "on"
+#         # else:
+#         #     chat = "off"""
+#         #return ("autojoin " + font.green + autojoin + font.reset + ", aggressiveness " + font.green + channelfunctions[1] + font.reset +  ", join_greeting " + font.green + joingreet + font.reset + ", statistics_commands " + font.green + statscmds + font.reset + ", games " + font.green + games + font.reset + ", chat " + font.green + chat + font.reset + ".")
+#         return ('autojoin %s %s %s.' % font.green, autojoin, font.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."
+#         elif function == "aggressiveness":
+#             message = "How to respond to kick, ban and mode events. Options: " + font.blue + "passive" + font.reset + ", " + font.blue + "defense_only" + font.reset + ", " + font.blue + "equal_retalliation" + font.reset + ", " + font.blue + "battlebot" + font.reset + "."
+#         elif function == "join_greeting":
+#             message = "Greet users joining the channel on the first few joins, and later on large amounts."
+#         elif function == "statistics_commands":
+#             message = "Enable use of statistics commands."
+#         elif function == "games":
+#             message = "Enable use of game commands."
+#         elif function == "chat":
+#             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():
@@ -119,73 +119,63 @@ class GameHelpers():
         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, ))
-        all_kicks = self.db.all("SELECT given, received FROM kicks WHERE LOWER(\"user\")=%s AND user_network='" + self.network + "'", (user, ))
-        all_messages = self.db.all("SELECT messages, messages_words, messages_characters, actions, actions_words, actions_characters, notices, notices_words, notices_characters FROM messages WHERE LOWER(\"user\")=%s AND user_network='" + self.network + "'", (user, ))
-        joins =0
-        for record in all_joins:
-            joins += record
-        given = 0
-        received = 0
-        for record in all_kicks:
-            given += int(record[0])
-            received += int(record[1])
-        messages = 0
-        messages_words = 0
-        messages_characters = 0
-        actions = 0
-        actions_words = 0
-        actions_characters = 0
-        notices = 0
-        notices_words = 0
-        notices_characters = 0
-        for record in all_messages:
-            messages += int(record[0])
-            messages_words += int(record[1])
-            messages_characters += int(record[2])
-            actions += int(record[3])
-            actions_words += int(record[4])
-            actions_characters += int(record[5])
-            notices += int(record[6])
-            notices_words += int(record[7])
-            notices_characters += int(record[8])
-        userrecord = self.db.one("SELECT xp_spent, level, coin, coin_given, coin_spent, ap_spent, karma_correction FROM users WHERE LOWER(name)=%s AND network='" + self.network + "'", (user, ))
-        xp_spent = userrecord[0]
-        level = userrecord[1]
-        coin = userrecord[2]
-        coin_given = userrecord[3]
-        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
-        total_words = messages_words + actions_words + notices_words
-        total_characters = messages_characters + actions_characters + notices_characters
-        if total_xp < 1:
-            total_xp = 1
-        joinkarma = (((messages / 19) - joins) / total_xp) /10
-        words_per_message = (total_words / 6) - total_messages
-        characters_per_message = (total_characters / 20) - total_messages
-        characters_per_word = (total_characters / 6) - total_words
-        chatkarma = ((words_per_message + characters_per_message + characters_per_message + characters_per_word) / total_xp) / 100
-        kickkarma = ((given * received) / total_xp) / 2
-        xpkarma = xp / 25
-        coinkarma = (coin - coin_given / (xp_spent + 1)) / 99
-        karma = float(joinkarma) + float(chatkarma) - float(kickkarma) + float(xpkarma) - float(coinkarma) + float(karma_correction)
-        if xp < 0:
-            xp = 0
+        total_joins = queries.get_user_total_joins(self, user)
+        total_kicks = queries.get_user_total_kicks(self, user)
+        total_kicked = queries.get_user_total_kicked(self, user)
+        total_messages = queries.get_user_total_messages(self, user)
+        total_actions = queries.get_user_total_actions(self, user)
+        total_notices = queries.get_user_total_notices(self, user)
+        total_cursewords_added = queries.get_user_total_curseword_added(self, user)
+        total_curseadjectives_added = queries.get_user_total_curseadjective_added(self, user)
+        gamestats = queries.get_user_gamestats(self, user)
+        xp_spent = gamestats[0]
+        level = gamestats[1]
+        coin = gamestats[2]
+        coin_given = gamestats[3]
+        coin_spent = gamestats[4]
+        ap_spent = gamestats[5]
+        karma_correction = gamestats[6]
+
+        # Experience & action points.
+        chat_xp = total_messages + (total_actions * 3) + (total_notices / 3)
+        print('chat_xp: ' + str(chat_xp) + ' = total_messages: ' + str(total_messages) + ' + (' + str(total_actions) + ' *3) + (' + str(total_notices) + ' / 3)')
+        curse_add_xp = (total_cursewords_added + total_curseadjectives_added) * 10
+        print('curse_add_xp: ' + str(curse_add_xp) + ' = (total_cursewords_added:' + str(total_cursewords_added) + ' + total_curseadjectives_added:' + str(total_curseadjectives_added) + ') * 10')
+        coin_xp = (coin_given * 5) + coin_spent
+        print('coin_xp: ' + str(coin_xp) + ' = (coin_given:' + str(coin_given) + ' * 5) + coin_spent:' + str(coin_spent))
+        total_xp = chat_xp + curse_add_xp + coin_xp + total_joins + total_kicks + total_kicked + ap_spent
+        print('total_xp: ' + str(total_xp) + ' = chat_xp: ' + str(chat_xp) + ' + curse_add_xp: ' + str(curse_add_xp) + ' + coin_xp: ' + str(coin_xp) + ' + total_joins: ' + str(total_joins) + ' + total_kicks: ' + str(total_kicks) + ' + total_kicked: ' + str(total_kicked) + ' + ap_spent : ' + str(ap_spent))
+        xp = (total_xp - xp_spent - (level * 2))
+        print('xp :' + str(xp) + ' = (total_xp: ' + str(total_xp) + ' - xp_spent: ' + str(xp_spent) + ' - (level: ' + str(level) + ' * 100))')
+        ap = (total_xp / 100) - ap_spent
+        print('ap: + ' + str(ap) + ' = (total_xp: ' + str(total_xp) + ' / 100) - ap_spent: ' + str(ap_spent))
+
+        # Karma.
+        if total_xp == 0:
+            total_xp = 0.01
+        join_karma = total_joins / total_xp   # High value should have negative impact.
+        print('join_karma: ' + str(join_karma) + ' = joins: ' + str(total_joins) + ' / total_xp: ' + str(total_xp))
+        kick_karma = (total_kicks + total_kicked) / total_xp  # High value should have negative impact.
+        print('kick_karma: ' + str(kick_karma) + ' = (given: ' + str(total_kicks) + ' + received: ' + str(total_kicked) + ') / total_xp:' + str(total_xp))
+        shout_karma =  total_notices / total_xp # High value should have negative impact.
+        print('shout_karma: ' + str(shout_karma) + ' =  total_notices: ' + str(total_notices) + ' + / total_xp: ' + str(total_xp))
+        curse_add_karma = ((total_cursewords_added + total_curseadjectives_added) * 10 ) / total_xp  # High value should have positive impact.
+        print('curse_add_karma: ' + str(curse_add_karma) + ' = ((total_cursewords_added: ' + str(total_cursewords_added) + ' + total_curseadjectives_added: ' + str(total_curseadjectives_added) + ') * 10 ) / total_xp: '+ str(total_xp))
+        wealth_karma = (coin * 50) / total_xp
+        print('wealth_karma: ' + str(wealth_karma) + ' = (coin: ' + str(coin) + ' * 50) / total_xp: ' + str(total_xp))
+        charity_karma = (coin_given * 10) / total_xp
+        print('charity_karma: ' + str(charity_karma) + ' = (coin_given: ' + str(coin_given) + ' * 10) / total_xp: ' + str(total_xp))
+        karma =  float(karma_correction) + float(curse_add_karma) + float(charity_karma) - float(join_karma) - float(kick_karma) - float(shout_karma) - float(wealth_karma)
+        print('karma: ' + str(karma) + ' =  float(karma_correction: ' + str(karma_correction) + ') + float(curse_karma: ' + str(curse_add_karma) + ') + float(charity_karma: ' + str(charity_karma) + ') - float(join_karma: ' + str(join_karma) + ') - float(kick_karma: ' + str(kick_karma) + ') - float(shout_karma: ' + str(shout_karma) + ') - float(wealth_karma: ' + str(wealth_karma) + ')')
 
         return level, xp, xp_spent, total_xp, karma, coin, coin_spent, coin_given, ap, ap_spent
 
+    def player_info(self, user):
+        level, xp, xp_spent, total_xp, karma, coin, coin_spent, coin_given, ap, ap_spent = GameHelpers.get_info(self, user)
+        return 'Level: ' + str(level) + font.grey + ", " + font.reset + "XP: " + str(int(xp)) + "/" + font.grey + str(round(total_xp, 4)) + ", " + font.reset + "AP: " + str(int(ap)) + font.grey + ", " + font.reset + "coin: " + str(coin) + font.grey + "[S " + str(coin_spent) + ", G " + str(coin_given) + "], " + font.reset + "karma: " + str(round(karma, 4))
+
     def list_top_players(self, sort):
-        result = self.db.all("SELECT name, level, xp_spent, coin FROM users WHERE network=%s ORDER BY " + sort + " DESC LIMIT 3 ", (self.network, ))
+        result = queries.get_top_users(self, sort)
         if sort == "level":
             column = 1
             threshold = 0
@@ -197,31 +187,16 @@ class GameHelpers():
             threshold = 10
         message = ""
         for record in result:
-            if not record[column] <= threshold:
-                level, xp, xpspent, totalxp, karma, coin, coinspent, coingiven, ap, apspent = GameHelpers.get_info(self, record[0])
-                if ap < 0:
-                    ap = 0
-                if sort == "level":
-                    message += red + str(record[0]) + font.reset + " [" + font.blue + "L " + font.green + str(level) + font.reset + ", X " + font.grey + str(xpspent) + "/" + font.green + str(int(xp)) + font.reset + ", A " + str(int(ap)) + ", C " + font.green + str(coin) + font.reset + ", K " + font.green + str(round(karma, 2)) + font.reset + "], "
-                if sort == "xp_spent":
-                    message += red + str(record[0]) + font.reset + " [L " + font.green + str(level) + font.reset + ", " + font.blue + "X " + font.grey + str(xpspent) + "/" + font.green + str(int(xp)) + font.reset + ", A " + str(int(ap)) + ", C " + font.green + str(coin) + font.reset + ", K " + font.green + str(round(karma, 2)) + font.reset + "], "
-                if sort == "coin":
-                    message += red + str(record[0]) + font.reset + " [L " + font.green + str(level) + font.reset + ", X " + font.grey + str(xpspent) + "/" + font.green + str(int(xp)) + font.reset + ", " + font.reset + "A " + str(int(ap)) + ", " + font.blue + "C " + font.green + str(coin) + font.reset + ", K " + font.green + str(round(karma, 2)) + font.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:
-            ap = 0
-        return "Level: " + str(level) + font.grey + ", " + font.reset + "XP: " + str(int(xp)) + "/" + font.grey + str(round(totalxp, 4)) + ", " + font.reset + "AP: " + str(int(ap)) + font.grey + ", " + font.reset + "coin: " + str(coin) + font.grey + "[S " + str(coinspent) + ", G " + str(coingiven) + "], " + font.reset + "karma: " + str(round(karma, 4))
-
-class StatisticsHelpers():
-    def add_message_stats(stats):
-        messages = 0
-        words = 0
-        characters = 0
-        for record in stats:
-            messages += record[0]
-            words += record[1]
-            characters += record[2]
-        return messages, words, characters
+            #if not record[column] <= threshold:
+
+            level, xp, xp_spent, total_xp, karma, coin, coin_spent, coin_given, ap, ap_spent = GameHelpers.get_info(self, record)
+            if ap < 0:
+                ap = 0
+            if sort == "level":
+                message += font.red + str(record[0]) + font.reset + " [" + font.blue + "L " + font.green + str(level) + font.reset + ", X " + font.grey + str(xp_spent) + "/" + font.green + str(int(xp)) + font.reset + ", A " + str(int(ap)) + ", C " + font.green + str(coin) + font.reset + ", K " + font.green + str(round(karma, 2)) + font.reset + "], "
+            if sort == "xp_spent":
+                message += font.red + str(record[0]) + font.reset + " [L " + font.green + str(level) + font.reset + ", " + font.blue + "X " + font.grey + str(xp_spent) + "/" + font.green + str(int(xp)) + font.reset + ", A " + str(int(ap)) + ", C " + font.green + str(coin) + font.reset + ", K " + font.green + str(round(karma, 2)) + font.reset + "], "
+            if sort == "coin":
+                message += font.red + str(record[0]) + font.reset + " [L " + font.green + str(level) + font.reset + ", X " + font.grey + str(xp_spent) + "/" + font.green + str(int(xp)) + font.reset + ", " + font.reset + "A " + str(int(ap)) + ", " + font.blue + "C " + font.green + str(coin) + font.reset + ", K " + font.green + str(round(karma, 2)) + font.reset + "], "
+        return message[:-2]

+ 165 - 189
rotbot/commands/games.py

@@ -27,8 +27,171 @@ def do_command(self, connection, event, user, channel):
 
     if command == 'cmd' or command == 'cmds' or command == 'commands':
         if cmdtype == 'cmd':
-            connection.privmsg(replyto, '%sGames: %s %s' % (font.grey, CH.ccc(self, "8ball"), CH.ccc(self, "dice")[:-2]))#, CH.ccc(self, "player"), CH.ccc(self, "players"), CH.ccc(self, "levelup") ,CH.ccc(self, "givecoin")[:-2]))
-            #connection.privmsg(replyto, font.grey + "Game help: " + font.blue + self.network.help_character + "level" + font.grey + ", " + font.blue + self.network.help_character + "xp" + font.grey + ", " + font.blue + self.network.help_character + "ap" + font.grey + ", " + font.blue + self.network.help_character + "coin" + font.grey + ", " + font.blue + self.network.help_character + "karma" + font.grey + ".")
+            connection.privmsg(replyto, '%sGames: %s %s %s %s %s %s' % (font.grey, CH.ccc(self, "player"), CH.ccc(self, "players"), CH.ccc(self, "levelup") ,CH.ccc(self, "givecoin"), CH.ccc(self, "8ball"), CH.ccc(self, "dice")[:-2]))
+            connection.privmsg(replyto, font.grey + "Game help: " + font.blue + self.network.help_character + "level" + font.grey + ", " + font.blue + self.network.help_character + "xp" + font.grey + ", " + font.blue + self.network.help_character + "ap" + font.grey + ", " + font.blue + self.network.help_character + "coin" + font.grey + ", " + font.blue + self.network.help_character + "karma" + font.grey + ".")
+
+
+    elif one == 'player':
+        if len(command.split()) == 1:
+            if cmdtype == 'help':   # Display help text.
+                connection.privmsg(replyto, 'Displays a users game statistics. User optional.')
+                connection.privmsg(replyto, 'Usage: %s%s%s%s %snickname' % (font.blue, self.network.command_character, one, font.reset, font.italic))
+                return
+            message = '%s Your player stats: ' % font.grey
+            player_name = event.source.nick
+            player = queries.get_user(self, player_name)
+        elif len(command.split()) == 2:
+            player_name = command.split()[1]
+            if cmdtype == 'help':
+                connection.privmsg(replyto, 'Displays game stats for player: %s%s' % (font.red, player_name))
+                return
+            player = queries.get_user(self, player_name)
+            if not player:
+                connection.action(replyto, 'does not know of a %s%s%s.' % (font.red, trigger.split()[1], font.reset))
+                return
+            if player_name.lower() == connection.get_nickname().lower():
+                connection.privmsg(replyto, "The game does not play the master.")
+                return
+            message = '%sPlayer stats for %s%s%s: ' % (font.grey, font.red, player.name, font.reset)
+        else:   # Too many arguments.
+            connection.privmsg(replyto, 'Too many arguments, For help type: %s%s%s' % (font.blue, self.network.help_character, one))
+            return
+
+        info = GameHelpers.player_info(self, player)
+        connection.privmsg(replyto, message + info)
+
+    elif one == 'levelup':
+        level, xp, xp_spent, total_xp, karma, coin, coin_spent, coin_given, ap, ap_spent = GameHelpers.get_info(self, user)
+        if cmdtype == 'help':    #Display help text.
+            connection.privmsg(replyto, 'Spend %s XP to gain your next level. Levelup multiple levels by adding an amount.' %  str(int(level * 2.7)))
+            connection.privmsg(replyto, 'Example: %s%s%s 3' % (font.blue, self.network.command_character, one))
+        elif cmdtype == "cmd":
+            upgrade_count = 1
+            if xp < int(level * 2.7):
+                connection.privmsg(replyto, "Insufficient XP, you need at least " + str(int(level * 2.7)) + ".")
+                return
+            elif ap <= 0:
+                connection.privmsg(replyto, "Insufficient AP, you need at least 1.")
+                return
+            elif len(command.split()) == 2:   # Command with one argument.
+                try:
+                    levels = int(command.split()[1])
+                except:
+                    connection.privmsg(replyto, "Invalid amount.")
+                    return
+                if levels < 1:
+                    connection.privmsg(replyto, "Invalid amount.")
+                    return
+                upgrade_count = levels  # Valid amount.
+            elif len(command.split()) >= 3:
+                connection.privmsg(replyto, "Too many arguments. For help type " + font.blue + self.network.help_character + "levelup" + font.reset + ".")
+                return
+
+            while xp > int(level * 2.7) and ap > 0 and upgrade_count > 0:
+                queries.levelup_user(self, user, int(level * 2.7))
+                level, xp, xp_spent, total_xp, karma, coin, coin_spent, coin_given, ap, ap_spent = GameHelpers.get_info(self, user)
+
+            if upgrade_count < 0:
+                connection.privmsg(replyto, 'Exhausted XP or AP before levelup was complete. You are only partially levelled up.')
+            info = GameHelpers.player_info(self, user)
+            connection.privmsg(replyto, "Your new statistics: " + info)
+
+    elif command.split()[0] == "givecoin":
+        if cmdtype == "help":    #Display help text.
+            connection.privmsg(replyto, "Give coins to another player. Amount optional.")
+            connection.privmsg(replyto, font.grey + "Usage: " + font.blue + self.network.command_character + "givecoin " + font.reset + font.italic + "user amount")
+        elif cmdtype == "cmd":
+
+            if len(command.split()) == 1:
+                connection.privmsg(replyto, "Insufficient arguments. For help type " + font.blue + self.network.help_character + "givecoin" + font.reset + ".")
+                return
+            elif len(command.split()) > 3:
+                connection.privmsg(replyto, "Too many arguments. For help type " + font.blue + self.network.help_character + "givecoin" + font.reset + ".")
+                return
+            elif command.split()[1] == event.source.nick.lower():
+                connection.privmsg(replyto, "You already have your own coin. For help type " + font.blue + self.network.help_character + "givecoin" + font.reset + ".")
+                return
+            elif len(command.split()) == 3:
+                stop = False
+                try:
+                    if float(command.split()[2]) < 0:
+                        connection.privmsg(replyto, "You clever abuser! The " + font.red + self.network.command_character + font.bold + "give" + font.bold + "coin" + font.reset + " command is not designed for robbing. There will be consequences...")
+                        self.db.run("UPDATE users SET coin=coin-3, karma_correction=karma_correction-0.5 WHERE name=%s AND network=%s", (event.source.nick, self.network))
+                        stop = True
+                except TypeError:
+                    connection.privmsg(replyto, "Invalid amount. For help type " + font.blue + self.network.help_character + "givecoin" + font.reset + ".")
+                    return
+                if stop:
+                    return
+            elif not event.target == connection.get_nickname():
+                if not command.split()[1] in self.channels[event.target].users():
+                    connection.privmsg(replyto, font.red + trigger.split()[1] + font.reset + " is not present.")
+                    return
+
+            level, xp, xpspent, totalxp, karma, coin, coinspent, coingiven, ap, apspent = GameHelpers.get_info(self, event.source.nick)
+            receivingrecord = self.db.one("SELECT level, away FROM users WHERE LOWER(name)=%s AND network=%s", (command.split()[1], self.network, ))
+            if level < 1:
+                connection.privmsg(replyto, "You need to " + font.blue + self.network.command_character + "levelup " + font.reset + "to be able to give coin.")
+                return
+            elif coin < 1:
+                connection.privmsg(replyto, "You have no coin to give.")
+                return
+            elif not receivingrecord:
+                connection.action(replyto, "does not know of any \"" + font.red + trigger.split()[1] + font.reset + "\".")
+                return
+            elif receivingrecord[0] == 0:
+                connection.privmsg(replyto, font.red + trigger.split()[1] + font.reset + " is not playing the game.")
+                return
+            elif receivingrecord[1] == True:
+                connection.privmsg(replyto, font.red + trigger.split()[1] + font.reset + "is not here right now.")
+                return
+            elif ap < 1:
+                connection.privmsg(replyto, "You have no action points, go use IRC some more...")
+                return
+            if len(command.split()) == 2:
+                self.db.run("UPDATE users SET coin=coin-1, coin_spent=coin_spent+1, coin_given=coin_given+1, ap_spent=ap_spent+1 WHERE name=%s AND network=%s", (event.source.nick, self.network, ))
+                self.db.run("UPDATE users SET coin=coin+1 WHERE LOWER(name)=%s AND network=%s", (command.split()[1], self.network, ))
+            elif len(command.split()) == 3:
+                self.db.run("UPDATE users SET coin=coin-%s, coin_spent=coin_spent+%s, coin_given=coin_given+%s, ap_spent=ap_spent+1 WHERE name=%s AND network=%s", (command.split()[2], command.split()[2], command.split()[2], event.source.nick, self.network, ))
+                self.db.run("UPDATE users SET coin=coin+%s WHERE LOWER(name)=%s AND network=%s", (command.split()[2], command.split()[1], self.network))
+            info = GameHelpers.player_info(self, event.source.nick)
+            connection.notice(event.source.nick, "Your new statistics: " + info)
+
+    elif command.split()[0] == "players":
+        if cmdtype == "help":    #Display help text.
+            connection.privmsg(replyto, "Display top player rankings.")
+        elif cmdtype == "cmd":
+
+            toplevel = GameHelpers.list_top_players(self, "level")
+            if toplevel:
+                connection.notice(event.source.nick, "Ranking level: " + toplevel)
+            topxp = GameHelpers.list_top_players(self, "xp_spent")
+            if topxp:
+                connection.notice(event.source.nick, "Ranking experience: " + topxp)
+            topcoin = GameHelpers.list_top_players(self, "coin")
+            if topcoin:
+                connection.notice(event.source.nick, "Ranking wealth: " + topcoin)
+            if not toplevel and not topxp and not topcoin:
+                connection.privmsg(replyto, "Nobody is playing the game.")
+
+
+    elif command.split()[0] == "level":
+        if cmdtype == "help":    #Display help text.
+            connection.privmsg(replyto, "For 10 XP you can use !levelup to gain a level. As your level increases, things become more difficult, while more capabilities and options are unlocked.")
+    elif command.split()[0] == "xp":
+        if cmdtype == "help":    #Display help text.
+            connection.privmsg(replyto, "XP is earned by using IRC and playing the game, in channels with " + font.red + connection.get_nickname() + font.reset + ". Ask any operator in " + font.red + self.network.home_channel + font.reset + " to add a channel. XP is used to level up, advance classes, as a limit and as a multiplier. Once XP is expended it keeps counting towards your total.")
+    elif command.split()[0] == "ap":
+        if cmdtype == "help":    #Display help text.
+            connection.privmsg(replyto, "AP is earned by using IRC and playing the game in channels with " + font.red + connection.get_nickname() + font.reset + ". Ask any operator in " + font.red + self.network.home_channel + font.reset + " to add a channel. AP is expended for every action you take in the game.")
+    elif command.split()[0] == "coin":
+        if cmdtype == "help":    #Display help text.
+            connection.privmsg(replyto, "Coin is earned when certain events occur. Coin is used to buy items and classes. To give a player coin use " + font.blue + self.network.command_character + "givecoin" + font.reset + ". Coin affects karma.")
+    elif command.split()[0] == "karma":
+        if cmdtype == "help":    #Display help text.
+            connection.privmsg(replyto, "Karma is a secret formula, based upon on your IRC events and how you play the game. It does not increase like XP. Some events, skills and items are karma based.")
+
+
 
     elif command.split()[0] == "8ball":
         if cmdtype == "help":    #Display help text.
@@ -134,190 +297,3 @@ def do_command(self, connection, event, user, channel):
                     connection.privmsg(replyto, ", ".join(str(rolls) for rolls in GameHelpers.roll_dice(diceamount,  dicetype)) + ".") # Roll x amount of x type dice.
             else:   # Invalid amount of arguments.
                 connection.privmsg(replyto, "Too many arguments. For help type: " + font.blue + self.network.help_character + "dice" + font.reset + ".")
-
-    # elif command.split()[0] == "player":
-    #     if cmdtype == "help":    #Display help text.
-    #         connection.privmsg(replyto, "Displays a users game statistics. User optional.")
-    #     elif cmdtype == "cmd":
-    #
-    #         if len(command.split()) == 1:
-    #             user = event.source.nick.lower()
-    #             message = font.grey + "Your statistics. " + reset
-    #         elif len(command.split()) == 2:
-    #             user = command.split()[1]
-    #             if not self.db.one("SELECT id FROM users WHERE LOWER(name)=%s AND network='" + self.network + "'", (user, )):
-    #                 connection.action(replyto, "does not know of a " + font.red + trigger.split()[1] + font.reset + ".")
-    #                 return
-    #             if user == connection.get_nickname().lower():
-    #                 connection.privmsg(replyto, "The game does not play the master.")
-    #                 return
-    #             if user == event.source.nick.lower():
-    #                 message = font.grey + "Your statistics. " + reset
-    #             else:
-    #                 message = font.grey + "Statistics for " + font.red + trigger.split()[1] + font.reset + ". "
-    #         else:
-    #             connection.privmsg(replyto, "Too many arguments, For help type " + font.blue + self.network.help_character + "players " + font.reset + ".")
-    #             return
-    #
-    #         level, xp, xpspent, totalxp, karma, coin, coinspent, coingiven, ap, apspent = GameHelpers.get_info(self, user)
-    #         if ap < 0:
-    #             ap = 0
-    #         info = GameHelpers.player_info(self, user)
-    #         connection.privmsg(replyto, message + info)
-    #
-    # elif command.split()[0] == "levelup":
-    #     user = event.source.nick.lower()
-    #     level, xp, xpspent, totalxp, karma, coin, coinspent, coingiven, ap, apspent = GameHelpers.get_info(self, user)
-    #     if cmdtype == "help":    #Display help text.
-    #         connection.privmsg(replyto, "Spend " + str(int(level * 2.7)) + " XP to gain your next level. Gain multiple levels (for the price of the lowest level) by specifying the amount.")
-    #     elif cmdtype == "cmd":
-    #         if len(command.split()) == 1:
-    #             if xp < int(level * 2.7):
-    #                 connection.privmsg(replyto, "Insufficient XP, you need at least " + str(int(level * 2.7)) + ".")
-    #             elif ap < 1:
-    #                 connection.privmsg(replyto, "Insufficient AP, you need at least 1.")
-    #             else:
-    #                 self.db.run("UPDATE users SET level=level+1, xp_spent=xp_spent+%s, ap_spent=ap_spent+1 WHERE LOWER(name)=%s AND network=%s", (int(level * 2.7), user, self.network, ))
-    #                 self.db.run("UPDATE users SET coin=coin+0.3 WHERE level>0 AND network=%s", (self.network, ))
-    #                 info = GameHelpers.player_info(self, user)
-    #                 connection.privmsg(replyto, "Your new statistics: " + info)
-    #         elif len(command.split()) == 2:
-    #             try:
-    #                 levels = int(command.split()[1])
-    #             except:
-    #                 connection.privmsg(replyto, "Invalid amount.")
-    #                 return
-    #             if levels < 1:
-    #                 connection.privmsg(replyto, "Invalid amount.")
-    #             elif xp < int(level * 2.7):
-    #                 connection.privmsg(replyto, "Insufficient XP, you need at least " + str(int(level * 2.7)) + ".")
-    #             elif ap < levels:
-    #                 connection.privmsg(replyto, "Insufficient AP, you need at least 1.")
-    #             else:
-    #                 self.db.run("UPDATE users SET level=level+%s, xp_spent=xp_spent+%s, ap_spent=ap_spent+%s WHERE LOWER(name)=LOWER(%s) AND network=%s", (levels, int(level * 2.7), levels, user, self.network, ))
-    #                 self.db.run("UPDATE users SET coin=coin+%s WHERE level>0 AND network=%s", (levels * 0.3, self.network, ))
-    #                 info = GameHelpers.player_info(self, user)
-    #                 connection.privmsg(replyto, "Your new statistics: " + info)
-    #         else:
-    #             connection.privmsg(replyto, "Too many arguments. For help type " + font.blue + self.network.help_character + "levelup " + font.reset + ".")
-    #
-    # elif command.split()[0] == "givecoin":
-    #     if cmdtype == "help":    #Display help text.
-    #         connection.privmsg(replyto, "Give coins to another player. Amount optional.")
-    #         connection.privmsg(replyto, font.grey + "Usage: " + font.blue + self.network.command_character + "givecoin " + font.reset + font.italic + "user amount")
-    #     elif cmdtype == "cmd":
-    #
-    #         if len(command.split()) == 1:
-    #             connection.privmsg(replyto, "Insufficient arguments. For help type " + font.blue + self.network.help_character + "givecoin" + font.reset + ".")
-    #             return
-    #         elif len(command.split()) > 3:
-    #             connection.privmsg(replyto, "Too many arguments. For help type " + font.blue + self.network.help_character + "givecoin" + font.reset + ".")
-    #             return
-    #         elif command.split()[1] == event.source.nick.lower():
-    #             connection.privmsg(replyto, "You already have your own coin. For help type " + font.blue + self.network.help_character + "givecoin" + font.reset + ".")
-    #             return
-    #         elif len(command.split()) == 3:
-    #             stop = False
-    #             try:
-    #                 if float(command.split()[2]) < 0:
-    #                     connection.privmsg(replyto, "You clever abuser! The " + font.red + self.network.command_character + font.bold + "give" + font.bold + "coin" + font.reset + " command is not designed for robbing. There will be consequences...")
-    #                     self.db.run("UPDATE users SET coin=coin-3, karma_correction=karma_correction-0.5 WHERE name=%s AND network=%s", (event.source.nick, self.network))
-    #                     stop = True
-    #             except TypeError:
-    #                 connection.privmsg(replyto, "Invalid amount. For help type " + font.blue + self.network.help_character + "givecoin" + font.reset + ".")
-    #                 return
-    #             if stop:
-    #                 return
-    #         elif not event.target == connection.get_nickname():
-    #             if not command.split()[1] in self.channels[event.target].users():
-    #                 connection.privmsg(replyto, font.red + trigger.split()[1] + font.reset + " is not present.")
-    #                 return
-    #
-    #         level, xp, xpspent, totalxp, karma, coin, coinspent, coingiven, ap, apspent = GameHelpers.get_info(self, event.source.nick)
-    #         receivingrecord = self.db.one("SELECT level, away FROM users WHERE LOWER(name)=%s AND network=%s", (command.split()[1], self.network, ))
-    #         if level < 1:
-    #             connection.privmsg(replyto, "You need to " + font.blue + self.network.command_character + "levelup " + font.reset + "to be able to give coin.")
-    #             return
-    #         elif coin < 1:
-    #             connection.privmsg(replyto, "You have no coin to give.")
-    #             return
-    #         elif not receivingrecord:
-    #             connection.action(replyto, "does not know of any \"" + font.red + trigger.split()[1] + font.reset + "\".")
-    #             return
-    #         elif receivingrecord[0] == 0:
-    #             connection.privmsg(replyto, font.red + trigger.split()[1] + font.reset + " is not playing the game.")
-    #             return
-    #         elif receivingrecord[1] == True:
-    #             connection.privmsg(replyto, font.red + trigger.split()[1] + font.reset + "is not here right now.")
-    #             return
-    #         elif ap < 1:
-    #             connection.privmsg(replyto, "You have no action points, go use IRC some more...")
-    #             return
-    #         if len(command.split()) == 2:
-    #             self.db.run("UPDATE users SET coin=coin-1, coin_spent=coin_spent+1, coin_given=coin_given+1, ap_spent=ap_spent+1 WHERE name=%s AND network=%s", (event.source.nick, self.network, ))
-    #             self.db.run("UPDATE users SET coin=coin+1 WHERE LOWER(name)=%s AND network=%s", (command.split()[1], self.network, ))
-    #         elif len(command.split()) == 3:
-    #             self.db.run("UPDATE users SET coin=coin-%s, coin_spent=coin_spent+%s, coin_given=coin_given+%s, ap_spent=ap_spent+1 WHERE name=%s AND network=%s", (command.split()[2], command.split()[2], command.split()[2], event.source.nick, self.network, ))
-    #             self.db.run("UPDATE users SET coin=coin+%s WHERE LOWER(name)=%s AND network=%s", (command.split()[2], command.split()[1], self.network))
-    #         info = GameHelpers.player_info(self, event.source.nick)
-    #         connection.notice(event.source.nick, "Your new statistics: " + info)
-    #
-    # elif command.split()[0] == "players":
-    #     if cmdtype == "help":    #Display help text.
-    #         connection.privmsg(replyto, "Display top player rankings.")
-    #     elif cmdtype == "cmd":
-    #
-    #         toplevel = GameHelpers.list_top_players(self, "level")
-    #         if toplevel:
-    #             connection.notice(event.source.nick, "Ranking level: " + toplevel)
-    #         topxp = GameHelpers.list_top_players(self, "xp_spent")
-    #         if topxp:
-    #             connection.notice(event.source.nick, "Ranking experience: " + topxp)
-    #         topcoin = GameHelpers.list_top_players(self, "coin")
-    #         if topcoin:
-    #             connection.notice(event.source.nick, "Ranking wealth: " + topcoin)
-    #         if not toplevel and not topxp and not topcoin:
-    #             connection.privmsg(replyto, "Nobody is playing the game.")
-    #
-    # elif command.split()[0] == "level":
-    #     if cmdtype == "help":    #Display help text.
-    #         connection.privmsg(replyto, "For 10 XP you can use !levelup to gain a level. As your level increases, things become more difficult, while more capabilities and options are unlocked.")
-    # elif command.split()[0] == "xp":
-    #     if cmdtype == "help":    #Display help text.
-    #         connection.privmsg(replyto, "XP is earned by using IRC and playing the game, in channels with " + font.red + connection.get_nickname() + font.reset + ". Ask any operator in " + font.red + self.homechannel + font.reset + " to add a channel. XP is used to level up, advance classes, as a limit and as a multiplier. Once XP is expended it keeps counting towards your total.")
-    # elif command.split()[0] == "ap":
-    #     if cmdtype == "help":    #Display help text.
-    #         connection.privmsg(replyto, "AP is earned by using IRC and playing the game in channels with " + font.red + connection.get_nickname() + font.reset + ". Ask any operator in " + font.red + self.homechannel + font.reset + " to add a channel. AP is expended for every action you take in the game.")
-    # elif command.split()[0] == "coin":
-    #     if cmdtype == "help":    #Display help text.
-    #         connection.privmsg(replyto, "Coin is earned when certain events occur, for instance when a player expends XP. Coin is used to buy items and classes. To give a player coin use " + font.blue + self.network.command_character + "givecoin" + font.reset + ". Coin affects karma.")
-    # elif command.split()[0] == "karma":
-    #     if cmdtype == "help":    #Display help text.
-    #         connection.privmsg(replyto, "Karma is a secret formula, based upon on your IRC events and how you play the game. It does not increase like XP. Some events, skills and items are karma based.")
-
-#    elif command.split()[0] == "classup":
-#        if cmdtype == "help":    #Display help text.
-#            connection.privmsg(replyto, "Spend 10 XP to gain a class in your current level. List available classes with " + font.blue + self.network.help_character + "classup available " + font.reset + ".")
-#            connection.privmsg(replyto, font.grey + "Usage: " + font.blue + self.network.command_character + "classup " + font.reset + font.italic + "class")
-#        elif cmdtype == "cmd":
-#
-#            if len(command.split()) == 1:
-#                connection.privmsg(replyto, "Insufficient arguments. For help type " + font.blue + self.network.help_character + "classup" + font.reset + ".")
-#            if len(command.split()) == 2:
-#                if command.split()[1] == "available":
-#                    level, xp, xpspent, karma = GameHelpers.get_info(self, event.source.nick)
-#                    if level == 0:
-#                        connection.privmsg(replyto, "There are no level 0 classes.")
-#                    if level == 1:
-#                        connection.privmsg(replyto, "Level 1 classes: Maggot, nubcake, ")
-#                    if level == 2:
-#                        connection.privmsg(replyto, "Level 2 classes: Retard, ")
-#                    if level == 3:
-#                        connection.privmsg(replyto, "Level 3 classes: ")
-#                    if level == 4:
-#                        connection.privmsg(replyto, "Level 3 classes: ")
-#                    if level == 5:
-#                        connection.privmsg(replyto, "Level 4 classes: ")
-#                else:
-#            else:
-#                connection.privmsg(replyto, "Too many arguments. For help type " + font.blue + self.network.help_character + "classup" + font.reset + ".")

+ 1 - 1
rotbot/commands/statistics.py

@@ -1,6 +1,6 @@
 from datetime import datetime
 from common import font, queries
-from commands.common import CommandHelpers as CH, StatisticsHelpers
+from commands.common import CommandHelpers as CH
 
 
 def do_command(self, connection, event, user, channel):

+ 137 - 31
rotbot/common/queries.py

@@ -3,24 +3,6 @@ from slugify import slugify
 from common import log
 
 def create_or_get_and_update_last_event(self, table, event_type, channel_name=None, user_name=None, event_content=None, event_subject_name=None):
-    # EVENT_TYPE_CHOICES = [
-    #     ('pm', 'private message'),
-    #     ('pa', 'private action'),
-    #     ('pn', 'private notice'),
-    #     ('cm', 'channel message')
-    #     ('ca', 'channel action'),
-    #     ('cn', 'channel notice'),
-    #     ('ct', 'channel topic'),
-    #     ('ck', 'channel password'),
-    #     ('ci', 'channel invite'),
-    #     ('cj', 'channel join'),
-    #     ('cp', 'channel part'),
-    #     ('ck', 'channel kick'),
-    #     ('kd', 'channel kicked'),
-    #     ('mc', 'mode change'),
-    #     ('nc', 'nick change'),
-    #     ('sq', 'quit'),
-    # ]
     if table == 'channel':
         name = channel_name
     if table == 'user':
@@ -43,7 +25,15 @@ def create_or_get_and_update_last_event(self, table, event_type, channel_name=No
         if 'event_subject' in locals():
             event_subject_id = get_user_id(self, user_name)
             fields +- ', last_event_subject_id=%(last_event_subject_id)s'
-        self.db.run('UPDATE rotbot_' + table + ' SET ' + fields + ' WHERE LOWER(name)=LOWER(%(name)s) AND network_id=%(network_id)s', name=name, network_id=self.network.id, last_event_type=event_type, last_event_channel_id=event_channel_id, last_event_user_id=event_user_id, last_event_content=event_content, last_event_subject_id=event_subject_id) # Correct capitalisation and update last event.
+        self.db.run(    # Correct capitalisation and update last event.
+            'UPDATE rotbot_' + table + ' SET ' + fields + ' WHERE LOWER(name)=LOWER(%(name)s) AND network_id=%(network_id)s',
+            name=name,
+            network_id=self.network.id,
+            last_event_type=event_type,
+            last_event_channel_id=event_channel_id,
+            last_event_user_id=event_user_id,
+            last_event_content=event_content,
+            last_event_subject_id=event_subject_id)
 
     else:   # Not on record.
         new_slug = slugify('%s-%s' % (name, self.network.name), max_length=50) # Create a slug from the name.
@@ -81,21 +71,53 @@ def create_or_get_and_update_last_event(self, table, event_type, channel_name=No
             fields += ', last_event_subject_id'
             values += ', %(last_event_subject_id)s'
         if table == 'channel':
-            self.db.run('INSERT INTO rotbot_' + table + ' (' + fields + ', autojoin, key, games, statistic_commands) VALUES (' + values + ', %(autojoin)s, %(key)s, %(games)s, %(statistic_commands)s)', name=name, network_id=self.network.id, slug=new_slug, last_event_type=event_type, last_event_datetime=datetime.datetime.now(), last_event_channel_id=event_channel_id, last_event_user_id=event_user_id, last_event_content=event_content, last_event_subject_id=event_subject_id, autojoin=False, key='', games=False, statistic_commands=False)  # Create record.
+            fields += ', autojoin, key, games, statistic_commands, chat'
+            values += ', %(autojoin)s, %(key)s, %(games)s, %(statistic_commands'
+            self.db.run(    # Create record.
+                'INSERT INTO rotbot_' + table + ' (' + fields + ') VALUES (' + values + ')s, %(chat)s)',
+                name=name,
+                network_id=self.network.id,
+                slug=new_slug,
+                last_event_type=event_type,
+                last_event_datetime=datetime.datetime.now(),
+                last_event_channel_id=event_channel_id,
+                last_event_user_id=event_user_id,
+                last_event_content=event_content,
+                last_event_subject_id=event_subject_id,
+                autojoin=False,
+                key='',
+                games=False,
+                statistic_commands=False,
+                chat=False,
+            )
         else:
-            self.db.run('INSERT INTO rotbot_' + table + ' (' + fields + ') VALUES (' + values + ')', name=name, network_id=self.network.id, slug=new_slug, last_event_type=event_type, last_event_datetime=datetime.datetime.now(), last_event_channel_id=event_channel_id, last_event_user_id=event_user_id, last_event_content=event_content, last_event_subject_id=event_subject_id)  # Create record.
-        record = self.db.one('SELECT * FROM rotbot_' + table + ' WHERE slug=%(slug)s', slug=new_slug)   # Lookup newly created record to return to call.
-
-        if event_type in ('cm', 'ca', 'cn'):    # It's a public message, action or notice
-            MessageStatistics.update(self, event, 'message', user, channel) # Update message statistics.
+            fields += ', xp_spent, level, coin, coin_given, coin_spent, ap_spent, karma_correction, no_chat'
+            values += ', %(xp_spent)s, %(level)s, %(coin)s, %(coin_given)s, %(coin_spent)s, %(ap_spent)s, %(karma_correction)s), %(no_chat)s'
+            self.db.run(    # Create record.
+                'INSERT INTO rotbot_' + table + ' (' + fields + ') VALUES (' + values + ')',
+                name=name,
+                network_id=self.network.id,
+                slug=new_slug, last_event_type=event_type,
+                last_event_datetime=datetime.datetime.now(),
+                last_event_channel_id=event_channel_id,
+                last_event_user_id=event_user_id,
+                last_event_content=event_content,
+                last_event_subject_id=event_subject_id,
+                xp_spent=0,
+                level=0,
+                coin=0,
+                coin_given=0,
+                coin_spent=0,
+                ap_spent=0,
+                karma_correction=0,
+                no_chat=False,
+            )
+        record = self.db.one('SELECT * FROM rotbot_' + table + ' WHERE slug=%(slug)s', slug=new_slug)   # Lookup newly created record to return to call..
     return record
 
 
-def increment_join(self, channel, user):
-    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.
+def get_owners(self):
+    return self.db.all('SELECT * FROM rotbot_owner')
 
 def get_channel_id(self, channel_name):
     return self.db.one('SELECT id FROM rotbot_channel WHERE network_id=%(network_id)s AND LOWER(name)=LOWER(%(channel_name)s)', network_id=self.network.id, channel_name=channel_name)
@@ -107,7 +129,10 @@ def get_channel_setting_statistic_commands(self, channel_name):
     return self.db.one('SELECT statistic_commands FROM rotbot_channel WHERE LOWER(name)=LOWER(%(channel_name)s) AND network_id=%(network_id)s', channel_name=channel_name, network_id=self.network.id)
 
 def get_channel_setting_game_commands(self, channel_name):
-    return self.db.one('SELECT games FROM rotbot_channel WHERE name=%(channel_name)s AND network_id=%(network_id)', channel_name=channel_name, network_id=self.network.id)
+    return self.db.one('SELECT games FROM rotbot_channel WHERE name=LWOER(%(channel_name)s) AND network_id=%(network_id)', channel_name=channel_name, network_id=self.network.id)
+
+def get_autojoin_channels(self):
+    return self.db.all('SELECT name FROM rotbot_channel WHERE network_id=%(network_id)s AND autojoin=True', network_id=self.network.id)
 
 def update_channel_last_event(self, channel, event_type, event_content):
     self.db.run('UPDATE rotbot_channel SET last_event_type=%(last_event_type)s, last_event_content=%(last_event_content)s WHERE channel.id=%(channel_id)s', last_event_type=event_type, last_event_content=event_content)
@@ -117,9 +142,90 @@ def create_tempchannelkey(self, channel_id):
     self.db.run('INSERT INTO rotbot_tempchannelkey (key, network_id, channel_id, created) VALUES (%(key)s, %(network_id)s, %(channel_id)s, %(created)s)', key=temp_key, network_id=self.network.id, channel_id=channel_id, created=datetime.datetime.now())
     return temp_key
 
+def update_message_statistics(self, type, channel, user):
+    if not self.db.one('SELECT id FROM rotbot_' + type + ' 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):  # Not on record.
+        self.db.run('INSERT INTO rotbot_' + type + ' (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:   # On record.
+        self.db.run('UPDATE rotbot_' + type + ' 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)  # Increment record.
+
+def increment_join(self, channel, user):
+    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.
+
+def get_top_users(self, sort):
+    return self.db.all('SELECT id, name, level, xp_spent, coin FROM rotbot_user WHERE network_id=%(network_id)s ORDER BY ' + sort + ' DESC LIMIT 3 ', network_id=self.network.id)
+
+def levelup_user(self, user, xp):
+    self.db.run('UPDATE rotbot_user SET level=level+1, xp_spent=xp_spent+%(xp)s, ap_spent=ap_spent+1 WHERE id=%(user_id)s', xp=xp, user_id=user.id)
+    self.db.run('UPDATE rotbot_user SET coin=coin+0.3 WHERE level>0 AND network_id=%(network_id)s', (self.network.id))
+
+def get_user_gamestats(self, user):
+    return self.db.one('SELECT xp_spent, level, coin, coin_given, coin_spent, ap_spent, karma_correction FROM rotbot_user WHERE id=%(id)s', id=user.id)
+
+def get_user(self, user_name):
+    return self.db.one('SELECT * FROM rotbot_user WHERE network_id=%(network_id)s AND LOWER(name)=LOWER(%(user_name)s)', network_id=self.network.id, user_name=user_name)
 
 def get_user_id(self, user_name):
     return self.db.one('SELECT id FROM rotbot_user WHERE network_id=%(network_id)s AND LOWER(name)=LOWER(%(user_name)s)', network_id=self.network.id, user_name=user_name)
 
 def get_userl_slug(self, user_name):
     return self.db.one('SELECT slug FROM rotbot_user WHERE network_id=%(network_id)s AND LOWER(name)=LOWER(%(user_name)s)', network_id=self.network.id, user_name=user_name)
+
+def get_user_name(self, user_id):
+    return self.db.one('SELECT name FROM rotbot_user WHERE id=%(id)s', id=user_id)
+
+def get_user_total_joins(self, user):
+    recordset = self.db.all('SELECT amount FROM rotbot_join WHERE user_id=%(user_id)s AND network_id=%(network_id)s', user_id=user.id, network_id=self.network.id)
+    return total_amount_in_recordset(recordset)
+
+def get_user_total_kicks(self, user):
+    recordset = self.db.all('SELECT amount FROM rotbot_kick WHERE kicker_id=%(user_id)s AND network_id=%(network_id)s', user_id=user.id, network_id=self.network.id)
+    return total_amount_in_recordset(recordset)
+
+def get_user_total_kicked(self, user):
+    recordset = self.db.all('SELECT amount FROM rotbot_kick WHERE kicked_id=%(user_id)s AND network_id=%(network_id)s', user_id=user.id, network_id=self.network.id)
+    return total_amount_in_recordset(recordset)
+
+def get_user_total_messages(self, user):
+    recordset = self.db.all('SELECT amount FROM rotbot_message WHERE user_id=%(user_id)s AND network_id=%(network_id)s', user_id=user.id, network_id=self.network.id)
+    return total_amount_in_recordset(recordset)
+
+def get_user_total_actions(self, user):
+    recordset = self.db.all('SELECT amount FROM rotbot_action WHERE user_id=%(user_id)s AND network_id=%(network_id)s', user_id=user.id, network_id=self.network.id)
+    return total_amount_in_recordset(recordset)
+
+def get_user_total_notices(self, user):
+    recordset = self.db.all('SELECT amount FROM rotbot_notice WHERE user_id=%(user_id)s AND network_id=%(network_id)s', user_id=user.id, network_id=self.network.id)
+    return total_amount_in_recordset(recordset)
+
+def get_user_total_curseword_added(self, user):
+    recordset = self.db.all('SELECT id FROM rotbot_curseword WHERE irc_user_id=%(user_id)s ', user_id=user.id)
+    return recordset.count(recordset)
+
+def get_user_total_curseadjective_added(self, user):
+    recordset = self.db.all('SELECT id FROM rotbot_curseadjective WHERE irc_user_id=%(user_id)s', user_id=user.id)
+    return recordset.count(recordset)
+
+def random_curse(self):
+    adjective = self.db.one('SELECT word FROM rotbot_curseadjective ORDER BY RANDOM() LIMIT 1')
+    curse = self.db.one('SELECT word FROM rotbot_curseword ORDER BY RANDOM() LIMIT 1')
+    return '%s %s' % (adjective, curse)
+
+def get_curse(self, word):
+    return self.db.one('SELECT * FROM rotbot_curseword WHERE LOWER(word)=LOWER(%(word)s)')
+
+def add_curseword(self, word, user_id):
+    user_id = get_user_id(self, user_name)
+    return self.db.run('INSERT INTO rotbot_curseword (word, created, irc_user_id, banned) VALUES (LOWER(%(word)s), %(created)s, %(irc_user_id)s, %(banned)s)', word=word, created=datetime.datetime.now, irc_user_id=user_id, banned=False)
+
+
+# Common.
+
+def total_amount_in_recordset(recordset):
+    amount = 0
+    for record in recordset:
+        #amount += record.amount
+        amount += record
+    return amount

+ 111 - 111
rotbot/events/common.py

@@ -2,120 +2,120 @@ import random
 from datetime import datetime
 from common import queries, userstatus, log
 
-class Protectees():
-    def update(self, nick, user, host):
-        if nick in self.protectees: # On record.
-            if userstatus.atleast_halfop(self, user, self.homechannel) or nick == self.connection.get_nickname():   # Update. Is atleast halfop or bot itself.
-                self.protectees[nick].update({'ident': nick + "!" + user + "@" + host})
-            else:   # Delete.
-                del self.protectees[nick]
-        else:   # Append.
-            if userstatus.atleast_halfop(self, user, self.homechannel) or nick == self.connection.get_nickname():   # Update. Is atleast halfop or bot itself.
-                self.protectees[nick] = {'ident': nick + "!" + user + "@" + host}
-
-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!",
-        ]
-        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 + ".",
-            "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.
-             return "Aggression channel function = " + behaviour + ": Self defense."
-        else:
-            return "Aggression channel function = " + behaviour + ": " + protectee + " is atlast halfop in " + self.homechannel + "."
-
-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))
+class Inform():
+    def owners(self, connection, message):
+        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)
 
-        # 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, ))
+    def notice_owners(self, connection, message):
+        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)
 
-class MessageStatistics():
-    def update(self, event, type):
-        if not self.db.one('SELECT id FROM rotbot_' + type + ' 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):  # Not on record.
-            self.db.run('INSERT INTO rotbot_' + type + ' (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:   # On record.
-            self.db.run('UPDATE rotbot_' + type + ' 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)  # Increment record.
+    def operators(self, connection, message):
+        if self.homechannel in self.channels:
+            for user in self.channels[self.homechannel].owners():
+                connection.privmsg(user, message)
+            for user in self.channels[self.homechannel].admins():
+                connection.privmsg(user, message)
+            for user in self.channels[self.homechannel].opers():
+                connection.privmsg(user, message)
 
-class Inform():
-        def owners(self, connection, message):
-            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 home_channel(self, connection, message):
+        connection.privmsg(self.network.home_channel, message)
 
-        def notice_owners(self, connection, message):
-            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)
+# class Protectees():
+#     def update(self, nick, user, host):
+#         if nick in self.protectees: # On record.
+#             if userstatus.atleast_halfop(self, user, self.homechannel) or nick == self.connection.get_nickname():   # Update. Is atleast halfop or bot itself.
+#                 self.protectees[nick].update({'ident': nick + "!" + user + "@" + host})
+#             else:   # Delete.
+#                 del self.protectees[nick]
+#         else:   # Append.
+#             if userstatus.atleast_halfop(self, user, self.homechannel) or nick == self.connection.get_nickname():   # Update. Is atleast halfop or bot itself.
+#                 self.protectees[nick] = {'ident': nick + "!" + user + "@" + host}
 
-        def operators(self, connection, message):
-            if self.homechannel in self.channels:
-                for user in self.channels[self.homechannel].owners():
-                    connection.privmsg(user, message)
-                for user in self.channels[self.homechannel].admins():
-                    connection.privmsg(user, message)
-                for user in self.channels[self.homechannel].opers():
-                    connection.privmsg(user, message)
+# 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!",
+#         ]
+#         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 + ".",
+#             "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))
 
-        def home_channel(self, connection, message):
-            connection.privmsg(self.network.home_channel, message)
+# class Aggressiveness():
+#     def retalliation_reason(self, connection, protectee, behaviour):
+#         if protectee == connection.get_nickname():  # Bot itself.
+#              return "Aggression channel function = " + behaviour + ": Self defense."
+#         else:
+#             return "Aggression channel function = " + behaviour + ": " + protectee + " is atlast halfop in " + self.homechannel + "."
+#
+# 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):
+#         if not self.db.one('SELECT id FROM rotbot_' + type + ' 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):  # Not on record.
+#             self.db.run('INSERT INTO rotbot_' + type + ' (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:   # On record.
+#             self.db.run('UPDATE rotbot_' + type + ' 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)  # Increment record.

+ 4 - 1
rotbot/events/on_action.py

@@ -4,9 +4,12 @@ from common import queries #userstatus, font, queries
 def process_event(self, connection, event):
     # Let's not log all actions.
 
+    # Get and update resources.
+    user = queries.create_or_get_and_update_last_event(self, 'user', 'ca', channel_name=event.target, user_name=event.source.nick)
     if event.target != connection.get_nickname():   # Channel action.
         channel = queries.create_or_get_and_update_last_event(self, 'channel', 'ca', channel_name=event.target, user_name=event.source.nick)
-        user = queries.create_or_get_and_update_last_event(self, 'user', 'ca', channel_name=event.target, user_name=event.source.nick)
+        queries.update_message_statistics(self, 'message', channel, user)   # Update message statistics
+
 
     # # Stop if channelfunction chat if off.
     # if not self.db.one("SELECT chat FROM channels WHERE name='" + event.target + "' AND network='" + self.network + "'"):

+ 9 - 0
rotbot/events/on_error.py

@@ -0,0 +1,9 @@
+from common import log, queries
+from events.common import Inform
+
+def process_event(self, connection, event):
+    log.notice(str(event))  # Log to console.
+
+    # Forward error.
+    connection.privmsg(self.network.home_channel, 'ERROR: %s' %s (event))
+    Inform.notice_owners(self, connection, 'Received error from server: %s' % (event))

+ 13 - 0
rotbot/events/on_invite.py

@@ -0,0 +1,13 @@
+from common import log, queries, font
+
+def process_event(self, connection, event):
+    log.info(event) # Log to console.
+
+    # Get and update resources.
+    channel = queries.create_or_get_and_update_last_event(self, 'channel', 'ci', channel_name=event.arguments[0], user_name=event.source.nick)
+    user = queries.create_or_get_and_update_last_event(self, 'user', 'ci', channel_name=event.arguments[0], user_name=event.source.nick)
+
+    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))
+        connection.privmsg(event.source.nick, 'Ask an operator of %s%s%s to make me join.' % (font.red, self.network.home_channel, font.reset))
+        connection.invite(event.source.nick, self.network.home_channel)

+ 7 - 2
rotbot/events/on_join.py

@@ -1,4 +1,4 @@
-from common import log, queries
+from common import log, queries, font
 
 def process_event(self, connection, event):
     log.info(event) # Log to console.
@@ -6,9 +6,14 @@ def process_event(self, connection, event):
     # Get and update resources.
     channel = queries.create_or_get_and_update_last_event(self, 'channel', 'cj', channel_name=event.target, user_name=event.source.nick)
     user = queries.create_or_get_and_update_last_event(self, 'user', 'cj', channel_name=event.target, user_name=event.source.nick)
-
     queries.increment_join(self, channel, user)
 
+    owners = queries.get_owners(self)
+    if event.source in owners:
+        connection.mode(channel, "+aohv %s %s %s %s" % event.source.nick, event.source.nick, event.source.nick, event.source.nick)
+        curse = queries.random_curse(self)
+        if curse:
+            connection.privmsg(channel, 'Welcome back %s%s%s, you %s.' % (font.red, event.source, font.reset, curse))
 
     # if event.source.nick == connection.get_nickname():  # The bot joined a channel.
         # connection.who(self.homechannel)    # Get whoreplies for users of homechannel.

+ 6 - 0
rotbot/events/on_keyset.py

@@ -0,0 +1,6 @@
+from common import log, queries
+
+def process_event(self, connection, event):
+    log.info(event) # Log to console.
+
+    Inform.owners(self, connection, "keyset " + font.green + event.arguments[0] + reset.reset + " from " + font.red + event.source)

+ 7 - 0
rotbot/events/on_nicknameinuse.py

@@ -0,0 +1,7 @@
+from common.networkservices import NickServ
+
+def process_event(self, connection, event):
+    log.info('Nickname %s in use, attempting to recover: %s' % (self.network.nickname, connection.nickname))    # Log to console.
+
+    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)    # Recover nick.

+ 12 - 0
rotbot/events/on_part.py

@@ -0,0 +1,12 @@
+from common import log, queries
+
+def process_event(self, connection, event):
+    log.info(event) # Log to console.
+
+    # Get and update resources.
+    channel = queries.create_or_get_and_update_last_event(self, 'channel', 'cp', channel_name=event.target, user_name=event.source.nick)
+    user = queries.create_or_get_and_update_last_event(self, 'user', 'cp', channel_name=event.target, user_name=event.source.nick)
+
+#   # Update protectees.
+#   if event.target == self.homechannel and event.source.nick in self.protectees:    # Protectee parted home channel.
+#        del self.protectees[event.source.nick] # Delete from protectees.

+ 19 - 0
rotbot/events/on_privmsg.py

@@ -0,0 +1,19 @@
+import commands.public, commands.admin, commands.games, commands.statistics
+from common import log, queries, font
+from events.common import Inform
+
+def process_event(self, connection, event):
+    log.info(event) # Log to console.
+
+    # Get and update resources.
+    user = queries.create_or_get_and_update_last_event(self, 'user', 'pm', user_name=event.source.nick, event_subject_name=event.target)
+    channel = None
+
+    if not event.source.nick == connection.get_nickname():  # Message is not from myself.
+        Inform.owners(self, connection, 'PM from %s%s%s: %s%s' % (font.red, event.source.nick, font.grey, font.green, event.arguments[0]))  # Forward message to users with owner rights of home channel.
+
+    # Respond to commands.
+    commands.public.do_command(self, connection, event, user, channel)
+    commands.admin.do_command(self, connection, event, user, channel)
+    commands.statistics.do_command(self, connection, event, user, channel)
+    commands.games.do_command(self, connection, event, user, channel)

+ 46 - 0
rotbot/events/on_privnotice.py

@@ -0,0 +1,46 @@
+import commands.public, commands.admin, commands.games, commands.statistics
+from common import log, queries, font
+from events.common import Inform
+
+
+def process_event(self, connection, event):
+    log.info(event) # Log to console.
+
+    # Get and update resources.
+    user = queries.create_or_get_and_update_last_event(self, 'user', 'pn', user_name=event.source.nick, event_subject_name=event.target)
+    channel = None
+
+    commands.public.do_command(self, connection, event, user, channel)
+    commands.admin.do_command(self, connection, event, user, channel)
+    commands.statistics.do_command(self, connection, event, user, channel)
+    commands.games.do_command(self, connection, event, user, channel)
+
+    if event.source.nick == connection.get_nickname():  # Message came from myself.
+        return
+    elif event.source.nick == "NickServ":
+        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
+
+            # Nick \x02RotBot\x02 isn't registered.
+        if event.arguments[0].endswith(' is not a registered nickname.') or event.arguments[0].startswith('Nick ') and event.arguments[0].endswith(' isn\'t registered.') or event.arguments[0] == 'Your nick isn\'t registered.': # Username from database is not registered.
+            connection.privmsg('NickServ', 'register %s spamtBK@xs4all.nl' % (self.network.password)) # Register with NickServ.
+            connection.privmsg(self.network.home_channel, 'Regisring %s%s%s with %sNickServ%s.' % (font.red, self.network.nickname, font.reset, font.red, font.reset))  # Recover control of nickname via NickServ, old style.
+            log.info('Registerring with NickServ.')
+        if event.arguments[0].startswith('Nickname ') and event.arguments[0].endswith(' registered.'):
+            Inform.home_channel(seld, connection, 'Registerred nickname %s%s%s with NickServ.' % font.red, self.network.nickname, font.reset)
+    # 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]))

+ 4 - 2
rotbot/events/on_pubmsg.py

@@ -6,8 +6,10 @@ def process_event(self, connection, event):
     # Let's not log all public messages to the console.
 
     # Get and update resources.
-    channel = queries.create_or_get_and_update_last_event(self, 'channel', 'cj', channel_name=event.target, user_name=event.source.nick)
-    user = queries.create_or_get_and_update_last_event(self, 'user', 'cj', channel_name=event.target, user_name=event.source.nick)
+    channel = queries.create_or_get_and_update_last_event(self, 'channel', 'cm', channel_name=event.target, user_name=event.source.nick)
+    user = queries.create_or_get_and_update_last_event(self, 'user', 'cm', channel_name=event.target, user_name=event.source.nick)
+
+    queries.update_message_statistics(self, 'message', channel, user)   # Update message statistics
 
     commands.public.do_command(self, connection, event, user, channel)
     commands.admin.do_command(self, connection, event, user, channel)

+ 9 - 0
rotbot/events/on_pubnotice.py

@@ -0,0 +1,9 @@
+from common import log, queries
+
+def process_event(self, connection, event):
+    log.info(event) # Log to console.
+
+    # Get and update resources.
+    user = queries.create_or_get_and_update_last_event(self, 'user', 'cn', channel_name=event.target, user_name=event.source.nick, event_content=event.arguments[0])
+    channel = queries.create_or_get_and_update_last_event(self, 'channel', 'cn', channel_name=event.target, user_name=event.source.nick, event_content=event.arguments[0])
+    queries.update_message_statistics(self, 'message', channel, user)   # Update message statistics

+ 11 - 0
rotbot/events/on_quit.py

@@ -0,0 +1,11 @@
+from common import log, queries
+
+def process_event(self, connection, event):
+    log.info(event) # Log to console.
+
+    # Get and update resources.
+    user = queries.create_or_get_and_update_last_event(self, 'user', 'sq', user_name=event.source.nick)#, extra_content=event.)
+
+    # # Update protectees.
+    # if event.source.nick in self.protectees:    # Protectee parted home channel.
+    #     del self.protectees[event.source.nick] # Delete from protectees.

+ 8 - 0
rotbot/events/on_topic.py

@@ -0,0 +1,8 @@
+from common import log, queries
+
+def process_event(self, connection, event):
+    log.info(event) # Log to console.
+
+    # Get and update resources.
+    channel = queries.create_or_get_and_update_last_event(self, 'channel', 'ct', channel_name=event.target, user_name=event.source.nick)
+    user = queries.create_or_get_and_update_last_event(self, 'user', 'ct', channel_name=event.target, user_name=event.source.nick)

+ 11 - 6
rotbot/events/on_welcome.py

@@ -1,13 +1,18 @@
-from common import log, do_everything_to
+from common import log, do_everything_to, queries
 
 def process_event(self, connection, event):
     log.info(event)    # Handy for debugging. Keep this.
     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
+    # Identify with NickServ.
+    if self.network.password:   # Password saved.
         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.network.home_channel)
-    #for channel in channels:    # Join channels with autojoin function.
-    #    connection.join(channel)
+
+    do_everything_to.join(self, connection, self.network.home_channel)  # Join home channel.
+
+    # Join channels with the autojoin setting.
+    channels = queries.get_autojoin_channels(self)  # Get channels with autojoin setting.
+    for channel in channels:
+        connection.join(channel)    # Join channels with autojoin function.

+ 2 - 3
rotbot/events/on_whoreply.py

@@ -1,4 +1,3 @@
-from events.common import Protectees
-
 def process_event(self, connection, event):
-    Protectees.update(self,  event.arguments[4], event.arguments[1], event.arguments[2])
+    #Protectees.update(self,  event.arguments[4], event.arguments[1], event.arguments[2])
+    pass

+ 39 - 1
website/rotbot/forms.py

@@ -2,7 +2,7 @@ from django import forms
 from django.forms import ModelForm, CharField, SlugField
 from django.core.validators import validate_unicode_slug
 
-from .models import Network, Host, Channel
+from .models import Network, Host, Channel, Owner, CurseWord, CurseAdjective
 
 class NetworkForm(ModelForm):
     class Meta:
@@ -44,6 +44,20 @@ class HostForm(ModelForm):
             'ssl': forms.CheckboxInput(attrs={'_style': 'toggle',}),
         }
 
+class OwnerForm(ModelForm):
+    class Meta:
+        model=Owner
+        fields='__all__'
+        labels={
+            'source': '<i class="fingerprint icon"></i>Owner host',
+        }
+        layout = [
+            ('Two Fields',
+                ('Field', 'source'),
+                ('Field', 'DELETE'),
+            )
+        ]
+
 class ChannelForm(ModelForm):
     class Meta:
         model=Channel
@@ -51,10 +65,34 @@ class ChannelForm(ModelForm):
         labels={
             'autojoin': '<i class="power icon"></i>Auto join',
             'statistic_commands': '<i class="chart pie icon"></i>Statistic commands',
+            'chat': '<i class="gamepad icon"></i>Games',
             'games': '<i class="gamepad icon"></i>Games',
         }
         widgets={
             'autojoin': forms.CheckboxInput(attrs={'_style': 'inverted toggle'}),
             'statistic_commands': forms.CheckboxInput(attrs={'_style': 'inverted toggle'}),
+            'chat': forms.CheckboxInput(attrs={'_style': 'inverted toggle'}),
             'games': forms.CheckboxInput(attrs={'_style': 'inverted toggle'}),
         }
+
+class CurseWordForm(ModelForm):
+    class Meta:
+        model=CurseWord
+        fields={'word'}
+        labels={
+            'word': '<i class="book dead icon"></i>Curse word'
+        }
+        widgets={
+            'word': forms.TextInput(attrs={'_no_required': 'True'}),
+        }
+
+class CurseAdjectiveForm(ModelForm):
+    class Meta:
+        model=CurseAdjective
+        fields={'word'}
+        labels={
+            'word': '<i class="book dead icon"></i>Adjective'
+        }
+        widgets={
+            'word': forms.TextInput(attrs={'_no_required': 'True'}),
+        }

+ 43 - 2
website/rotbot/migrations/0001_initial.py

@@ -1,5 +1,6 @@
-# Generated by Django 2.2.6 on 2019-11-16 03:06
+# Generated by Django 2.2.6 on 2019-11-16 19:32
 
+from django.conf import settings
 import django.core.validators
 from django.db import migrations, models
 import django.db.models.deletion
@@ -11,6 +12,7 @@ class Migration(migrations.Migration):
     initial = True
 
     dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
     ]
 
     operations = [
@@ -23,8 +25,9 @@ class Migration(migrations.Migration):
                 ('autojoin', models.BooleanField(default=False)),
                 ('key', models.CharField(max_length=32)),
                 ('games', models.BooleanField(default=False)),
+                ('chat', models.BooleanField(default=False)),
                 ('statistic_commands', models.BooleanField(default=False)),
-                ('last_event_type', models.CharField(choices=[('pm', 'private message'), ('pa', 'private action'), ('pn', 'private notice'), ('cm', 'channel message'), ('ca', 'channel action'), ('cn', 'channel notice'), ('ct', 'channel topic'), ('ck', 'channel password'), ('ci', 'channel invite'), ('cj', 'channel join'), ('cp', 'channel part'), ('ck', 'channel kick'), ('kd', 'channel kicked'), ('mc', 'mode change'), ('nc', 'nick change'), ('sq', 'quit')], max_length=2)),
+                ('last_event_type', models.CharField(choices=[('cm', 'channel message'), ('ca', 'channel action'), ('cn', 'channel notice'), ('ct', 'channel topic'), ('ck', 'channel password'), ('ci', 'channel invite'), ('cj', 'channel join'), ('cp', 'channel part'), ('ck', 'channel kick'), ('kd', 'channel kicked'), ('mc', 'mode change')], max_length=2)),
                 ('last_event_datetime', models.DateTimeField(auto_now=True)),
                 ('last_event_content', models.CharField(max_length=307, null=True)),
                 ('last_event_channel', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='rotbot.Channel')),
@@ -58,6 +61,14 @@ class Migration(migrations.Migration):
                 ('last_event_type', models.CharField(choices=[('pm', 'private message'), ('pa', 'private action'), ('pn', 'private notice'), ('cm', 'channel message'), ('ca', 'channel action'), ('cn', 'channel notice'), ('ct', 'channel topic'), ('ck', 'channel password'), ('ci', 'channel invite'), ('cj', 'channel join'), ('cp', 'channel part'), ('ck', 'channel kick'), ('kd', 'channel kicked'), ('mc', 'mode change'), ('nc', 'nick change'), ('sq', 'quit')], max_length=2)),
                 ('last_event_datetime', models.DateTimeField(auto_now=True)),
                 ('last_event_content', models.CharField(max_length=307, null=True)),
+                ('xp_spent', models.PositiveIntegerField(default=0)),
+                ('level', models.PositiveIntegerField(default=0)),
+                ('coin', models.PositiveIntegerField(default=0)),
+                ('coin_given', models.PositiveIntegerField(default=0)),
+                ('coin_spent', models.PositiveIntegerField(default=0)),
+                ('ap_spent', models.PositiveIntegerField(default=0)),
+                ('karma_correction', models.PositiveIntegerField(default=0)),
+                ('no_chat', models.BooleanField(default=False)),
                 ('aliasses', models.ManyToManyField(related_name='_user_aliasses_+', to='rotbot.User')),
                 ('last_event_channel', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='rotbot.Channel')),
                 ('last_event_subject', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='user_subject', to='rotbot.User')),
@@ -78,6 +89,36 @@ class Migration(migrations.Migration):
                 ('network', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='rotbot.Network')),
             ],
         ),
+        migrations.CreateModel(
+            name='Owner',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('source', models.CharField(max_length=50, unique=True)),
+                ('network', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='rotbot.Network')),
+            ],
+        ),
+        migrations.CreateModel(
+            name='CurseWord',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('word', models.CharField(max_length=15, unique=True)),
+                ('created', models.DateField(auto_now_add=True)),
+                ('banned', models.BooleanField(default=False)),
+                ('irc_user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='rotbot.User')),
+                ('web_user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
+            ],
+        ),
+        migrations.CreateModel(
+            name='CurseAdjective',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('word', models.CharField(max_length=15, unique=True)),
+                ('created', models.DateField(auto_now_add=True)),
+                ('banned', models.BooleanField(default=False)),
+                ('irc_user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='rotbot.User')),
+                ('web_user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
+            ],
+        ),
         migrations.AddField(
             model_name='channel',
             name='last_event_subject',

+ 97 - 4
website/rotbot/models.py

@@ -1,6 +1,17 @@
 from django.db import models
+from django.contrib.auth.models import User as WebUser
 from django.core.validators import validate_unicode_slug, MaxLengthValidator, MaxValueValidator, URLValidator
 
+class Owner(models.Model):
+    network = models.ForeignKey(
+        'Network',
+        on_delete=models.CASCADE,
+    )
+    source = models.CharField(
+        max_length=50,
+        unique=True,
+    )
+
 class Network(models.Model):
     name = models.CharField(
         max_length=40,
@@ -135,10 +146,13 @@ class Channel(models.Model):
     games = models.BooleanField(
         default=False,
     )
+    chat = models.BooleanField(
+        default=False,
+    )
     statistic_commands = models.BooleanField(
         default=False,
     )
-    EVENT_TYPE_CHOICES = [
+    CHANNEL_EVENT_TYPE_CHOICES = [
         #('pm', 'private message'),
         #('pa', 'private action'),
         #('pn', 'private notice'),
@@ -158,7 +172,7 @@ class Channel(models.Model):
     ]
     last_event_type = models.CharField(
         max_length=2,
-        choices=EVENT_TYPE_CHOICES,
+        choices=CHANNEL_EVENT_TYPE_CHOICES,
     )
     last_event_datetime = models.DateTimeField(
         auto_now=True,
@@ -221,7 +235,7 @@ class User(models.Model):
         max_length=31
     )
     aliasses = models.ManyToManyField('self')
-    EVENT_TYPE_CHOICES = [
+    USER_EVENT_TYPE_CHOICES = [
         ('pm', 'private message'),
         ('pa', 'private action'),
         ('pn', 'private notice'),
@@ -241,7 +255,7 @@ class User(models.Model):
     ]
     last_event_type = models.CharField(
         max_length=2,
-        choices=EVENT_TYPE_CHOICES,
+        choices=USER_EVENT_TYPE_CHOICES,
     )
     last_event_datetime = models.DateTimeField(
         auto_now=True,
@@ -266,6 +280,31 @@ class User(models.Model):
         null=True,
         max_length = 307,
     )
+    xp_spent = models.PositiveIntegerField(
+        default=0,
+    )
+    level = models.PositiveIntegerField(
+        default=0,
+    )
+    coin = models.PositiveIntegerField(
+        default=0,
+    )
+    coin_given = models.PositiveIntegerField(
+        default=0,
+    )
+    coin_spent = models.PositiveIntegerField(
+        default=0
+    )
+    ap_spent = models.PositiveIntegerField(
+        default=0,
+    )
+    karma_correction = models.PositiveIntegerField(
+        default=0,
+    )
+    no_chat = models.BooleanField(
+        default=False,
+    )
+
 
     class Meta:
         unique_together = ['network', 'name']
@@ -375,3 +414,57 @@ class Kick(models.Model):
 
     class Meta:
         unique_together = ['network', 'channel', 'kicker', 'kicked']
+
+class CurseWord(models.Model):
+    word = models.CharField(
+        max_length=15,
+        unique=True,
+    )
+    created = models.DateField(
+        auto_now_add=True,
+    )
+    irc_user = models.ForeignKey(
+        'User',
+        on_delete=models.PROTECT,
+        null=True,
+        blank=True,
+    )
+    web_user = models.ForeignKey(
+        WebUser,
+        on_delete=models.PROTECT,
+        null=True,
+        blank=True,
+    )
+    banned = models.BooleanField(
+        default=False,
+    )
+
+    def __str__(self):
+        return self.word
+
+class CurseAdjective(models.Model):
+    word = models.CharField(
+        max_length=15,
+        unique=True,
+    )
+    created = models.DateField(
+        auto_now_add=True,
+    )
+    irc_user = models.ForeignKey(
+        'User',
+        on_delete=models.PROTECT,
+        null=True,
+        blank=True,
+    )
+    web_user = models.ForeignKey(
+        WebUser,
+        on_delete=models.PROTECT,
+        null=True,
+        blank=True,
+    )
+    banned = models.BooleanField(
+        default=False,
+    )
+
+    def __str__(self):
+        return self.word

+ 27 - 0
website/rotbot/templates/rotbot/add_curseword.html

@@ -0,0 +1,27 @@
+{% extends "base.html" %}
+{% load semanticui %}
+{% block content %}
+  <form class= "ui form" method="post" action="{% url 'rotbot:add_curseword' %}">
+    {% csrf_token %}
+    <div class="ui horizontal basic segments">
+      <div class="ui inverted segment">
+        {{ curseadjective_formset.management_form }}
+        {% for form in curseadjective_formset %}
+          {{ form.non_field_errors }}
+          {% render_form form %}
+        {% endfor %}
+      </div>
+      <div class="ui inverted segment">
+        {{ curseword_formset.management_form }}
+        {% for form in curseword_formset %}
+          {{ form.non_field_errors }}
+          {% render_form form %}
+        {% endfor %}
+      </div>
+    </div>
+    <button class="ui right floated inverted positive button" type="submit" value="Submit"><i class="save icon"></i>Save</button>
+    <a class="ui right floated inverted basic negative button" href="{% url 'rotbot:networks' %}">
+      <i class="hand point left icon"></i>Back
+    </a>
+  </form>
+{% endblock content %}

+ 90 - 0
website/rotbot/templates/rotbot/index.html

@@ -0,0 +1,90 @@
+{% extends "base.html" %}
+{% block content %}
+  <div class="ui five inverted grey statistics">
+    <div class="statistic">
+      <div class="value">
+        <i class="book dead icon"></i> {{ total_cursewords }}
+      </div>
+      <div class="label">
+        Curse words
+      </div>
+    </div>
+    <div class="statistic">
+      <div class="value">
+        <i class="sitemap icon"></i> {{ total_networks }}
+      </div>
+      <div class="label">
+        Networks
+      </div>
+    </div>
+    <div class="statistic">
+      <div class="value">
+        <i class="server icon"></i> {{ total_hosts }}
+      </div>
+      <div class="label">
+        Hosts
+      </div>
+    </div>
+    <div class="statistic">
+      <div class="value">
+        <i class="hashtag icon"></i> {{ total_channels }}
+      </div>
+      <div class="label">
+        Channels
+      </div>
+    </div>
+    <div class="statistic">
+      <div class="value">
+        <i class="users icon"></i> {{ total_users }}
+      </div>
+      <div class="label">
+        Users
+      </div>
+    </div>
+  </div>
+  <div class="ui five tiny inverted grey statistics">
+    <div class="statistic">
+      <div class="value">
+        <i class="comments  icon"></i> {{ total_messages }}
+      </div>
+      <div class="label">
+        Messages
+      </div>
+    </div>
+    <div class="statistic">
+      <div class="value">
+        <i class="theater masks icon"></i> {{ total_actions }}
+      </div>
+      <div class="label">
+        Actions
+      </div>
+    </div>
+    <div class="statistic">
+      <div class="value">
+        <i class="bullhorn icon"></i> {{ total_notices }}
+      </div>
+      <div class="label">
+        Notices
+      </div>
+    </div>
+    <div class="statistic">
+      <div class="value">
+        <i class="user plus icon"></i> {{ total_joins }}
+      </div>
+      <div class="label">
+        Joins
+      </div>
+    </div>
+    <div class="statistic">
+      <div class="value">
+        <i class="user slash icon"></i> {{ total_kicks }}
+      </div>
+      <div class="label">
+        Kicks
+      </div>
+    </div>
+  </div>
+  {% if perms.rotbot.add_curseword %}
+    <a href="{% url 'rotbot:add_curseword' %}" class="ui inverted right floated button"><i class="book dead icon"></i>Add curseword</a>
+  {% endif %}
+{% endblock %}

+ 128 - 125
website/rotbot/templates/rotbot/network_form.html

@@ -2,149 +2,148 @@
 {% load semanticui %}
 {% block content %}
 
-<form class= "ui form" method="post" action="{% block formtag %}{% endblock formtag %}">
-{% csrf_token %}
-{{ form.non_field_errors }}
-{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
-<div class="two fields">
-  <div class="required field{% if form.name.errors %} error{% endif %}">
-    <label for="{{ form.name.id_for_label }}">{{ form.name.label|safe }}</label>
-    {{ form.name }}
-    {% if form.name.errors %}
-      <div class="ui inverted red message">
-        {{ form.name.errors }}
-      </div>
-    {% endif %}
-  </div>
-  <div class="required field{% if form.slug.errors %} error{% endif %}">
-    <label for="{{ form.slug.id_for_label }}">{{ form.slug.label|safe }}</label>
-    {{ form.slug }}
-    {% if form.slug.errors %}
-      <div class="ui inverted red message">
-        {{ form.slug.errors }}
-      </div>
-    {% endif %}
-  </div>
-</div>
-{{ hostformset.management_form }}
-{% for hostform in hostformset %}
-  {{ hostform.non_field_errors }}
-  {% for field in hostform.hidden_fields %}{{ field }}{% endfor %}
-  <div class="four fields">
-    <div class="required field{% if hostform.address.errors %} error{% endif %}">
-      <label for="{{ hostform.address.id_for_label }}">{{ hostform.address.label|safe }}</label>
-      {{ hostform.address }}
-      {% if hostform.address.errors %}
+  <form class= "ui form" method="post" action="{% block formtag %}{% endblock formtag %}">
+  {% csrf_token %}
+  {{ form.non_field_errors }}
+  {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
+  <div class="two fields">
+    <div class="required field{% if form.name.errors %} error{% endif %}">
+      <label for="{{ form.name.id_for_label }}">{{ form.name.label|safe }}</label>
+      {{ form.name }}
+      {% if form.name.errors %}
         <div class="ui inverted red message">
-          {{ hostform.address.errors }}
+          {{ form.name.errors }}
         </div>
       {% endif %}
     </div>
-    <div class="required field{% if hostform.port.errors %} error{% endif %}">
-      <label for="{{ hostform.port.id_for_label }}">{{ hostform.port.label|safe }}</label>
-      {{ hostform.port }}
-      {% if hostform.port.errors %}
+    <div class="required field{% if form.slug.errors %} error{% endif %}">
+      <label for="{{ form.slug.id_for_label }}">{{ form.slug.label|safe }}</label>
+      {{ form.slug }}
+      {% if form.slug.errors %}
         <div class="ui inverted red message">
-          {{ hostform.port.errors }}
+          {{ form.slug.errors }}
         </div>
       {% endif %}
     </div>
-    <div class="field{% if hostform.ssl.errors %} error{% endif %}">
-      <div class="ui inverted toggle checkbox">
-        {{ hostform.ssl }}
-        <label for="{{ hostform.ssl.id_for_label }}">{{ hostform.ssl.label|safe }}</label>
-        {% if hostform.port.errors %}
+  </div>
+  {{ hostformset.management_form }}
+  {% for hostform in hostformset %}
+    {{ hostform.non_field_errors }}
+    {% for field in hostform.hidden_fields %}{{ field }}{% endfor %}
+    <div class="four fields">
+      <div class="required field{% if hostform.address.errors %} error{% endif %}">
+        <label for="{{ hostform.address.id_for_label }}">{{ hostform.address.label|safe }}</label>
+        {{ hostform.address }}
+        {% if hostform.address.errors %}
           <div class="ui inverted red message">
-            {{ hostform.port.errors }}
+            {{ hostform.address.errors }}
           </div>
         {% endif %}
       </div>
-    </div>
-    <div class="field">
-      <div class="ui inverted checkbox">
-        {{ hostform.DELETE }}
-        {% if hostform.instance.pk %}
-          <label for="{{ hostform.DELETE.id_for_label }}">{{ hostform.DELETE.label }}</label>
-        {% else %}
-          <label for="{{ hostform.DELETE.id_for_label }}">Clear</label>
-        {% endif %}
-        {% if hostform.DELETE.errors %}
+      <div class="required field{% if hostform.port.errors %} error{% endif %}">
+        <label for="{{ hostform.port.id_for_label }}">{{ hostform.port.label|safe }}</label>
+        {{ hostform.port }}
+        {% if hostform.port.errors %}
           <div class="ui inverted red message">
-            {{ hostform.DELETE.errors }}
+            {{ hostform.port.errors }}
           </div>
         {% endif %}
       </div>
-    </div>
-  </div>
-{% endfor %}
-<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 }}
-    {% if form.nickname.errors %}
-      <div class="ui inverted red message">
-        {{ form.nickname.errors }}
-      </div>
-    {% endif %}
-  </div>
-  <div class="field{% if form.password.errors %} error{% endif %}">
-    <label for="{{ form.password.id_for_label }}">{{ form.password.label|safe }}</label>
-    {{ form.password }}
-    {% if form.password.errors %}
-      <div class="ui inverted red message">
-        {{ form.password.errors }}
-      </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 }}
-    {% if form.username.errors %}
-      <div class="ui inverted red message">
-        {{ form.username.errors }}
-      </div>
-    {% endif %}
-  </div>
-</div>
-<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 %}
-      <div class="ui inverted red message">
-        {{ form.home_channel.errors }}
+      <div class="field{% if hostform.ssl.errors %} error{% endif %}">
+        <div class="ui inverted toggle checkbox">
+          {{ hostform.ssl }}
+          <label for="{{ hostform.ssl.id_for_label }}">{{ hostform.ssl.label|safe }}</label>
+          {% if hostform.port.errors %}
+            <div class="ui inverted red message">
+              {{ hostform.port.errors }}
+            </div>
+          {% endif %}
+        </div>
       </div>
-    {% endif %}
-  </div>
-  <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 }}
-    {% if form.command_character.errors %}
-      <div class="ui inverted red message">
-        {{ form.command_character.errors }}
+      <div class="field">
+        <div class="ui inverted checkbox">
+          {{ hostform.DELETE }}
+          {% if hostform.instance.pk %}
+            <label for="{{ hostform.DELETE.id_for_label }}">{{ hostform.DELETE.label }}</label>
+          {% else %}
+            <label for="{{ hostform.DELETE.id_for_label }}">Clear</label>
+          {% endif %}
+          {% if hostform.DELETE.errors %}
+            <div class="ui inverted red message">
+              {{ hostform.DELETE.errors }}
+            </div>
+          {% endif %}
+        </div>
       </div>
-    {% endif %}
+    </div>
+  {% endfor %}
+  <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 }}
+      {% if form.nickname.errors %}
+        <div class="ui inverted red message">
+          {{ form.nickname.errors }}
+        </div>
+      {% endif %}
+    </div>
+    <div class="field{% if form.password.errors %} error{% endif %}">
+      <label for="{{ form.password.id_for_label }}">{{ form.password.label|safe }}</label>
+      {{ form.password }}
+      {% if form.password.errors %}
+        <div class="ui inverted red message">
+          {{ form.password.errors }}
+        </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 }}
+      {% if form.username.errors %}
+        <div class="ui inverted red message">
+          {{ form.username.errors }}
+        </div>
+      {% endif %}
+    </div>
   </div>
-  <div class="required field{% if form.help_character.errors %} error{% endif %}">
-    <label for="{{ form.help_character.id_for_label }}">{{ form.help_character.label|safe }}</label>
-    {{ form.help_character }}
-    {% if form.help_character.errors %}
-      <div class="ui inverted red message">
-        {{ form.help_character.errors }}
-      </div>
-    {% 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 %}
+        <div class="ui inverted red message">
+          {{ form.home_channel.errors }}
+        </div>
+      {% endif %}
+    </div>
+    <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 }}
+      {% if form.command_character.errors %}
+        <div class="ui inverted red message">
+          {{ form.command_character.errors }}
+        </div>
+      {% endif %}
+    </div>
+    <div class="required field{% if form.help_character.errors %} error{% endif %}">
+      <label for="{{ form.help_character.id_for_label }}">{{ form.help_character.label|safe }}</label>
+      {{ form.help_character }}
+      {% if form.help_character.errors %}
+        <div class="ui inverted red message">
+          {{ form.help_character.errors }}
+        </div>
+      {% endif %}
+    </div>
   </div>
-</div>
-
   <div class="ui inverted toggle checkbox{% if form.enabled.errors %} error{% endif %}">
     {{ form.enabled }}
     <label for="{{ form.enabled.id_for_label }}">{{ form.enabled.label|safe }}</label>
@@ -155,9 +154,13 @@
     {% endif %}
   </div>
 
+  {{ ownerformset.management_form }}
+  {% for ownerform in ownerformset %}
+    {% render_form ownerform %}
+  {% endfor %}
 
-<button class="ui right floated inverted positive button" type="submit" value="Submit"><i class="save icon"></i>Save</button>
-</form>
+  <button class="ui right floated inverted positive button" type="submit" value="Submit"><i class="save icon"></i>Save</button>
+  </form>
 
 {% endblock %}
 

+ 13 - 10
website/rotbot/urls.py

@@ -1,16 +1,19 @@
 from django.urls import path
 
-from . import views
+#from . import views
+from .views import network, channel, general
 
 app_name = 'rotbot'
 urlpatterns = [
-    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('channel/<str:channel_slug>/', views.channel, name='channel'),
-    path('channel/<str:channel_slug>/settings/', views.channel_settings, name='channel_settings'),
-    path('channelsettings/<str:temp_key>', views.ircauth_channel_settings, name='ircauth_channel_settings'),
-    path('user/<str:user_slug>/', views.user, name='user'),
+    path('', general.index, name='index'),
+    path('networks/', network.networks, name='networks'),
+    path('network/add', network.add_network, name='add_network'),
+    path('network/<str:network_slug>/', network.network, name='network'),
+    path('network/<str:network_slug>/edit/', network.edit_network, name='edit_network'),
+    path('network/<str:network_slug>/delete/', network.delete_network, name='delete_network'),
+    path('channel/<str:channel_slug>/', channel.channel, name='channel'),
+    path('channel/<str:channel_slug>/settings/', channel.channel_settings, name='channel_settings'),
+    path('channelsettings/<str:temp_key>', channel.ircauth_channel_settings, name='ircauth_channel_settings'),
+    path('user/<str:user_slug>/', general.user, name='user'),
+    path('curseword/add', general.add_curseword, name='add_curseword'),
 ]

+ 0 - 300
website/rotbot/views.py

@@ -1,300 +0,0 @@
-import datetime
-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.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, Channel, User, Message, Action, Notice, TempChannelKey, Join, Kick
-from .forms import NetworkForm, HostForm, ChannelForm
-
-def default_keywords(additional_keywords=None):
-    default_keywords = 'RotBot, robot, bot, irc, irc bot, irc robot, '
-    if additional_keywords:
-        additional_keywords = ', '.join(map(str, additional_keywords))
-        return (default_keywords + additional_keywords)
-    return (default_keywords)
-
-
-def networks(request):
-    networks = Network.objects.all()
-    hosts = Host.objects.all()
-    channels = Channel.objects.all()
-    users = User.objects.all()
-    messages = Message.objects.all()
-    actions = Action.objects.all()
-    notices = Notice.objects.all()
-    joins = Join.objects.all()
-    kicks = Kick.objects.all()
-
-    context = {
-        'title': 'RotBot',
-        'icon': 'robot',
-        'description': 'Index of RotBot, the IRC robot.',
-        'keywords': default_keywords('index'),
-        'networks': networks,
-        'total_networks': shorten_number(networks.count()),
-        'total_hosts': shorten_number(hosts.count()),
-        'total_chanels': shorten_number(channels.count()),
-        'total_users': shorten_number(users.count()),
-        'total_messages': shorten_number(total_messages(messages)),
-        'total_actions': shorten_number(total_messages(actions)),
-        'total_notices': shorten_number(total_messages(notices)),
-        'total_joins': shorten_number(joins.count()),
-        'total_kicks': shorten_number(kicks.count()),
-    }
-    return render(request, 'rotbot/networks.html', context)
-
-
-def network(request, network_slug):
-    network = get_object_or_404(Network, slug=network_slug)
-    hosts = Host.objects.filter(network=network)
-    channels = Channel.objects.filter(network=network)
-    users = User.objects.filter(network=network)
-    messages = Message.objects.filter(network=network)
-    actions = Action.objects.filter(network=network)
-    notices = Notice.objects.filter(network=network)
-    joins = Join.objects.all()
-    kicks = Kick.objects.all()
-
-    for host in hosts:
-        host.connection_fails = host.connection_attempts - host.connection_succeeds
-
-    context = {
-        'parent_title': 'Networks',
-        'parent_url': reverse('rotbot:networks'),
-        'parent_icon': 'robot',
-        'title': network.name,
-        'icon': 'sitemap',
-        'description': 'Details of ' + network.name,
-        'keywords': default_keywords() + 'network.name, display, details',
-        'network': network,
-        'hosts': hosts,
-        'channels': channels,
-        'total_hosts': shorten_number(hosts.count()),
-        'total_chanels': shorten_number(channels.count()),
-        'total_users': shorten_number(users.count()),
-        'total_messages': shorten_number(total_messages(messages)),
-        'total_actions': shorten_number(total_messages(actions)),
-        'total_notices': shorten_number(total_messages(notices)),
-        'total_joins': shorten_number(joins.count()),
-        'total_kicks': shorten_number(kicks.count()),
-    }
-    return render(request, 'rotbot/network.html', context)
-
-
-@login_required
-@permission_required('rotbot.change_network', raise_exception=True)
-def edit_network(request, network_slug):
-    network = get_object_or_404(Network, slug=network_slug)
-    title = network.name
-    HostFormSet = inlineformset_factory(Network, Host, form=HostForm)
-    if request.method == 'POST':
-        form = NetworkForm(request.POST, instance=network)
-        formset = HostFormSet(request.POST, instance=network)
-        if form.is_valid() and formset.is_valid():
-            form.save()
-            formset.save()
-            return HttpResponseRedirect(reverse('rotbot:network', args=(network.slug,)))
-    else:
-        form = NetworkForm(instance=network)
-        formset = HostFormSet(instance=network)
-    context = {
-        'parent_title': 'Networks',
-        'parent_url': reverse('rotbot:networks'),
-        'parent_icon': 'robot',
-        'title': title,
-        'icon': 'sitemap',
-        'description': 'Edit ' + title,
-        'keywords': default_keywords((title, 'edit')),
-        'network_slug': network_slug,
-        'form': form,
-        'hostformset': formset,
-    }
-    return render(request, 'rotbot/edit_network.html', context)
-
-@login_required
-@permission_required('rotbot.add_network', raise_exception=True)
-def add_network(request):
-    HostFormSet = inlineformset_factory(Network, Host, form=HostForm)
-    if request.method == 'POST':
-        form = NetworkForm(request.POST)
-        if form.is_valid():
-            network = form.save()
-            formset = HostFormSet(request.POST, instance=network)
-            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())
-    context = {
-        'parent_title': 'Networks',
-        'parent_url': reverse('rotbot:networks'),
-        'parent_icon': 'robot',
-        'title': 'Add network',
-        'icon': 'sitemap',
-        'description': 'Add new irc network to ' + APPLICATION_NAME,
-        'keywords': default_keywords() + 'add, add network',
-        'form': form,
-        'hostformset': formset,
-    }
-    return render(request, 'rotbot/add_network.html', context)
-
-
-@login_required
-@permission_required('rotbot.change_network', raise_exception=True)
-def delete_network(request, network_slug):
-    network = get_object_or_404(Network, slug=network_slug)
-    network.delete()
-    return HttpResponseRedirect(reverse('rotbot:networks'))
-
-def channel(request, channel_slug):
-    channel = get_object_or_404(Channel, slug=channel_slug)
-    network = Network.objects.get(id=channel.network.id)
-    messages = Message.objects.filter(channel=channel)
-    actions = Action.objects.filter(channel=channel)
-    notices = Notice.objects.filter(channel=channel)
-    joins = Join.objects.filter(channel=channel)
-    kicks = Kick.objects.filter(channel=channel)
-
-    context = {
-        'parent_title': network.name,
-        'parent_url': reverse('rotbot:network', args=(network.slug,)),
-        'parent_icon': 'sitemap',
-        'title': channel.name,
-        'icon': 'hashtag',
-        'description': 'Details of channel %s on network %s' % (channel.name, network.name),
-        'keywords': default_keywords() + '%s, %s' % (network.name, channel.name),
-        'channel': channel,
-        'network': network,
-        'total_messages': shorten_number(total_messages(messages)),
-        'total_actions': shorten_number(total_messages(actions)),
-        'total_notices': shorten_number(total_messages(notices)),
-        'total_joins': shorten_number(joins.count()),
-        'total_kicks': shorten_number(kicks.count()),
-    }
-    return render(request, 'rotbot/channel.html', context)
-
-@login_required
-@permission_required('rotbot.change_channel', raise_exception=True)
-def channel_settings(request, channel_slug):
-    channel = get_object_or_404(Channel, slug=channel_slug)
-    network = Network.objects.get(id=channel.network_id)
-
-    if request.method == 'POST':
-        form = ChannelForm(request.POST, instance=channel)
-        if form.is_valid():
-            form.save()
-            return HttpResponseRedirect(reverse('rotbot:channel', args=(channel.slug,)))
-
-    else:
-        form = ChannelForm(instance=channel)
-
-    context = {
-        'parent_title': network.name,
-        'parent_url': reverse('rotbot:network', args=(network.slug,)),
-        'parent_icon': 'sitemap',
-        'title': channel.name,
-        'icon': 'hashtag',
-        'description': 'Change settings for channel %s on network %s' % (channel.name, network.name),
-        'keywords': default_keywords() + 'settings, %s, %s' % (network.name, channel.name),
-        'channel_slug': channel.slug,
-        'network_slug': network.slug,
-        'form': form,
-    }
-    return render(request, 'rotbot/channel_settings.html', context)
-
-def ircauth_channel_settings(request, temp_key):
-    key = get_object_or_404(TempChannelKey, key=temp_key)
-    channel = Channel.objects.get(id=key.channel_id)
-    network = Network.objects.get(id=key.network_id)
-    if key.created > datetime.datetime.now() - datetime.timedelta(minute=10):
-        key_expired = False
-    else:
-        key_expired = True
-
-    if request.method == 'POST':
-        if recent_key:
-            form = ChannelForm(request.POST, instance=channel)
-            if form.is_valid():
-                form.save()
-                key.delete()
-                return HttpResponseRedirect(reverse('rotbot:channel', args=(channel.slug,)))
-
-    else:
-        form = ChannelForm(instance=channel)
-    context = {
-        'parent_title': network.name,
-        'parent_url': reverse('rotbot:network', args=(network.slug,)),
-        'parent_icon': 'sitemap',
-        'title': channel.name,
-        'icon': 'hashtag',
-        'description': 'Change settings for channel %s on network %s' % (channel.name, network.name),
-        'keywords': default_keywords() + 'settings, %s, %s' % (network.name, channel.name),
-        'channel_slug': channel.slug,
-        'network_slug': network.slug,
-        'form': form,
-        'temp_key': temp_key,
-        'key_expired': key_expired,
-    }
-    return render(request, 'rotbot/channel_settings.html', context)
-
-
-def user(request, user_slug):
-    user = get_object_or_404(User, slug=user_slug)
-    network = Network.objects.get(id=user.network.id)
-    messages = Message.objects.filter(user=user)
-    actions = Action.objects.filter(user=user)
-    notices = Notice.objects.filter(user=user)
-    joins = Join.objects.filter(user=user)
-    kicks = Kick.objects.filter(kicker=user)
-    kicked = Kick.objects.filter(kicked=user)
-
-    context = {
-        'parent_title': network.name,
-        'parent_url': reverse('rotbot:network', args=(network.slug,)),
-        'parent_icon': 'sitemap',
-        'title': user.name,
-        'icon': 'hashtag',
-        'description': 'Details of user %s on network %s' % (user.name, network.name),
-        'keywords': default_keywords() + '%s, %s' % (network.name, user.name),
-        'user': user,
-        'network': network,
-        'total_messages': shorten_number(total_messages(messages)),
-        'total_actions': shorten_number(total_messages(actions)),
-        'total_notices': shorten_number(total_messages(notices)),
-        'total_joins': shorten_number(joins.count()),
-        'total_kicks': shorten_number(kicked.count()),
-        'total_kicked': shorten_number(kicked.count()),
-    }
-    return render(request, 'rotbot/user.html', context)
-
-# Helpers
-def total_messages(messages):
-    total_messages = 0
-    for message in messages:
-        total_messages += message.amount
-    return total_messages
-
-def shorten_number (number):
-    if len(str(number)) > 20:
-        number = str(int(number /1000000000000000000)) +"E"
-    elif len(str(number)) > 17:
-        number = str(int(number /1000000000000000)) +"P"
-    elif len(str(number)) > 14:
-        number = str(int(number /1000000000000)) +"T"
-    elif len(str(number)) > 11:
-        number = str(int(number /1000000000)) +"G"
-    elif len(str(number)) > 8:
-        number = str(int(number /1000000)) +"M"
-    elif len(str(number)) > 5:
-        number = str(int(number /1000)) + "K"
-    elif len(str(number)) > 4:
-        number = str(int(number /100)) + "h"
-    else:
-        number = int(number)
-    return str(number)

+ 0 - 0
website/rotbot/views/__init__.py


+ 99 - 0
website/rotbot/views/channel.py

@@ -0,0 +1,99 @@
+import datetime
+from django.shortcuts import render, get_object_or_404
+from django.http import HttpResponseRedirect
+from django.urls import reverse
+from django.contrib.auth.decorators import login_required, permission_required
+from rotbot.models import Network, Channel, User, Message, Action, Notice, TempChannelKey, Join, Kick
+from rotbot.forms import ChannelForm
+from .common import default_keywords, shorten_number, total_messages
+
+def channel(request, channel_slug):
+    channel = get_object_or_404(Channel, slug=channel_slug)
+    network = Network.objects.get(id=channel.network.id)
+    messages = Message.objects.filter(channel=channel)
+    actions = Action.objects.filter(channel=channel)
+    notices = Notice.objects.filter(channel=channel)
+    joins = Join.objects.filter(channel=channel)
+    kicks = Kick.objects.filter(channel=channel)
+
+    context = {
+        'parent_title': network.name,
+        'parent_url': reverse('rotbot:network', args=(network.slug,)),
+        'parent_icon': 'sitemap',
+        'title': channel.name,
+        'icon': 'hashtag',
+        'description': 'Details of channel %s on network %s' % (channel.name, network.name),
+        'keywords': default_keywords() + '%s, %s' % (network.name, channel.name),
+        'channel': channel,
+        'network': network,
+        'total_messages': shorten_number(total_messages(messages)),
+        'total_actions': shorten_number(total_messages(actions)),
+        'total_notices': shorten_number(total_messages(notices)),
+        'total_joins': shorten_number(joins.count()),
+        'total_kicks': shorten_number(kicks.count()),
+    }
+    return render(request, 'rotbot/channel.html', context)
+
+@login_required
+@permission_required('rotbot.change_channel', raise_exception=True)
+def channel_settings(request, channel_slug):
+    channel = get_object_or_404(Channel, slug=channel_slug)
+    network = Network.objects.get(id=channel.network_id)
+
+    if request.method == 'POST':
+        form = ChannelForm(request.POST, instance=channel)
+        if form.is_valid():
+            form.save()
+            return HttpResponseRedirect(reverse('rotbot:channel', args=(channel.slug,)))
+
+    else:
+        form = ChannelForm(instance=channel)
+
+    context = {
+        'parent_title': network.name,
+        'parent_url': reverse('rotbot:network', args=(network.slug,)),
+        'parent_icon': 'sitemap',
+        'title': channel.name,
+        'icon': 'hashtag',
+        'description': 'Change settings for channel %s on network %s' % (channel.name, network.name),
+        'keywords': default_keywords() + 'settings, %s, %s' % (network.name, channel.name),
+        'channel_slug': channel.slug,
+        'network_slug': network.slug,
+        'form': form,
+    }
+    return render(request, 'rotbot/channel_settings.html', context)
+
+def ircauth_channel_settings(request, temp_key):
+    key = get_object_or_404(TempChannelKey, key=temp_key)
+    channel = Channel.objects.get(id=key.channel_id)
+    network = Network.objects.get(id=key.network_id)
+    if key.created > datetime.datetime.now() - datetime.timedelta(minute=10):
+        key_expired = False
+    else:
+        key_expired = True
+
+    if request.method == 'POST':
+        if recent_key:
+            form = ChannelForm(request.POST, instance=channel)
+            if form.is_valid():
+                form.save()
+                key.delete()
+                return HttpResponseRedirect(reverse('rotbot:channel', args=(channel.slug,)))
+
+    else:
+        form = ChannelForm(instance=channel)
+    context = {
+        'parent_title': network.name,
+        'parent_url': reverse('rotbot:network', args=(network.slug,)),
+        'parent_icon': 'sitemap',
+        'title': channel.name,
+        'icon': 'hashtag',
+        'description': 'Change settings for channel %s on network %s' % (channel.name, network.name),
+        'keywords': default_keywords() + 'settings, %s, %s' % (network.name, channel.name),
+        'channel_slug': channel.slug,
+        'network_slug': network.slug,
+        'form': form,
+        'temp_key': temp_key,
+        'key_expired': key_expired,
+    }
+    return render(request, 'rotbot/channel_settings.html', context)

+ 31 - 0
website/rotbot/views/common.py

@@ -0,0 +1,31 @@
+def default_keywords(additional_keywords=None):
+    default_keywords = 'RotBot, robot, bot, irc, irc bot, irc robot, '
+    if additional_keywords:
+        additional_keywords = ', '.join(map(str, additional_keywords))
+        return (default_keywords + additional_keywords)
+    return (default_keywords)
+
+def total_messages(messages):
+    total_messages = 0
+    for message in messages:
+        total_messages += message.amount
+    return total_messages
+
+def shorten_number (number):
+    if len(str(number)) > 20:
+        number = str(int(number /1000000000000000000)) +"E"
+    elif len(str(number)) > 17:
+        number = str(int(number /1000000000000000)) +"P"
+    elif len(str(number)) > 14:
+        number = str(int(number /1000000000000)) +"T"
+    elif len(str(number)) > 11:
+        number = str(int(number /1000000000)) +"G"
+    elif len(str(number)) > 8:
+        number = str(int(number /1000000)) +"M"
+    elif len(str(number)) > 5:
+        number = str(int(number /1000)) + "K"
+    elif len(str(number)) > 4:
+        number = str(int(number /100)) + "h"
+    else:
+        number = int(number)
+    return str(number)

+ 107 - 0
website/rotbot/views/general.py

@@ -0,0 +1,107 @@
+from django.shortcuts import render, get_object_or_404
+from django.http import HttpResponseRedirect
+from django.urls import reverse
+from django.forms import modelformset_factory
+#from django.views import generic
+from django.contrib.auth.decorators import login_required, permission_required
+from website.settings import APPLICATION_NAME
+from rotbot.models import Network, Host, Channel, User, Message, Action, Notice, Join, Kick, CurseWord, CurseAdjective
+from rotbot.forms import CurseWordForm, CurseAdjectiveForm
+from rotbot.views.common import default_keywords, shorten_number, total_messages
+
+def index(request):
+    networks = Network.objects.all()
+    hosts = Host.objects.all()
+    channels = Channel.objects.all()
+    users = User.objects.all()
+    joins = Join.objects.all()
+    kicks = Kick.objects.all()
+    messages = Message.objects.all()
+    actions = Action.objects.all()
+    notices = Notice.objects.all()
+    cursewords = CurseWord.objects.all()
+
+    context = {
+        'title': 'RotBot',
+        'icon': 'hashtag',
+        'description': 'Index of RotBot',
+        'keywords': default_keywords('index'),
+        'total_networks':shorten_number(networks.count()),
+        'total_hosts':shorten_number(hosts.count()),
+        'total_channels':shorten_number(channels.count()),
+        'total_users':shorten_number(users.count()),
+        'total_joins':shorten_number(joins.count()),
+        'total_kicks':shorten_number(kicks.count()),
+        'total_messages':shorten_number(messages.count()),
+        'total_actions':shorten_number(actions.count()),
+        'total_notices':shorten_number(notices.count()),
+        'total_cursewords':shorten_number(cursewords.count()),
+    }
+    return render(request, 'rotbot/index.html', context)
+
+def user(request, user_slug):
+    user = get_object_or_404(User, slug=user_slug)
+    network = Network.objects.get(id=user.network.id)
+    messages = Message.objects.filter(user=user)
+    actions = Action.objects.filter(user=user)
+    notices = Notice.objects.filter(user=user)
+    joins = Join.objects.filter(user=user)
+    kicks = Kick.objects.filter(kicker=user)
+    kicked = Kick.objects.filter(kicked=user)
+
+    context = {
+        'parent_title': network.name,
+        'parent_url': reverse('rotbot:network', args=(network.slug,)),
+        'parent_icon': 'sitemap',
+        'title': user.name,
+        'icon': 'hashtag',
+        'description': 'Details of user %s on network %s' % (user.name, network.name),
+        'keywords': default_keywords() + '%s, %s' % (network.name, user.name),
+        'user': user,
+        'network': network,
+        'total_messages': shorten_number(total_messages(messages)),
+        'total_actions': shorten_number(total_messages(actions)),
+        'total_notices': shorten_number(total_messages(notices)),
+        'total_joins': shorten_number(joins.count()),
+        'total_kicks': shorten_number(kicked.count()),
+        'total_kicked': shorten_number(kicked.count()),
+    }
+    return render(request, 'rotbot/user.html', context)
+
+@login_required
+@permission_required('rotbot.add_curseword', raise_exception=True)
+def add_curseword(request):
+    CurseWordFormSet = modelformset_factory(CurseWord, form=CurseWordForm, extra=4)
+    CurseAdjectiveFormSet = modelformset_factory(CurseAdjective, form=CurseAdjectiveForm, extra=4)
+    if request.method == 'POST':
+        curseword_formset = CurseWordFormSet(request.POST)
+        curseadjective_formset = CurseAdjectiveFormSet(request.POST)
+        if curseword_formset.is_valid() and curseadjective_formset.is_valid():
+            new_cursewords = curseword_formset.save(commit=False)
+            new_curseadjectives = curseadjective_formset.save(commit=False)
+            for record in new_cursewords:
+                record.word = record.word.lower()
+                record.save()
+            for record in new_curseadjectives:
+                record.word = record.word.lower()
+                record.save()
+            return HttpResponseRedirect(reverse('rotbot:networks'))
+        else:
+            curseword_formset = CurseWordFormSet(queryset=CurseWord.objects.none())
+            curseadjective_formset = CurseWordFormSet(queryset=CurseAdjective.objects.none())
+    else:   # Not a POST request.
+        curseword_formset = CurseWordFormSet(queryset=CurseWord.objects.none())
+        curseadjective_formset = CurseAdjectiveFormSet(queryset=CurseAdjective.objects.none())
+
+    context = {
+        'parent_title': 'RotBot',
+        'parent_url': reverse('rotbot:networks'),
+        'parent_icon': 'sitemap',
+        'title': 'Add curseword',
+        'icon': 'book dead',
+        'description': 'Add a curseword to RotBot\'s vocabulary.',
+        'keywords': default_keywords(),
+        'curseword_formset': curseword_formset,
+        'curseadjective_formset': curseadjective_formset,
+    }
+    return render(request, 'rotbot/add_curseword.html', context)

+ 149 - 0
website/rotbot/views/network.py

@@ -0,0 +1,149 @@
+from django.shortcuts import render, get_object_or_404
+from django.http import HttpResponseRedirect
+from django.urls import reverse
+from django.forms import inlineformset_factory
+from django.contrib.auth.decorators import login_required, permission_required
+from website.settings import APPLICATION_NAME
+from rotbot.models import Network, Host, Channel, User, Message, Action, Notice, Join, Kick, Owner
+from rotbot.forms import NetworkForm, HostForm, OwnerForm
+from .common import default_keywords, shorten_number, total_messages
+
+def networks(request):
+    networks = Network.objects.all()
+    hosts = Host.objects.all()
+    channels = Channel.objects.all()
+    users = User.objects.all()
+    messages = Message.objects.all()
+    actions = Action.objects.all()
+    notices = Notice.objects.all()
+    joins = Join.objects.all()
+    kicks = Kick.objects.all()
+
+    context = {
+        'title': 'RotBot',
+        'icon': 'robot',
+        'description': 'Index of RotBot, the IRC robot.',
+        'keywords': default_keywords('index'),
+        'networks': networks,
+        'total_networks': shorten_number(networks.count()),
+        'total_hosts': shorten_number(hosts.count()),
+        'total_chanels': shorten_number(channels.count()),
+        'total_users': shorten_number(users.count()),
+        'total_messages': shorten_number(total_messages(messages)),
+        'total_actions': shorten_number(total_messages(actions)),
+        'total_notices': shorten_number(total_messages(notices)),
+        'total_joins': shorten_number(joins.count()),
+        'total_kicks': shorten_number(kicks.count()),
+    }
+    return render(request, 'rotbot/networks.html', context)
+
+
+def network(request, network_slug):
+    network = get_object_or_404(Network, slug=network_slug)
+    hosts = Host.objects.filter(network=network)
+    channels = Channel.objects.filter(network=network)
+    users = User.objects.filter(network=network)
+    messages = Message.objects.filter(network=network)
+    actions = Action.objects.filter(network=network)
+    notices = Notice.objects.filter(network=network)
+    joins = Join.objects.all()
+    kicks = Kick.objects.all()
+
+    for host in hosts:
+        host.connection_fails = host.connection_attempts - host.connection_succeeds
+
+    context = {
+        'parent_title': 'Networks',
+        'parent_url': reverse('rotbot:networks'),
+        'parent_icon': 'robot',
+        'title': network.name,
+        'icon': 'sitemap',
+        'description': 'Details of ' + network.name,
+        'keywords': default_keywords() + 'network.name, display, details',
+        'network': network,
+        'hosts': hosts,
+        'channels': channels,
+        'total_hosts': shorten_number(hosts.count()),
+        'total_chanels': shorten_number(channels.count()),
+        'total_users': shorten_number(users.count()),
+        'total_messages': shorten_number(total_messages(messages)),
+        'total_actions': shorten_number(total_messages(actions)),
+        'total_notices': shorten_number(total_messages(notices)),
+        'total_joins': shorten_number(joins.count()),
+        'total_kicks': shorten_number(kicks.count()),
+    }
+    return render(request, 'rotbot/network.html', context)
+
+
+@login_required
+@permission_required('rotbot.change_network', raise_exception=True)
+def edit_network(request, network_slug):
+    network = get_object_or_404(Network, slug=network_slug)
+    title = network.name
+    HostFormSet = inlineformset_factory(Network, Host, form=HostForm)
+    OwnerFormSet = inlineformset_factory(Network, Owner, form=OwnerForm)
+    if request.method == 'POST':
+        form = NetworkForm(request.POST, instance=network)
+        hostformset = HostFormSet(request.POST, instance=network)
+        ownerformset = HostFormSet(request.POST, instance=network)
+        if form.is_valid() and hostformset.is_valid() and ownerformset.is_valid():
+            form.save()
+            hostformset.save()
+            ownerformset.save()
+            return HttpResponseRedirect(reverse('rotbot:network', args=(network.slug,)))
+    else:
+        form = NetworkForm(instance=network)
+        hostformset = HostFormSet(instance=network)
+        ownerformset = OwnerFormSet(instance=network)
+    context = {
+        'parent_title': 'Networks',
+        'parent_url': reverse('rotbot:networks'),
+        'parent_icon': 'robot',
+        'title': title,
+        'icon': 'sitemap',
+        'description': 'Edit ' + title,
+        'keywords': default_keywords((title, 'edit')),
+        'network_slug': network_slug,
+        'form': form,
+        'hostformset': hostformset,
+        'ownerformset': ownerformset,
+    }
+    return render(request, 'rotbot/edit_network.html', context)
+
+@login_required
+@permission_required('rotbot.add_network', raise_exception=True)
+def add_network(request):
+    HostFormSet = inlineformset_factory(Network, Host, form=HostForm)
+    if request.method == 'POST':
+        form = NetworkForm(request.POST)
+        if form.is_valid():
+            network = form.save()
+            formset = HostFormSet(request.POST, instance=network)
+            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())
+    context = {
+        'parent_title': 'Networks',
+        'parent_url': reverse('rotbot:networks'),
+        'parent_icon': 'robot',
+        'title': 'Add network',
+        'icon': 'sitemap',
+        'description': 'Add new irc network to ' + APPLICATION_NAME,
+        'keywords': default_keywords() + 'add, add network',
+        'form': form,
+        'hostformset': formset,
+    }
+    return render(request, 'rotbot/add_network.html', context)
+
+
+@login_required
+@permission_required('rotbot.change_network', raise_exception=True)
+def delete_network(request, network_slug):
+    network = get_object_or_404(Network, slug=network_slug)
+    network.delete()
+    return HttpResponseRedirect(reverse('rotbot:networks'))