From 037052d1c7b1db69f41a142b3b6cf4d2497b1d5a Mon Sep 17 00:00:00 2001 From: Kris Lamoureux Date: Mon, 29 Feb 2016 05:05:46 -0500 Subject: [PATCH] Added logging functionality Added logging of all sent/received data and errors. Passwords are censored from the terminal and log file. Fixed case-sensitivity of the Control nick and channel. --- Flea.py | 23 +++++++++++++-- core/irclib.py | 20 +++++++++++-- core/main.py | 64 +++++++++++++++++++++++++++++++---------- plugins/Control/main.py | 46 +++++++++++++++++++---------- settings.conf | 1 + 5 files changed, 118 insertions(+), 36 deletions(-) diff --git a/Flea.py b/Flea.py index 46dd8f4..8b95b63 100644 --- a/Flea.py +++ b/Flea.py @@ -15,10 +15,27 @@ # along with this program. If not, see import traceback +import sys try: import core.main -except: - traceback.print_exc() - input() + +except KeyboardInterrupt: + print "\nGoodbye!" + sys.exit() + +except SystemExit: + raise + +except: + # Print error, save it to error.txt + # then wait to be closed. + traceback.print_exc() + + errorfile = open("error.txt", 'a') + traceback.print_exc(None, errorfile) + errorfile.close() + + raw_input() + sys.exit(0) diff --git a/core/irclib.py b/core/irclib.py index 820e170..dbf15cf 100644 --- a/core/irclib.py +++ b/core/irclib.py @@ -16,10 +16,12 @@ # Built-in to Python 2.7 import socket +import re class irc: - debug = False + debug = False + log = False config = {} pack = {} sock = socket.socket() @@ -79,10 +81,22 @@ class irc: return packet - # Basic message function to send any data + # Basic message function to send and log any data def msg(self, message): self.sock.send(message+"\r\n") - if self.debug: print ">>> "+message + + # Match messages containing private information + # then censors it to output to the terminal and log + pattern = r"^PRIVMSG NickServ :IDENTIFY *" + if re.search(pattern, message): + message = "PRIVMSG NickServ :IDENTIFY ****" + + output = ">>> "+message + + if self.debug: + print output + if self.log: + self.log.write(output+"\n") def User(self, nick, mode, unused, owner): self.msg("USER "+nick+' '+mode+' '+unused+" :"+owner) diff --git a/core/main.py b/core/main.py index e333db2..4696757 100644 --- a/core/main.py +++ b/core/main.py @@ -58,7 +58,14 @@ class ImportRollback: __builtin__.__import__ = self.newImport -def PluginsImport(): +# Print and log to logfile +def prntlog(message, logfile): + print message + if logfile: + logfile.write(message+"\n") + + +def PluginsImport(log=False): # Get root of Flea current = os.getcwd() # Path to /plugins/ under /Flea/ @@ -76,7 +83,7 @@ def PluginsImport(): # Only import directory plugins (no single files) if os.path.isdir(plugins+item): - print "[Plugins] Initializing "+item + prntlog("[Plugins] Initializing "+item, log) plugin = __import__(item+".main") plugin_list.append(plugin) @@ -87,7 +94,6 @@ def PluginsImport(): return plugin_list - def main(): # Create irclib irc object @@ -96,14 +102,22 @@ def main(): # Parse main settings.conf file irc.config = cfgParser("settings.conf") + # If logging is enabled, open log file. + if irc.config["logging"]: + log = open("log.txt", 'a') + irc.log = log + else: + log = False + # Keep track of modules for a rollback importctrl = ImportRollback() # Import /plugins/ if irc.config["plugins"]: - plugins = PluginsImport() + plugins = PluginsImport(log) + if not plugins: - print "[Plugins] Failed to load." + prntlog("[Plugins] Failed to load.", log) # Set debug to true/false inside irc() object irc.debug = irc.config["debug"] @@ -115,15 +129,18 @@ def main(): irc.sock = ssl.wrap_socket(irc.sock) # Connect to IRC server - irc.sock.connect((irc.config["host"], irc.config["port"])) - print "Connecting to "+irc.config["host"]+':'+str(irc.config["port"]) + host = irc.config["host"] + port = irc.config["port"] + + irc.sock.connect((host, port)) + prntlog("Connecting to "+host+':'+str(port), log) # Display SSL information to the user ssl_info = irc.sock.cipher() if ssl_info != None: - print "[SSL] Cipher: "+ssl_info[0] - print "[SSL] Version: "+ssl_info[1] - print "[SSL] Bits: "+str(ssl_info[2]) + prntlog("[SSL] Cipher: "+ssl_info[0], log) + prntlog("[SSL] Version: "+ssl_info[1], log) + prntlog("[SSL] Bits: "+str(ssl_info[2]), log) # Send User/Nick message to establish user on the server irc.User(irc.config["ident"], irc.config["mode"], @@ -145,8 +162,9 @@ def main(): # If no incoming data exists then connection has closed if len(tmpdata) == 0: - input("Connection closed.") - sys.exit() + print "Connection closed." + raw_input() + sys.exit(0) # Split data to easily deal with it data = tmpdata.split("\r\n") @@ -157,14 +175,17 @@ def main(): # Ignore empty lines if len(line) > 0: - # Print line, parse it and respond - print line + # Print/log line, parse it and respond + prntlog(line, log) irc.pack = irc.Parser(line) # Run all plugins main() function + wait = '' if irc.config["plugins"]: for plugin in plugins: - plugin.main.main(irc) + wait = plugin.main.main(irc) + if wait == "QUIT": + break # Ping Pong, keep the connection alive. if irc.pack["cmd"] == "PING": @@ -183,5 +204,18 @@ def main(): irc.Identify(irc.config["password"]) irc.Join(irc.config["channel"]) + if log: log.flush() + + # Wait for QUIT to be returned from any plugin's main() function + if wait == "QUIT": + # Quit, close connection and logfile. + irc.Quit("Fleabot https://github.com/Kris619/Flea") + irc.sock.close() + if log: log.close() + + print "Press the [ENTER] key to close." + raw_input() + sys.exit(0) + main() diff --git a/plugins/Control/main.py b/plugins/Control/main.py index 72cbdad..e867123 100644 --- a/plugins/Control/main.py +++ b/plugins/Control/main.py @@ -1,3 +1,19 @@ +# An IRC bot named Flea +# Copyright (C) 2016 Kris Lamoureux + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see + import re # Init @@ -11,7 +27,7 @@ def chkcontrol(irc): global access global queue - nick = irc.pack["nick"] + nick = irc.pack["nick"].lower() for person in access: if nick == person: @@ -33,13 +49,14 @@ def main(irc): # Set control channel controlchan = irc.config["channel"] + controlusr = irc.config["control"] # NOTICEs are used to "control" Flea if irc.pack["cmd"] == "NOTICE": # Nick must match the nick in settings # e.g. control = Kris - if irc.pack["nick"] == irc.config["control"]: + if irc.pack["nick"].lower() == controlusr.lower(): # Split message by spaces, for functions with # required parameters. e.g. " " @@ -67,13 +84,10 @@ def main(irc): if chkcontrol(irc): irc.Notice("You already have access to Control.", irc.pack["nick"]) - else: - noaccess(irc) if irc.pack["text"] == "quit": if chkcontrol(irc): - irc.Quit("Fleabot https://github.com/Kris619/Flea") - quit() + return "QUIT" else: noaccess(irc) @@ -81,32 +95,34 @@ def main(irc): elif irc.pack["cmd"] == "307": if irc.pack["text"] == "is identified for this nick": + usernick = irc.pack["params"][1] + # Check if Nick is in the queue for access list - if queue.count(irc.pack["params"][1]) == 1: + if queue.count(usernick) == 1: # Remove Nick from queue - queue.remove(irc.pack["params"][1]) + queue.remove(usernick) # Check if user is set to control in settings # e.g. control = Kris - if irc.pack["params"][1] == irc.config["control"]: + if usernick.lower() == controlusr.lower(): # Is control user inside the control channel? - if chans[controlchan].count(irc.pack["params"][1]) == 1: + if chans[controlchan.lower()].count(usernick) == 1: # Grant access - access.append(irc.pack["params"][1]) + access.append(usernick) - print irc.pack["params"][1]+" added to Access" + print usernick+" added to Access" irc.Notice( "You have been given access to Control.", - irc.pack["params"][1]) + usernick) else: irc.Notice( "You are not in the Control channel: "+controlchan, - irc.pack["params"][1]) + usernick) # Keep track of users in every channel # "353" lists users to a channel @@ -137,7 +153,7 @@ def main(irc): if irc.pack["nick"] == irc.config["nick"]: # Create channel user list if it doesn't exist if chans.keys().count(irc.pack["text"]) == 0: - chans[irc.pack["text"]] = [] + chans[irc.pack["text"].lower()] = [irc.pack["text"]] # Another user joined else: diff --git a/settings.conf b/settings.conf index 44babd0..4756de4 100644 --- a/settings.conf +++ b/settings.conf @@ -1,4 +1,5 @@ debug = true +logging = false plugins = true host = irc.example.net