diff -uNr a/logotron/MANIFEST.TXT b/logotron/MANIFEST.TXT --- a/logotron/MANIFEST.TXT 82b2aa673901d86655d3977cf22cbced5e8ec8baab2af28e105edb9d60dcb397ac1023727ae5fcc4c59f7b41a292fbe6a2ea5c7e8207695a10c406ecff5a99e8 +++ b/logotron/MANIFEST.TXT 30e3a359b4fad69e63d5c11098cb3292a4166a644488099744130e4eb35762ec6fad517b3faf5d06818ab18303c92afc7cafa3b7e17d5159f83010d44a465262 @@ -19,3 +19,4 @@ 641484 navbar_date_auto asciilifeform "Automatically skip empty days in navbar. Handle 'dawn of time' case." 641773 searchpg_oldlinks asciilifeform "Paginated search; automatic replacement of ancient logger link targets; fix for unicode barf in eat_dump.py." 641794 aug2020_errata asciilifeform "Stylistic fixes for previous patch." +684804 frontend_updates billymg "Changes to HTML/CSS to allow for easier theming. Added config knobs for custom log root and CSS file." diff -uNr a/logotron/README.txt b/logotron/README.txt --- a/logotron/README.txt 892c1058be06d507d51083d944192c163494aaf861c2d68b413dcbf2e6561a79b54ec5530993c4387389f4bfe258aa1c93f8352491ce64d8f8aa1f756c02354c +++ b/logotron/README.txt 904c7dda848f33b172a5541f633edbbf425edf7d8bf9c1c9bc257d8033de2eeff3742e8d813dd735e34e4a9249d7df4b4ef3fe3c3d0f1143b25172cb0e2c27d6 @@ -7,7 +7,14 @@ (2) 'flask' lib for (1). (3) 'psycopg2' lib for (1). (4) 'postgres' (9 or 10). -(5) A WWW server that knows how to proxy. +(5) A WWW server that can serve WSGI applications (e.g. Apache with mod_wsgi installed). + - If installing mod_wsgi for the first time be sure to set your target + python version to 2.7 (/etc/portage/make.conf in gentoo/dulap systems) + + +#################### +DB Setup +#################### To use the kit, you will first need to create a user and DB, e.g.: @@ -17,7 +24,7 @@ create user nsabot createdb; alter role nsabot superuser; create database nsalog; -grant all privileges on database nsalog to nsabot ; +grant all privileges on database nsalog to nsabot; ... you can take 'super' away from this user after 1st run, it is needed in order to let him load the pg_trgm indexer @@ -26,6 +33,11 @@ Next, run 'init_db.sh' (alter the constants to match the names of your postgres user and the DB), this creates the schema. + +#################### +Importing Logs +#################### + Then see 'eat.sh' and the 'eat_dump.py' it uses, re how to fill your log archive DB. 'eat_dump.py' eats in Phf's classical format, e.g.: @@ -41,137 +53,55 @@ currently capable of eating config file. Set these to your DB and PG user. -Now, adjust the constants in 'nsabot.conf' (rename per taste) -to specify your IRC params, name of bot, host at which www -logger will reside, and other knob values. - -Adjust the three 'flask' templates in 'templates' subdir to -give the desired look and feel for the www end. Currently we are using -Phf's classic style sheet, with minor modifications. - -'reader.py' takes one mandatory command-line argument: full path -to the config above. Same for 'bot.py', which is the IRC bot. - -Run these via e.g. nohup ./bot.py & ; nohup ./reader.py & -and let your proxying WWW server know how to reach the latter's port. - -For bot.py you will need a registered nick on fleanode (or wherever -it is used.) There are no fleanode-specific hacks in the bot, ergo -it can be stood up behind ZNC (although this has not been tested.) - -Certain important features are presently unimplemented, in no order: -(1) Backlinkage. -(2) Search result pagination. -(3) Double-quoted search terms. -(4) Paste archiving. -(5) Multi-headed IRC bot for weather resistance. -(6) Informative eggogology for bot commands. -(7) Automatic synchronization with mirrors - -A ZNC log eater is also required, to properly fill in the archives. -This is not yet available at the time of this writing. - -############### -Aug. 11 Update: -############### - -(1) Multi-line selection is implemented. - -(2) User may request export of raw log lines in Phf-classical format. - E.g. : - http://logs.nosuchlabs.com/log-raw/trilema?istart=1925000&iend=1925500 +See subdir 'logconverters' for scripts to convert ZNC and irssi log formats to Phf's format -############### -Aug. 16 Update: -############### - -(1) Bot: added Phf's algo for uniturd processing ('fallback to latin-1'). -(2) Bot: May leave IRC password blank in config for use with unregged bots -(3) WWW: 'Checkerboard' colouring; footer links. -(4) WWW: Links in log lines now open in new browser tab (a la Phf's log) -(5) WWW: Can now be loaded 'in reverse gear', http://...../log?rev=1 -(6) WWW: 'Random' knob, takes reader to an arbitrary moment in past log. - -############### -Sep. 28 Update: -############### - -(1) Bot: incorporated reconnector fixes. -(2) Bot: 'seen', 'seen-all', and 'version' knobs implemented. -(3) WWW: up/down arrows for mouse-driven operation on pocket comps -(4) WWW: double-quotes prevent breakup of search query - (Note: still needs improvement to force matching on word boundaries) -(5) WWW: demarkation of "action" lines -(6) WWW: Raw 'Tape" knob: - -e.g. http://logs.nosuchlabs.com/tape?istart=1937934&iend=1937941 -returns: - -trilema;1937934;1569444908;asciilifeform;achtung panzers! piz pipe down ?! -trilema;1937935;1569444920;diana_coman;seems so -trilema;1937936;1569444957;asciilifeform;paging BingoBoingo ! -trilema;1937937;1569444974;diana_coman;well, apparetly he was connected from piz too, lol -trilema;1937938;1569444982;diana_coman;ossasepia.com (pizarro) is down too, yes -trilema;1937939;1569445010;asciilifeform;dulap unreachable for 1st time since year+ ago when bb elbowed the mains cord -trilema;1937940;1569445073;diana_coman;apparently back on -trilema;1937941;1569445079;diana_coman;wb BingoBoingo ! - -Still needs a variant of 'eater' that will eat these. - -############### -Oct. 4 Update: -############### - -(1) WWW: Removed ill-conceived 'Tape' knob. -(2) Bot: Added silent-disconnection detector. - -Now requires the following value in bot config e.g.: +#################### +Configuration +#################### -[irc] -disc_t = 180 +Now, adjust the constants in 'nsabot.conf' (rename per taste) +to specify your IRC params, name of bot, host at which www +logger will reside, and other knob values. -This represents the currently-recommended interval: 3 minutes. -If inactivity on the incoming line (incl. pingism) exceeds this interval, -a disconnection is forced. The normal reconnection routine will execute. +Set css_file in the conf file to point to your desired CSS file in 'static' subdir. +Adjust the 'jinja' templates in 'templates' subdir to give the desired look +and feel for the www end IF your CSS changes alone are not sufficient. +Currently we are using Phf's classic style sheet, with minor modifications. -To disable forced disconnects, set disc_t to zero. #################### -Jul. 2020 Update #1: +Running the WWW #################### -(1) Hide by default chans where no activity in days_hide day (set in config.) -(2) Added 'join as guest' link (via 'kiwi' wwwtronic IRC client) -(3) Example Apache-WSGI configs. - -#################### -Jul. 2020 Update #2: -#################### +'reader.py' takes one mandatory argument: full path to the config above. +- When running as a WSGI module this argument needs to be hardcoded in 'reader.py' +- When running locally pass in as a single command-line argument, + e.g. `./reader.py path/to/config` +- Optionally, when running locally, set FLASK_ENV=development to allow for + auto-reloading on file changes (see Flask documentation for more info). -(1) Fix unclosed 'table' tag from earlier. -(2) 'Search all chans' button. #################### -Jul. 2020 Update #3: +Running the Bot #################### -(1) Automatically skip empty days in date navigation bar. At the same time, handle empty days correctly when these are requested. -(2) Handle 'dawn of time' case for any given channel, to avoid infinite loop insanity from search engines. +Run bot.by and pass in the path to the same config file as a single command-line +argument, e.g. `nohup ./bot.py path/to/config &` -##################### -Aug. 2020 Update #1 : -##################### - -(1) Implemented fetch-by-channel-and-index (i.e. /ilog/chan/lineindex links). -(2) Automatically adjust link targets of #ba and 'btcbase' log citations to point to this logger, using the syntax in (1). Original text of link is preserved. Dates are handled correctly. -(3) Search results now properly paginated, with working 'back' and 'forward' buttons. -(4) Fixed ancient unicode bug in eat_dump.py. +For bot.py you will need a registered nick on fleanode (or wherever +it is used.) There are no fleanode-specific hacks in the bot, ergo +it can be stood up behind ZNC (although this has not been tested.) -##################### -Aug. 2020 Update #2 : -##################### -(1) Additional 'back' and 'forward' buttons at the bottom of search result pages. -(2) Fix era1/2 link diddler regexps to handle all (AFAIK) known variants correctly. +#################### +Not Yet Implemented +#################### +Certain important features are presently unimplemented, in no order: +(1) Backlinkage. +(3) Double-quoted search terms. +(4) Paste archiving. +(5) Multi-headed IRC bot for weather resistance. +(6) Informative eggogology for bot commands. +(7) Automatic synchronization with mirrors diff -uNr a/logotron/apacheism/example.vhost b/logotron/apacheism/example.vhost --- a/logotron/apacheism/example.vhost 14d7ac73f3c61335c9bb7ae63f70401b2342e82091d600a40653c4a86cbae5d6bdf8619a377a85d48d02ebb6b3f0bd1563751319bd02d4786c7ec6d4844de27a +++ b/logotron/apacheism/example.vhost 801c95ba92d8d9f576a2eb6076742f9b6198a9d7973d7e07b0b72f2e513e65c3fd946fd8d7f1acbdc6e783bbe31b699e5c5ea5ba4e10a7740bdb353333e51696 @@ -1,5 +1,6 @@ Listen 80 +# Path where your python libraries live. Check permissions to ensure it is accessible by apache WSGIPythonPath /home/yourbotuser/logger/ @@ -12,11 +13,11 @@ WSGIDaemonProcess yourbotuser user=yourbotuser group=apache threads=16 WSGIProcessGroup %{GLOBAL} - + WSGIScriptAlias / /home/yourbotuser/logger/yourbotuser.wsgi - + Alias /static/ /home/yourbotuser/logger/static/ - + Require all granted diff -uNr a/logotron/bot.py b/logotron/bot.py --- a/logotron/bot.py 80d24124b12055c919f68583a83445477d13680db62960b9ac57a6d7ca066f52973e9fc1bc3f80ecf6b541d0784cfbfa10195792f2a9d786c57143720584f88a +++ b/logotron/bot.py d33d6b379dc88808fd72e9579d23818b7a2611d24f48e07ed4eba2dcdc97e228d7f45c34f939fc282146ed3e89f320d709cf023711c9dbb0394dfd39b36dcf4c @@ -14,7 +14,7 @@ ############################################################################## # Version. If changing this program, always set this to same # as in MANIFEST -Ver = 597858 +Ver = 684804 ############################################################################## @@ -74,6 +74,7 @@ DB_DEBUG = cfg.get("db", "db_debug") # Logism: Base_URL = cfg.get("logotron", "base_url") + App_Root = cfg.get("logotron", "app_root") Era = int(cfg.get("logotron", "era")) NewChan_Idx = int(cfg.get("logotron", "newchan_idx")) Src_URL = cfg.get("logotron", "src_url") @@ -142,7 +143,7 @@ def init_socket(): global sock sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - + # Disable Nagle's algorithm for transmit operations sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) # Disable Nagle's algorithm for receive operation, Linux-only @@ -150,7 +151,7 @@ sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_QUICKACK, 1) except Exception as e: logging.warning(e) - + connected = False def deinit_socket(): @@ -250,32 +251,32 @@ global time_last_conn global time_last_recv global sock - + # Initialize a socket init_socket() - + # Connect to one among the specified servers, in given priority : while not connected: connected = connect_any(Servers, Port) # Save time of last successful connect time_last_conn = datetime.now() - + # Auth to server send("NICK %s\r\n" % Nick) send("USER %s %s %s :%s\r\n" % (Nick, Nick, Nick, Nick)) - + # If this is a production bot, rather than test, there will be a PW: if Pass != "": send("NICKSERV IDENTIFY %s %s\r\n" % (Nick, Pass)) - + time.sleep(Join_Delay) # wait to join until fleanode eats auth - + # Join selected channels for chan in Channels: logging.info("Joining channel '%s'..." % chan) send("JOIN #%s\r\n" % chan) - + while connected: try: data = sock.recv(Buf_Size) @@ -355,7 +356,8 @@ # Get perma-URL corresponding to given log line def line_url(l): - return "{0}log/{1}/{2}#{3}".format(Base_URL, + return "{0}{1}{2}/{3}#{4}".format(Base_URL, + App_Root, l['chan'], l['t'].strftime(Date_Short_Format), l['idx']) @@ -378,7 +380,7 @@ if arg == "": speak(chan, "Required argument: USER") return - + # Perform query: seen_line = query_db( '''select t, idx, payload, chan from loglines where @@ -387,7 +389,7 @@ # Where output will go result = "" - + # If user has been seen in THE CURRENT chan: if seen_line != None: time_txt = seen_line['t'].strftime(Date_Long_Format) @@ -399,7 +401,7 @@ else: # If user has never been seen in this chan: result = "The user %s has never been seen in #%s." % (arg, chan) - + # Speak the result into the chan where command was issued speak(chan, result) @@ -408,7 +410,7 @@ if arg == "": speak(chan, "Required argument: USER") return - + # Perform query: seen_line = query_db( '''select t, idx, payload, chan from loglines where speaker=%s @@ -417,7 +419,7 @@ # Where output will go result = "" - + # If user has been seen in ANY logged chan: if seen_line != None: time_txt = seen_line['t'].strftime(Date_Long_Format) @@ -474,20 +476,20 @@ '''select idx from loglines where chan=%s and idx = (select max(idx) from loglines where chan=%s) ;''', [chan, chan], one=True) - + # Was this chan unseen previously? if last_idx == None: cur_idx = NewChan_Idx # Then use the config'd start index else: cur_idx = last_idx['idx'] + 1 # Otherwise, get the next idx - + logging.debug("Adding log line with index: %s" % cur_idx) # Set up the insert exec_db('''insert into loglines (idx, t, chan, era, speaker, self, payload) values (%s, %s, %s, %s, %s, %s, %s) ; ''', [cur_idx, time, chan, Era, speaker, action, payload]) - + # Fire commit_db() except Exception as e: @@ -497,7 +499,10 @@ # RE for finding log refs -logref_re = re.compile(Base_URL + """log\/([^/]+)/[^/]+#(\d+)""") +if App_Root == '/': + logref_re = re.compile(Base_URL + """([^/]+)/[^/]+#(\d+)""") +else: + logref_re = re.compile(Base_URL + App_Root.strip('/') + """\/([^/]+)/[^/]+#(\d+)""") # All valid received lines end up here def eat_logline(user, chan, text, action): @@ -506,10 +511,10 @@ logging.warning( "Received martian : '%s' : '%s'" % (chan, text)) return - + # First, add the line to the log: save_line(datetime.now(), chan, user, action, text) - + # Then, see if the line was a command for this bot: if text.startswith(Prefix): cmd = text.partition(Prefix)[2].strip() @@ -555,7 +560,7 @@ time_txt, ref_line['speaker'], ref_line['payload']) - + # Speak the line echo into the chan where ref was seen speak(chan, my_line) diff -uNr a/logotron/eat_dump.py b/logotron/eat_dump.py --- a/logotron/eat_dump.py de1cc45eeef929259de9b60c09c535fd3440b09735785aa6225f8ce35ef6fd5cdafddb928991aecc399607ee442370cf08634484bfa70a522785e29350449282 +++ b/logotron/eat_dump.py 3c87be3c66e1a7e6a11c10770127ee5d0c7815854634e8a4248c08265f4011c1387f7e57311718219c9e3ee1471eef866772e952cd445d4a2792594f2629116d @@ -67,13 +67,13 @@ except IndexError: payload = "" self_speak = True - - ## Handle uniturds using the phf algorithm + + ## Handle uniturds using the phf algorithm try: payload = payload.decode("utf-8") except UnicodeDecodeError: payload = payload.decode('latin-1') - + ## Put in DB: try: exec_db('''insert into loglines (idx, t, chan, era, speaker, self, payload) diff -uNr a/logotron/nsabot.conf b/logotron/nsabot.conf --- a/logotron/nsabot.conf b17807009fa72380c472eeac1c72fbce4a009c541443610520ec2d406530b65ef3c85d4e640d27ac5a61753f4a56f172fd163a224bed5ef2d614e1ff758d04a8 +++ b/logotron/nsabot.conf e30960d97ff96aeddd33d35b1cc16f99a35023a942846bb87f691a0bf66764b6a5bc1eb979f51ac0a6410d0893693e199b719b17c9db1881ea4df459632d722b @@ -55,8 +55,16 @@ # Base URL of logtron site (change to yours!) base_url = http://logs.nosuchlabs.com/ +# Root of the logger after 'base_url' +# Leave as /log/ to display the logger at base_url/log/ (e.g. http://example.com/log/) +# Set to / to display the logger at base_url (e.g. http://example.com) +# Or set to any other value between two slashes +app_root = /log/ + +css_file = classic.css + # Other people's bots (for colouration strictly) -bots = a111, deedbot, feedbot, auctionbot, lobbesbot +bots = gribble, atcbot, punkbot, []bot, assbot, a111, deedbot, deedBot, deedbot-, feedbot, auctionbot, lobbesbot, snsabot, watchglass, trbexplorer, lekythion, sourcerer, ossabot, ericbot, sonofawitch, btcinfobot, BusyBot, drunkbot, spykedbot, pehbot, oglafbot, BingoBot, pokarBot, scoopbot, scoopbot_revived, ozbot, mpexbot # Days of inactivity after which chan is hidden by default days_hide = 14 diff -uNr a/logotron/reader.py b/logotron/reader.py --- a/logotron/reader.py 3413f8f586bcf01d2c230b537f987eb38c2c25f1bd133875128b441b1746c5901e8d573215169b22391f24ba71f1b3858ecb50c40c1010d4c4a1f07f56682711 +++ b/logotron/reader.py f79f7c4c71ad9151c80feed185acbf54b531eaafe10db7a1a3a1e559176ea8c821114ad03df77cebcd4af83ee3e64c2adb6a2eec52b8e152dc8a0926b37f59b9 @@ -7,20 +7,17 @@ psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY) import time -import datetime -from datetime import timedelta +from datetime import timedelta, datetime import sys reload(sys) sys.setdefaultencoding('utf8') import os import threading import re -from datetime import datetime from urlparse import urljoin from flask import Flask, request, session, url_for, redirect, Response, \ render_template, abort, g, flash, _app_ctx_stack, make_response, \ jsonify -from flask import Flask ############################################################################## ############################################################################## @@ -49,6 +46,8 @@ DB_DEBUG = int(cfg.get("db", "db_debug")) # Logism: Base_URL = cfg.get("logotron", "base_url") + App_Root = cfg.get("logotron", "app_root") + CSS_File = cfg.get("logotron", "css_file") Era = int(cfg.get("logotron", "era")) DEBUG = int(cfg.get("logotron", "www_dbg")) Max_Raw_Ln = int(cfg.get("logotron", "max_raw")) @@ -126,13 +125,13 @@ ## Get base URL def get_base(): if DEBUG: - return request.host_url - return Base_URL - + return request.host_url.rstrip('/') + return Base_URL.rstrip('/') # Get perma-URL corresponding to given log line def line_url(l): - return "{0}log/{1}/{2}#{3}".format(get_base(), + return "{0}{1}{2}/{3}#{4}".format(get_base(), + App_Root, l['chan'], l['t'].strftime(Date_Short_Format), l['idx']) @@ -141,28 +140,24 @@ # Get current time now = datetime.now() # Data for channel display : - chan_tbl = {} + chan_list = [] + chan_idx = 0 for chan in Channels: - chan_tbl[chan] = {} - chan_tbl[chan]['show'] = False - - chan_formed = chan - if chan == selected_chan: - chan_formed = "" + chan + "" - - chan_tbl[chan]['link'] = """{2}""".format( - get_base(), chan, chan_formed) - last_time = query_db( '''select t, idx from loglines where chan=%s and idx = (select max(idx) from loglines where chan=%s) ;''', [chan, chan], one=True) last_time_txt = "" - time_field = "" + last_time_url = "" if last_time != None: span = (now - last_time['t']) days = span.days + + # Only add to the list if it should be visible, otherwise continue + if days > Days_Hide and chan != selected_chan and not show_all_chans: + continue + hours = span.seconds/3600 minutes = (span.seconds%3600)/60 @@ -172,36 +167,24 @@ last_time_txt += '%dh ' % hours if minutes != 0: last_time_txt += '%dm' % minutes - - time_field = """{4}""".format( + + last_time_url = "{0}{1}{2}/{3}#{4}".format( get_base(), + App_Root, chan, last_time['t'].strftime(Date_Short_Format), - last_time['idx'], - last_time_txt) + last_time['idx']) - if (days <= Days_Hide) or (chan == selected_chan) or show_all_chans: - chan_tbl[chan]['show'] = True + chan_list.append({ 'name': chan }) - chan_tbl[chan]['time'] = time_field - - ## Generate channel selector bar : - s = """""" - for chan in Channels: - if chan_tbl[chan]['show']: - s += """""".format(chan_tbl[chan]['link']) - s += "" - ## Generate last-activ. links for above : - for chan in Channels: - if chan_tbl[chan]['show']: - s += """""".format(chan_tbl[chan]['time']) - # wrap up: - s += "
{0}
{0}
" - return s + chan_list[chan_idx]['last_time_url'] = last_time_url + chan_list[chan_idx]['last_time_txt'] = last_time_txt + chan_list[chan_idx]['chan_url'] = "{0}{1}{2}{3}".format( + get_base(), App_Root, chan, '/' if chan == Default_Chan else '') + chan_idx += 1 -# Make above callable from inside htm templater: -app.jinja_env.globals.update(gen_chanlist=gen_chanlist) + return chan_list # HTML Tag Regex @@ -257,7 +240,7 @@ ## Format given log line for display def format_logline(l, highlights = [], select=[], showchan=False): payload = html_escape(l['payload']) - + # Format ordinary links: payload = re.sub(stdlinks_re, r'\1', payload) @@ -265,20 +248,20 @@ # Now also format [link][text] links : payload = re.sub(boxlinks_re, r'\2', payload) - + # For ancient logs strictly: substitute orig. link with our logger : if l['era'] < 3: payload = re.sub(era1_re, r' ' + speaker - + # If 'action', annotate: if l['self']: separator = "" payload = "" + payload + "" speaker = "" + speaker + "" - + # HTMLize the given line : - s = ("
" + s = ("
" "{1}{7} {4}
").format(l['idx'], speaker, @@ -318,7 +301,7 @@ return s # Make above callable from inside htm templater: -app.jinja_env.globals.update(format_logline=format_logline) +app.jinja_env.globals.update(format_logline=format_logline) @app.route('/rnd/') @@ -335,10 +318,9 @@ return redirect(line_url(rnd_line)) -@app.route('/log//') -@app.route('/log/', defaults={'date': None}) -@app.route('/log/', defaults={'chan': Default_Chan, 'date': None}) -@app.route('/log', defaults={'chan': Default_Chan, 'date': None}) +@app.route('%s/' % App_Root) +@app.route('%s' % App_Root, defaults={'date': None}) +@app.route('%s' % App_Root, defaults={'chan': Default_Chan, 'date': None}) def log(chan, date): # Handle rubbish chan: if chan not in Channels: @@ -353,13 +335,13 @@ # Get possible 'show all' show_all = request.args.get('all', default = 0, type = int) - + # Get current time now = datetime.now() # Whether we are viewing 'current' tail tail = False - + # If viewing 'current' log: if date == None: date = now.strftime(Date_Short_Format) @@ -370,7 +352,7 @@ day_start = datetime.strptime(date, Date_Short_Format) except Exception, e: return redirect(url_for('log')) - + # Determine the end of the interval being shown day_end = day_start + timedelta(days=1) @@ -383,43 +365,51 @@ '''select * from loglines where chan=%s and t between %s and %s order by idx asc;''', [chan, day_start, day_end], one=False) - + # Optional 'reverse gear' knob: if rev == 1: lines.reverse() - + # Generate navbar for the given date: prev_day = "" next_day = "" - + prev_t = query_db( '''select t from loglines where chan=%s and t < %s order by idx desc limit 1;''', [chan, day_start], one=True) - + if prev_t != None: prev_day = prev_t['t'].strftime(Date_Short_Format) - + if not tail: next_t = query_db( '''select t from loglines where chan=%s and t > %s order by idx asc limit 1;''', [chan, day_end], one=True) - + if next_t != None: next_day = next_t['t'].strftime(Date_Short_Format) - + + # Generate url for css file based on config value + css_url = url_for('static', filename=CSS_File) + + chan_list = gen_chanlist(chan, show_all) + # Return the HTMLized text return render_template('log.html', - chan = chan, - loglines = lines, - sel = (sel_start, sel_end), - date = date, - prev_day = prev_day, - next_day = next_day, - rev = not rev, - show_all = show_all, - idle_day = Days_Hide) + css_url = css_url, + app_root = App_Root, + chan = chan, + chan_list = chan_list, + loglines = lines, + sel = (sel_start, sel_end), + date = date, + prev_day = prev_day, + next_day = next_day, + rev = not rev, + idle_day = Days_Hide, + show_all = show_all) # Primarily for use with 'era 1' and 'era 2' : @@ -438,26 +428,26 @@ # If given chan/idx not found: if item == None: return redirect(url_for('log')) - + # Determine date where item appears in log : item_date = item['t'].strftime(Date_Short_Format) - + # Go there: - return redirect("/log/" + chan + "/" + item_date + "#" + idx) - + return redirect(App_Root + chan + "/" + item_date + "#" + idx) + @app.route('/log-raw/') def rawlog(chan): res = "" - + # Handle rubbish chan: if chan not in Channels: return Response("EGGOG: No such Channel!", mimetype='text/plain') - + # Get start and end indices: idx_start = request.args.get('istart', default = 0, type = int) idx_end = request.args.get('iend', default = 0, type = int) - + # Malformed bounds? if idx_start > idx_end: return Response("EGGOG: Start must precede End!", @@ -473,7 +463,7 @@ '''select * from loglines where chan=%s and idx between %s and %s order by idx asc;''', [chan, idx_start, idx_end], one=False) - + # Retrieve raw lines in classical Phf format: for l in lines: action = "" @@ -486,7 +476,7 @@ action, speaker, l['payload']) - + # Return plain text: return Response(res, mimetype='text/plain') @@ -507,7 +497,11 @@ chan = request.args.get('chan', default = Default_Chan, type = str) query = request.args.get('q', default = '', type = str) offset = request.args.get('after', default = 0, type = int) - + show_all = 0 + + if len(query) < Min_Query_Length: + return redirect(url_for('log')) + # channels to search in chans = [] @@ -519,6 +513,7 @@ chans = Channels legend = "all logged channels" showchan = True + show_all = 1 else: # Handle possible rubbish chan: if chan not in Channels: @@ -527,7 +522,7 @@ # search in selected channel only chans = [chan] legend = chan - + nres = 0 searchres = [] tokens_orig = [] @@ -545,11 +540,11 @@ from_users.append(t.split(':')[1]) # Record user for 'from' query else: tokens_standard.append(t) - + from_users = ['%' + sanitize_speaker(t) + '%' for t in from_users] tokens_orig = [re_escape(t) for t in tokens_standard] tokens_formed = ['%' + t + '%' for t in tokens_orig] - + # Query is usable; perform the search on DB and get the finds if from_users == []: searchres = query_db( @@ -571,23 +566,29 @@ tokens_formed, Max_Search_Results, offset], one=False) - + # Number of search results returned in this query nres = len(searchres) # Whether to display 'back' button : back = (offset != 0) - + # Whether to display 'forward' button : forw = (nres == Max_Search_Results) - + # Starting index of search results sres = offset - + # Ending index of search results eres = offset + min(nres, Max_Search_Results) - + + # Generate url for css file based on config value + css_url = url_for('static', filename=CSS_File) + + chan_list = gen_chanlist(chan, show_all) + return render_template('searchres.html', + css_url = css_url, query = query, hquery = html_escape(query), legend = legend, @@ -597,9 +598,11 @@ forw = forw, psize = Max_Search_Results, chan = chan, + chan_list = chan_list, tokens = tokens_orig, loglines = searchres, - showchan = showchan) + showchan = showchan, + show_all = show_all) # Comment this out if you don't have one @app.route('/favicon.ico') diff -uNr a/logotron/release_notes.txt b/logotron/release_notes.txt --- a/logotron/release_notes.txt false +++ b/logotron/release_notes.txt 32e9afb62ffc8974daaffc380b1242d2a6cb0da5a859cb3d50fb5d6925fa863d1261106ada02e7984c96bd4bd42f57d2145eb28500b044ea223f4f1142bcdc21 @@ -0,0 +1,140 @@ +################### +2021 May 24 Update: +################### +------------------------------------------------------------------------------------------------------------------------------------------ +684804 frontend_updates billymg "Changes to HTML/CSS to allow for easier theming. Added config knobs for custom log root and CSS file." +------------------------------------------------------------------------------------------------------------------------------------------ + +(1) Tuned HTML and moved CSS to its own file at static/classic.css +(2) Added knob to config to specify CSS filename +(3) Added knob to config to specify app root +(4) Fixed server error when handling search queries less than Min_Query_Length +(5) Show/Hide idle chans link now doesn't lose the current day if other than tail +(6) Updated README.txt and added this release_notes.txt document + + +#################### +2020 Aug. Update #2: +#################### +---------------------------------------------------------------------------- +641794 aug2020_errata asciilifeform "Stylistic fixes for previous patch." +---------------------------------------------------------------------------- + +(1) Additional 'back' and 'forward' buttons at the bottom of search result pages. +(2) Fix era1/2 link diddler regexps to handle all (AFAIK) known variants correctly. + + +#################### +2020 Aug. Update #1: +#################### +-------------------------------------------------------------------------------------------------------------------------------------------------------- +641773 searchpg_oldlinks asciilifeform "Paginated search; automatic replacement of ancient logger link targets; fix for unicode barf in eat_dump.py." +-------------------------------------------------------------------------------------------------------------------------------------------------------- + +(1) Implemented fetch-by-channel-and-index (i.e. /ilog/chan/lineindex links). +(2) Automatically adjust link targets of #ba and 'btcbase' log citations to point to this logger, using the syntax in (1). Original text of link is preserved. Dates are handled correctly. +(3) Search results now properly paginated, with working 'back' and 'forward' buttons. +(4) Fixed ancient unicode bug in eat_dump.py. + + +#################### +2020 Jul. Update #3: +#################### +--------------------------------------------------------------------------------------------------------------- +641484 navbar_date_auto asciilifeform "Automatically skip empty days in navbar. Handle 'dawn of time' case." +--------------------------------------------------------------------------------------------------------------- + +(1) Automatically skip empty days in date navigation bar. At the same time, handle empty days correctly when these are requested. +(2) Handle 'dawn of time' case for any given channel, to avoid infinite loop insanity from search engines. + + +#################### +2020 Jul. Update #2: +#################### +------------------------------------------------------------------------------- +641046 search_all_chans asciilifeform "Button to search in all logged chans" +------------------------------------------------------------------------------- + +(1) Fix unclosed 'table' tag from earlier. +(2) 'Search all chans' button. + + +#################### +2020 Jul. Update #1: +#################### +------------------------------------------------------------------------------- +640165 hide_inactive asciilifeform "Configurable default-hide of idle chans" +------------------------------------------------------------------------------- + +(1) Hide by default chans where no activity in days_hide day (set in config.) +(2) Added 'join as guest' link (via 'kiwi' wwwtronic IRC client) +(3) Example Apache-WSGI configs. + + +################### +2019 Oct. 4 Update: +################### + +(1) WWW: Removed ill-conceived 'Tape' knob. +(2) Bot: Added silent-disconnection detector. + +Now requires the following value in bot config e.g.: + +[irc] +disc_t = 180 + +This represents the currently-recommended interval: 3 minutes. +If inactivity on the incoming line (incl. pingism) exceeds this interval, +a disconnection is forced. The normal reconnection routine will execute. + +To disable forced disconnects, set disc_t to zero. + + +#################### +2019 Sep. 28 Update: +#################### + +(1) Bot: incorporated reconnector fixes. +(2) Bot: 'seen', 'seen-all', and 'version' knobs implemented. +(3) WWW: up/down arrows for mouse-driven operation on pocket comps +(4) WWW: double-quotes prevent breakup of search query + (Note: still needs improvement to force matching on word boundaries) +(5) WWW: demarkation of "action" lines +(6) WWW: Raw 'Tape" knob: + +e.g. http://logs.nosuchlabs.com/tape?istart=1937934&iend=1937941 +returns: + +trilema;1937934;1569444908;asciilifeform;achtung panzers! piz pipe down ?! +trilema;1937935;1569444920;diana_coman;seems so +trilema;1937936;1569444957;asciilifeform;paging BingoBoingo ! +trilema;1937937;1569444974;diana_coman;well, apparetly he was connected from piz too, lol +trilema;1937938;1569444982;diana_coman;ossasepia.com (pizarro) is down too, yes +trilema;1937939;1569445010;asciilifeform;dulap unreachable for 1st time since year+ ago when bb elbowed the mains cord +trilema;1937940;1569445073;diana_coman;apparently back on +trilema;1937941;1569445079;diana_coman;wb BingoBoingo ! + +Still needs a variant of 'eater' that will eat these. + + +#################### +2019 Aug. 16 Update: +#################### + +(1) Bot: added Phf's algo for uniturd processing ('fallback to latin-1'). +(2) Bot: May leave IRC password blank in config for use with unregged bots +(3) WWW: 'Checkerboard' colouring; footer links. +(4) WWW: Links in log lines now open in new browser tab (a la Phf's log) +(5) WWW: Can now be loaded 'in reverse gear', http://...../log?rev=1 +(6) WWW: 'Random' knob, takes reader to an arbitrary moment in past log. + + +#################### +2019 Aug. 11 Update: +#################### + +(1) Multi-line selection is implemented. + +(2) User may request export of raw log lines in Phf-classical format. + E.g. : + http://logs.nosuchlabs.com/log-raw/trilema?istart=1925000&iend=1925500 diff -uNr a/logotron/static/classic.css b/logotron/static/classic.css --- a/logotron/static/classic.css false +++ b/logotron/static/classic.css da0ff8b2f9a180fab9855b9f86cb73ba056090992de825f970172766febdc6cf34986f1c2ed2ebd089208f8bdc4d0efb926819927a4145d1bcc3d5a29c205e6f @@ -0,0 +1,98 @@ +.chan-list { + display: table; + list-style: none; + margin: 10px auto 18px auto; + padding: 5px; + border: 1px solid black; + border-spacing: 25px 0; +} + +.chan-link-wrapper { + display: inline-block; + margin: 2px 15px; +} + +.chan-link { + display: inline-block; + font-weight: bold; +} + +.chan-link-wrapper-active .chan-link { + background: yellow; + padding: 1px; +} + +.chan-last-active-link { + display: block; + font-style: italic; +} + +.chan-filters { + text-align: center; + font-style: italic; + font-size: smaller; +} + +.search-form { + text-align: center; +} + +.paginator { + margin: 20px 0; +} + +.nick { + font-weight: bold; + text-decoration: none; + color: black; +} + +.nick:visited { + color: black; +} + +.bot, +.bot .nick, +.bot a { + color: #777; + font-weight: normal; +} + +.loglines { + word-wrap: break-word; +} + +.loglines div:nth-child(2n+1) { + background: #f5f5f5; +} + +.loglines div:target { + background: lightyellow; +} + +img.inline { + margin: 0.5em auto 1em auto; + display: block; + border: 1px solid black; + width: 34em; +} + +img.hist { + margin: 0.5em auto 1em auto; +} + +.annotations a { + text-decoration: none; +} + +.footer { + text-align: center; +} + +.jump { + float:right; +} + +.highlight { + background: yellow; +} diff -uNr a/logotron/templates/chan-nav-table.html b/logotron/templates/chan-nav-table.html --- a/logotron/templates/chan-nav-table.html false +++ b/logotron/templates/chan-nav-table.html 23b3fcde9e6f472619c9dc5ab61a54ba16bcc45184e72e8c6f15b1397f56e771c66d22c3fba478a76298c7a499bf9c9c875fc4f80aa53619b77ce4636fb49ceb @@ -0,0 +1,21 @@ + + + + {% for chan_item in chan_list %} + + {% endfor %} + + + + + {% for chan_item in chan_list %} + + {% endfor %} + + +
+ {{ chan_item.name }} +
+ {{ chan_item.last_time_txt }} +
diff -uNr a/logotron/templates/layout.html b/logotron/templates/layout.html --- a/logotron/templates/layout.html 6812442d036ef3f18cd4f148f2e7fe1b5833afd87555625293783246b10224e946a7c89fb8b762b82fd02b7a1b1a86abd6e1b006b095b254ac9ea446f3acd003 +++ b/logotron/templates/layout.html 68fb1bd53530a683f6796169325527d8a720cc306f143c276eb7677720deced935d780117ad3e75e9baed2aacf91fd70812050c35ed09e94b684308f5d8006cc @@ -1,161 +1,59 @@ - - - {% block title %} - {% endblock %} - + {% block title %}{% endblock %} + - + + - +
+ {% include 'chan-nav-table.html' %} -

- - - - -
- {% if chan != 'all' %} - {{ gen_chanlist( chan, show_all ) | safe }} - {% else %} - {{ gen_chanlist( chan, True ) | safe }} - {% endif %} -
- -

- - {% if chan != 'all' %} -
- {% endif %} - -
- +
+
-
- - {% block body %}{% endblock %} + -
+
-
- {% if chan != 'all' %}Visit #{{ chan }} as Guest | Random({{ chan }}) | {% endif %}Download hourly DB snapshot | Get Source Code + {% block body %}{% endblock %} + +
- + + - diff -uNr a/logotron/templates/log-pagination-links.html b/logotron/templates/log-pagination-links.html --- a/logotron/templates/log-pagination-links.html false +++ b/logotron/templates/log-pagination-links.html 47c5f0d26c9b58b487dbd36123ef58c9877968c71df56c7cbabd3ae4839f5490715915e76e527c2c20360cf79722c9112e736abde1e77e77ad55d5577fdc1acb @@ -0,0 +1,9 @@ +{% if prev_day != '' %} +← {{ prev_day }} +{% else %} +Dawn of Time +{% endif %} + +{% if next_day != '' %} +| {{ next_day }} → +{% endif %} diff -uNr a/logotron/templates/log.html b/logotron/templates/log.html --- a/logotron/templates/log.html 0f906a5db0ed7f390774fcc6f1d3c09dea5c2924ddb57dcde22ccbbff24bf9440820a23f3063058dbc84c14743cd494eeeb90a0c721988b2dbdf4b629290bfd0 +++ b/logotron/templates/log.html f0d6bc05aa0801e18623c6d5c4b70a39499ecfd950d55b0e1691304a4b64ab5189c82cc9842161d70dcec8018d0915372b10223127139b639ccd138898b09042 @@ -6,11 +6,10 @@ {% block body %} -