import os, re, time, base64 import supybot.utils as utils import http.server, sqlite3 host = 'http://domain.tld' port = 80 standalone = True webpath = '/bantracker' username = 'username' password = 'password' filename = '/home/botaccount/data/networkname/ChanTracker.db' channels = [] # empty to allow view of all channels recorded, otherwise restrict the views to channels # usage python server.py auth = '%s:%s' % (username,password) base64string = base64.b64encode(auth.encode('utf-8')).decode('utf-8') def timeElapsed(elapsed, short=False, leadingZeroes=False, years=True, weeks=True, days=True, hours=True, minutes=True, seconds=True): """Given seconds, returns a string with an English description of how much time as passed. leadingZeroes determines whether 0 days, 0 hours, etc. will be printed; the others determine what larger time periods should be used. """ ret = [] def Format(s, i): if i or leadingZeroes or ret: if short: ret.append('%s%s' % (i, s[0])) else: ret.append(format('%n', (i, s))) elapsed = int(elapsed) assert years or weeks or days or \ hours or minutes or seconds, 'One flag must be True' if years: (yrs, elapsed) = (elapsed // 31536000, elapsed % 31536000) Format('year', yrs) if weeks: (wks, elapsed) = (elapsed // 604800, elapsed % 604800) Format('week', wks) if days: (ds, elapsed) = (elapsed // 86400, elapsed % 86400) Format('day', ds) if hours: (hrs, elapsed) = (elapsed // 3600, elapsed % 3600) Format('hour', hrs) if minutes or seconds: (mins, secs) = (elapsed // 60, elapsed % 60) if leadingZeroes or mins: Format('minute', mins) if seconds: leadingZeroes = True Format('second', secs) if not ret: raise ValueError('Time difference not great enough to be noted.') if short: return ' '.join(ret) else: return format('%L', ret) class MyHandler(http.server.BaseHTTPRequestHandler): if not standalone: def log_request(self, *args): pass # disable logging def do_GET(self): self.page(self.path) def page(self, query): def write(subtitle, body): page = [ '', '\nBanTracker%s' % (' » %s' % subtitle if subtitle else ''), '', '', '\n' ] + body + ['\n'] self.send_response(200) self.send_header("Content-type","text/html") full = '\n'.join(page) print('HTML lines %s' % len(full)) self.send_header("Content-length",len(full)) self.end_headers() self.wfile.write(full.encode('utf-8')) if standalone: h = '%s:%s/' % (host,port) else: h = '%s/' % webpath if not query: return if query.startswith('/?username='): query = query.replace('/?','') a = query.split('&') u = p = None for item in a: aa = item.split('=') if aa[0] == 'username': u = aa[1] if aa[0] == 'password': p = aa[1] if u and p: auth = '%s:%s' % (u,p) raw = base64.b64encode(auth.encode('utf-8')).decode('utf-8') if raw != base64string: query = '' else: query = '/?hash=%s' % base64string if not query.startswith('/?hash='): subtitle = '' body = [ '
' % h, '

Username:

', '

Password:

', '', '
' ] write(subtitle, body) return query = query.replace('%3D','=') query = query.replace('/?hash=%s' % base64string,'') query = query.lstrip('&') q = '?hash=%s' % base64string query = utils.web.urlunquote(query) subtitle = '' body = [ '
', '
' % q, '
', '' % base64string, '', '', '
', '
' ] if not query: write(subtitle, body) return print(query) subtitle = query db = self._getbandb() c = db.cursor() ar = [] if query.startswith('id='): search = query.split('=')[1] si = int(search) c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE id=?""",(si,)) r = c.fetchall() if len(r): ban = r[0] (id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = ban if not channels or channel in channels: body.extend([ '

#%d

' % id, '

#%d by %s' % (id,h,q,utils.web.urlencode({'oper':oper}),oper), 'in %s:' % (h,q,channel.split('#')[1],channel), '+%s %s

' % (kind,h,q,utils.web.urlencode({'mask':mask}),mask), '

Begin at %s

' % time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(begin_at))) ]) was = float(begin_at) == float(end_at) if was: was = 'forever' else: was = timeElapsed(float(end_at) - float(begin_at)) body.append('

Original duration: %s

' % was) if not removed_at: if was != 'forever': body.append('

It will expire in %s

' % timeElapsed(float(end_at) - time.time())) else: body.extend(['

Removed after %s' % timeElapsed(float(removed_at)-float(begin_at)), 'on %s' % time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(removed_at))), 'by %s

' % (h,q,utils.web.urlencode({'removed_by':removed_by}),removed_by)]) c.execute("""SELECT full,log FROM nicks WHERE ban_id=?""",(id,)) r = c.fetchall() if len(r): body.append('

Logs

') for nick in r: (full,log) = nick body.append('

for %s

' % full) if log != '': body.append('') c.execute("""SELECT oper,at,comment FROM comments WHERE ban_id=?""",(id,)) r = c.fetchall() if len(r): body.extend(['

Comments

', '') c.close() write(subtitle, body) return elif query.startswith('channel='): search = '#'+query.split('=')[1] c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE channel=? ORDER BY id DESC""",(search,)) r = c.fetchall() if len(r): ar.extend(r) elif query.startswith('removed_by='): search = query.split('=')[1] c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE removed_by=? ORDER BY id DESC""",(search,)) r = c.fetchall() if len(r): ar.extend(r) elif query.startswith('oper='): search = query.split('=')[1] c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE oper=? ORDER BY id DESC""",(search,)) r = c.fetchall() if len(r): ar.extend(r) elif query.startswith('mask='): search = query.split('=')[1] sg = '*%s*' % search sl = '%%%s%%' % search c.execute("""SELECT ban_id,full FROM nicks WHERE full GLOB ? OR full LIKE ? OR log GLOB ? OR log LIKE ? ORDER BY ban_id DESC""",(sg,sl,sg,sl)) r = c.fetchall() L = [] a = {} if len(r): d = {} for ban in r: (id,full) = ban if id not in d: d[id] = id for id in d: c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE id=?""",(id,)) r = c.fetchall() if len(r): for ban in r: a[ban[0]] = ban c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE mask GLOB ? OR mask LIKE ? ORDER BY id DESC""",(sg,sl)) r = c.fetchall() if len(r): for ban in r: a[ban[0]] = ban if len(a): ar = [] for ban in list(a.keys()): ar.append(a[ban]) ar.sort(key=lambda x: x[0], reverse=True) elif query.startswith('search='): search = query.split('=')[1] search = search.replace('+','*') print(search) if search: if not re.match(r'^[0-9]+$', search): sg = '*%s*' % search sl = '%%%s%%' % search si = None c.execute("""SELECT ban_id,full FROM nicks WHERE full GLOB ? OR full LIKE ? OR log GLOB ? OR log LIKE ? ORDER BY ban_id DESC""",(sg,sl,sg,sl)) r = c.fetchall() else: si = int(search) r = [] L = [] a = {} if len(r): d = {} for ban in r: (id,full) = ban if id not in d: d[id] = id for id in d: c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE id=?""",(id,)) r = c.fetchall() if len(r): for ban in r: a[ban[0]] = ban if not si: c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE mask GLOB ? OR mask LIKE ? OR channel GLOB ? OR channel LIKE ? OR oper GLOB ? OR oper LIKE ? ORDER BY id DESC""",(sg,sl,sg,sl,sg,sl)) else: c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE id=?""",(si,)) r = c.fetchall() if len(r): for ban in r: a[ban[0]] = ban if not si: c.execute("""SELECT ban_id, comment FROM comments WHERE comment GLOB ? OR comment LIKE ? ORDER BY ban_id DESC""",(sg,sl)) r = c.fetchall() else: r = [] d = {} if len(r): for ban in r: (id,full) = ban d[id] = id for id in d: if id not in a: c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE id=?""",(id,)) r = c.fetchall() if len(r): for ban in r: a[ban[0]] = ban if len(a): ar = [] for ban in list(a.keys()): ar.append(a[ban]) ar.sort(key=lambda x: x[0], reverse=True) if len(ar): print('Found %s results' % len(ar)) body.extend([ '

Results %s

' % search, '
', '', '' ]) for ban in ar: (id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = ban if not channels or channel in channels: s = time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(begin_at))) body.extend([ '', '' % (h,q,id,id), '' % (h,q,channel.split('#')[1],channel), '' % (h,q,utils.web.urlencode({'oper':oper}),oper), '' % kind, '' % (h,q,utils.web.urlencode({'mask':mask}),mask), '' % s ]) if end_at and end_at != begin_at: s = time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(end_at))) body.append('' % s) else: body.append('') if removed_at: s = time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(removed_at))) body.append('' % s) else: body.append('') if removed_by: body.append('' % (h,q,utils.web.urlencode({'removed_by':removed_by}),removed_by)) else: body.append('') # affected = '' # try: # c.execute("""SELECT full, log FROM nicks WHERE ban_id=?""",(id,)) # affected = len(c.fetchall()) # except: # affected = '' # body.append('' % affected) body.append('') body.extend(['', '
IDChannelOperatorTypeMaskBegin dateEnd dateRemovedRemoved by
%d%s%s+%s%s%s%s%s%s%s
']) else: body.append('

Nothing found

') c.close() write(subtitle, body) def _getbandb(self): if os.path.exists(filename): db = sqlite3.connect(filename,timeout=10) return db db = sqlite3.connect(filename) c = db.cursor() c.execute("""CREATE TABLE bans ( id INTEGER PRIMARY KEY, channel VARCHAR(100) NOT NULL, oper VARCHAR(1000) NOT NULL, kind VARCHAR(1) NOT NULL, mask VARCHAR(1000) NOT NULL, begin_at TIMESTAMP NOT NULL, end_at TIMESTAMP NOT NULL, removed_at TIMESTAMP, removed_by VARCHAR(1000) )""") c.execute("""CREATE TABLE nicks ( ban_id INTEGER, ban VARCHAR(1000) NOT NULL, full VARCHAR(1000) NOT NULL, log TEXT NOT NULL )""") c.execute("""CREATE TABLE comments ( ban_id INTEGER, oper VARCHAR(1000) NOT NULL, at TIMESTAMP NOT NULL, comment TEXT NOT NULL )""") db.commit() return db def httpd(handler_class=MyHandler, server_address=('', port)): srvr = http.server.HTTPServer(server_address, handler_class) srvr.serve_forever() if __name__ == "__main__": httpd()