#!/usr/bin/env python3 import znc import pymssql import re from datetime import datetime from contextlib import closing class mssql(znc.Module): module_types = [znc.CModInfo.GlobalModule] description = "Log all channels to a Microsoft SQL Server database" has_args = False sql_server = "db.spd.internal" sql_user = "_srv_znc" sql_password = "zncznc" sql_database = "znc" # def OnLoad(self, args, message): # self.PutModule("Loading MSSQL...") # return True def OnOp(self, user, target_user, channel, noChange): self.insert("OP", channel.GetName(), user.GetHost(), user.GetNick(), user.GetIdent(), None, datetime.now(), target_user.GetNick(), None, str(self.GetNetwork())) return True def OnPrivNotice(self, user, message): self.insert("NOTICE", None, user.GetHost(), user.GetNick(), user.GetIdent(), None, datetime.now(), None, message.s, str(self.GetNetwork())) return True def OnDeop(self, user, target_user, channel, noChange): self.insert("DEOP", channel.GetName(), user.GetHost(), user.GetNick(), user.GetIdent(), None, datetime.now(), target_user.GetNick(), None, str(self.GetNetwork())) return True def OnVoice(self, user, target_user, channel, noChange): self.insert("VOICE", channel.GetName(), user.GetHost(), user.GetNick(), user.GetIdent(), None, datetime.now(), target_user.GetNick(), None, str(self.GetNetwork())) return True def OnDevoice(self, user, target_user, channel, noChange): self.insert("DEVOICE", channel.GetName(), user.GetHost(), user.GetNick(), user.GetIdent(), None, datetime.now(), target_user.GetNick(), None, str(self.GetNetwork())) return True def OnMode(self, user, channel, mode, argument, added, noChange): mode = chr(mode) code = "MODE" if mode == "b": code = "BAN" if added else "UNBAN" else: sign = "+" if added else "-" argument = "{0}{1} {2}".format(sign, mode, argument) self.insert(code, channel.GetName(), user.GetHost(), user.GetNick(), user.GetIdent(), None, datetime.now(), None, argument, str(self.GetNetwork())) return True def OnQuit(self, user, message, channels): for channel in channels: self.insert("QUIT", channel.GetName(), user.GetHost(), user.GetNick(), user.GetIdent(), None, datetime.now(), None, message, str(self.GetNetwork())) return True def OnNick(self, user, new_nick, channels): for channel in channels: self.insert("NICK", channel.GetName(), user.GetHost(), user.GetNick(), user.GetIdent(), None, datetime.now(), new_nick, None, str(self.GetNetwork())) return True def OnKick(self, user, target_nick, channel, message): self.insert("KICK", channel.GetName(), user.GetHost(), user.GetNick(), user.GetIdent(), None, datetime.now(), target_nick, message, str(self.GetNetwork())) return True def OnJoin(self, user, channel): self.insert("JOIN", channel.GetName(), user.GetHost(), user.GetNick(), user.GetIdent(), None, datetime.now(), None, None, str(self.GetNetwork())) return True def OnPart(self, user, channel, message): self.insert("PART", channel.GetName(), user.GetHost(), user.GetNick(), user.GetIdent(), None, datetime.now(), None, message, str(self.GetNetwork())) return True def OnUserAction (self, target, message): (channel, user) = self.resolveTarget(target) if channel == None: return True return self.OnChanAction(user, channel, message) def OnPrivMsg(self, nick, message): self.insert("MSG", "", nick.GetHost(), nick.GetNick(), nick.GetIdent(), None, datetime.now(), None, message.s, str(self.GetNetwork())) return True def OnUserMsg (self, target, message): (channel, user) = self.resolveTarget(target) if channel == None: return True return self.OnChanMsg(user, channel, message) def OnChanAction(self, user, channel, message): self.insert("ME", channel.GetName(), user.GetHost(), user.GetNick(), user.GetIdent(), None, datetime.now(), None, message.s, str(self.GetNetwork())) return True def OnChanMsg(self, user, channel, message): self.insert("SAY", channel.GetName(), user.GetHost(), user.GetNick(), user.GetIdent(), self.findMode(channel, user), datetime.now(), None, message.s, str(self.GetNetwork())) return True def OnTopic(self, user, channel, message): self.insert("TOPIC", channel.GetName(), user.GetHost(), user.GetNick(), user.GetIdent(), None, datetime.now(), None, message.s, str(self.GetNetwork())) return True # def OnLoad(self, args, message): # match = re.search("(.*?):(.*?)@(.*)", args) # if match: # self.username = match.group(1) # self.password = match.group(2) # self.host = match.group(3) # return True # else: # return False def findMode(self, channel, user): realUser = channel.FindNick(user.GetNick()) if not realUser: return None return str(realUser.GetPermChar()) def resolveTarget(self, target): target = target.s channel = self.GetNetwork().FindChan(target) if channel == None: return (None, None) user = self.GetNetwork().GetIRCNick() return (channel, user) def colourstrip(self, data): find = data.find('\x03') while find > -1: done = False data = data[0:find] + data[find+1:] if len(data) <= find+1: done = True try: assert not done assert int(data[find]) while True: assert int(data[find]) data = data[0:find] + data[find+1:] except: if not done: if data[find] != ',': done = True if (not done) and (len(data) > find+1) and (data[find] == ','): try: assert not done assert int(data[find+1]) data = data[0:find] + data[find+1:] data = data[0:find] + data[find+1:] except: done = True try: assert not done while True: assert int(data[find]) data = data[0:find] + data[find+1:] except: pass find = data.find('\x03') data = data.replace('\x02','') data = data.replace('\x1d','') data = data.replace('\x1f','') data = data.replace('\x16','') data = data.replace('\x0f','') return data def insert(self, code, channel, host, user, ident, user_mode, date, target_user, message, network): # self.PutModule("Inserting row...") regex = re.compile("\x1f|\x02|\x03(?:\d{1,2}(?:,\d{1,2})?)?", re.UNICODE) try: with closing(pymssql.connect ("db.spd.internal", "_srv_znc", "znc", "znc")) as conn: with closing(conn.cursor()) as cursor: # sql = "INSERT INTO log (date, code, network, channel, host, [user], ident, mode, target, message) VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)" sql = "EXEC dbo.add_log (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" if message is not None: # message = message.encode('utf-8').decode('utf-8', 'replace') message = regex.sub("", message) message = self.colourstrip(message) znc_user = self.GetUser().GetUserName() if self.GetUser() is not None else None params = (znc_user, str(date), code, network, channel, host, user, ident, user_mode, target_user, message) # cursor.execute (sql, params) cursor.callproc("add_log", params) conn.commit() conn.close() except Exception as e: self.PutModule("Could not save {0} to database caused by: {1} - {2}".format(code, type(e), str(e)))