tree checksum vpatch file split hunks

all signers: lobbes bvt diana_coman asciilifeform billymg

antecedents: navbar_date_auto.kv searchpg_oldlinks.kv detect_disconnect.kv hide_inactive.kv aug2020_errata.kv

press order:

logotron_genesis.kvasciilifeform diana_coman
multsel_and_datefix.kvasciilifeform diana_coman
raw_line_export.kvasciilifeform diana_coman
rle_errata.kvasciilifeform diana_coman
irssi2tmsr.kvasciilifeform diana_coman
uniturds_etc.kvasciilifeform diana_coman
line_wraps.kvasciilifeform diana_coman
znc2tmsr_etc.kvasciilifeform diana_coman lobbes
uptimefix_bye_cache.kvasciilifeform diana_coman lobbes
raw_line_fix.kvasciilifeform diana_coman lobbes
irssi_format.kvasciilifeform diana_coman
sept_fixes.kvasciilifeform diana_coman
active_disconnect_r3.kvasciilifeform bvt diana_coman
sept_errata.kvasciilifeform
detect_disconnect.kvasciilifeform
shlex_removal.kvasciilifeform
hide_inactive.kvasciilifeform
search_all_chans.kvasciilifeform
navbar_date_auto.kvasciilifeform
searchpg_oldlinks.kvasciilifeform
aug2020_errata.kvasciilifeform
frontend_updates.kvbillymg

patch:

- 82B2AA673901D86655D3977CF22CBCED5E8EC8BAAB2AF28E105EDB9D60DCB397AC1023727AE5FCC4C59F7B41A292FBE6A2EA5C7E8207695A10C406ECFF5A99E8
+ 30E3A359B4FAD69E63D5C11098CB3292A4166A644488099744130E4EB35762EC6FAD517B3FAF5D06818AB18303C92AFC7CAFA3B7E17D5159F83010D44A465262
logotron/MANIFEST.TXT
(19 . 3)(19 . 4)
5 641484 navbar_date_auto asciilifeform "Automatically skip empty days in navbar. Handle 'dawn of time' case."
6 641773 searchpg_oldlinks asciilifeform "Paginated search; automatic replacement of ancient logger link targets; fix for unicode barf in eat_dump.py."
7 641794 aug2020_errata asciilifeform "Stylistic fixes for previous patch."
8 684804 frontend_updates billymg "Changes to HTML/CSS to allow for easier theming. Added config knobs for custom log root and CSS file."
- 892C1058BE06D507D51083D944192C163494AAF861C2D68B413DCBF2E6561A79B54EC5530993C4387389F4BFE258AA1C93F8352491CE64D8F8AA1F756C02354C
+ 904C7DDA848F33B172A5541F633EDBBF425EDF7D8BF9C1C9BC257D8033DE2EEFF3742E8D813DD735E34E4A9249D7DF4B4EF3FE3C3D0F1143B25172CB0E2C27D6
logotron/README.txt
(7 . 7)(7 . 14)
13 (2) 'flask' lib for (1).
14 (3) 'psycopg2' lib for (1).
15 (4) 'postgres' (9 or 10).
16 (5) A WWW server that knows how to proxy.
17 (5) A WWW server that can serve WSGI applications (e.g. Apache with mod_wsgi installed).
18 - If installing mod_wsgi for the first time be sure to set your target
19 python version to 2.7 (/etc/portage/make.conf in gentoo/dulap systems)
20
21
22 ####################
23 DB Setup
24 ####################
25
26 To use the kit, you will first need to create a user and DB, e.g.:
27
(17 . 7)(24 . 7)
29 create user nsabot createdb;
30 alter role nsabot superuser;
31 create database nsalog;
32 grant all privileges on database nsalog to nsabot ;
33 grant all privileges on database nsalog to nsabot;
34
35 ... you can take 'super' away from this user after 1st run,
36 it is needed in order to let him load the pg_trgm indexer
(26 . 6)(33 . 11)
38 Next, run 'init_db.sh' (alter the constants to match the
39 names of your postgres user and the DB), this creates the schema.
40
41
42 ####################
43 Importing Logs
44 ####################
45
46 Then see 'eat.sh' and the 'eat_dump.py' it uses, re how to
47 fill your log archive DB. 'eat_dump.py' eats in Phf's classical
48 format, e.g.:
(41 . 137)(53 . 55)
50 currently capable of eating config file. Set these to your DB
51 and PG user.
52
53 Now, adjust the constants in 'nsabot.conf' (rename per taste)
54 to specify your IRC params, name of bot, host at which www
55 logger will reside, and other knob values.
56
57 Adjust the three 'flask' templates in 'templates' subdir to
58 give the desired look and feel for the www end. Currently we are using
59 Phf's classic style sheet, with minor modifications.
60
61 'reader.py' takes one mandatory command-line argument: full path
62 to the config above. Same for 'bot.py', which is the IRC bot.
63
64 Run these via e.g. nohup ./bot.py & ; nohup ./reader.py &
65 and let your proxying WWW server know how to reach the latter's port.
66
67 For bot.py you will need a registered nick on fleanode (or wherever
68 it is used.) There are no fleanode-specific hacks in the bot, ergo
69 it can be stood up behind ZNC (although this has not been tested.)
70
71 Certain important features are presently unimplemented, in no order:
72 (1) Backlinkage.
73 (2) Search result pagination.
74 (3) Double-quoted search terms.
75 (4) Paste archiving.
76 (5) Multi-headed IRC bot for weather resistance.
77 (6) Informative eggogology for bot commands.
78 (7) Automatic synchronization with mirrors
79
80 A ZNC log eater is also required, to properly fill in the archives.
81 This is not yet available at the time of this writing.
82
83 ###############
84 Aug. 11 Update:
85 ###############
86
87 (1) Multi-line selection is implemented.
88
89 (2) User may request export of raw log lines in Phf-classical format.
90 E.g. :
91 http://logs.nosuchlabs.com/log-raw/trilema?istart=1925000&iend=1925500
92 See subdir 'logconverters' for scripts to convert ZNC and irssi log formats to Phf's format
93
94
95 ###############
96 Aug. 16 Update:
97 ###############
98
99 (1) Bot: added Phf's algo for uniturd processing ('fallback to latin-1').
100 (2) Bot: May leave IRC password blank in config for use with unregged bots
101 (3) WWW: 'Checkerboard' colouring; footer links.
102 (4) WWW: Links in log lines now open in new browser tab (a la Phf's log)
103 (5) WWW: Can now be loaded 'in reverse gear', http://...../log?rev=1
104 (6) WWW: 'Random' knob, takes reader to an arbitrary moment in past log.
105
106 ###############
107 Sep. 28 Update:
108 ###############
109
110 (1) Bot: incorporated reconnector fixes.
111 (2) Bot: 'seen', 'seen-all', and 'version' knobs implemented.
112 (3) WWW: up/down arrows for mouse-driven operation on pocket comps
113 (4) WWW: double-quotes prevent breakup of search query
114 (Note: still needs improvement to force matching on word boundaries)
115 (5) WWW: demarkation of "action" lines
116 (6) WWW: Raw 'Tape" knob:
117
118 e.g. http://logs.nosuchlabs.com/tape?istart=1937934&iend=1937941
119 returns:
120
121 trilema;1937934;1569444908;asciilifeform;achtung panzers! piz pipe down ?!
122 trilema;1937935;1569444920;diana_coman;seems so
123 trilema;1937936;1569444957;asciilifeform;paging BingoBoingo !
124 trilema;1937937;1569444974;diana_coman;well, apparetly he was connected from piz too, lol
125 trilema;1937938;1569444982;diana_coman;ossasepia.com (pizarro) is down too, yes
126 trilema;1937939;1569445010;asciilifeform;dulap unreachable for 1st time since year+ ago when bb elbowed the mains cord
127 trilema;1937940;1569445073;diana_coman;apparently back on
128 trilema;1937941;1569445079;diana_coman;wb BingoBoingo !
129
130 Still needs a variant of 'eater' that will eat these.
131
132 ###############
133 Oct. 4 Update:
134 ###############
135
136 (1) WWW: Removed ill-conceived 'Tape' knob.
137 (2) Bot: Added silent-disconnection detector.
138
139 Now requires the following value in bot config e.g.:
140 ####################
141 Configuration
142 ####################
143
144 [irc]
145 disc_t = 180
146 Now, adjust the constants in 'nsabot.conf' (rename per taste)
147 to specify your IRC params, name of bot, host at which www
148 logger will reside, and other knob values.
149
150 This represents the currently-recommended interval: 3 minutes.
151 If inactivity on the incoming line (incl. pingism) exceeds this interval,
152 a disconnection is forced. The normal reconnection routine will execute.
153 Set css_file in the conf file to point to your desired CSS file in 'static' subdir.
154 Adjust the 'jinja' templates in 'templates' subdir to give the desired look
155 and feel for the www end IF your CSS changes alone are not sufficient.
156 Currently we are using Phf's classic style sheet, with minor modifications.
157
158 To disable forced disconnects, set disc_t to zero.
159
160 ####################
161 Jul. 2020 Update #1:
162 Running the WWW
163 ####################
164
165 (1) Hide by default chans where no activity in days_hide day (set in config.)
166 (2) Added 'join as guest' link (via 'kiwi' wwwtronic IRC client)
167 (3) Example Apache-WSGI configs.
168
169 ####################
170 Jul. 2020 Update #2:
171 ####################
172 'reader.py' takes one mandatory argument: full path to the config above.
173 - When running as a WSGI module this argument needs to be hardcoded in 'reader.py'
174 - When running locally pass in as a single command-line argument,
175 e.g. `./reader.py path/to/config`
176 - Optionally, when running locally, set FLASK_ENV=development to allow for
177 auto-reloading on file changes (see Flask documentation for more info).
178
179 (1) Fix unclosed 'table' tag from earlier.
180 (2) 'Search all chans' button.
181
182 ####################
183 Jul. 2020 Update #3:
184 Running the Bot
185 ####################
186
187 (1) Automatically skip empty days in date navigation bar. At the same time, handle empty days correctly when these are requested.
188 (2) Handle 'dawn of time' case for any given channel, to avoid infinite loop insanity from search engines.
189 Run bot.by and pass in the path to the same config file as a single command-line
190 argument, e.g. `nohup ./bot.py path/to/config &`
191
192 #####################
193 Aug. 2020 Update #1 :
194 #####################
195
196 (1) Implemented fetch-by-channel-and-index (i.e. /ilog/chan/lineindex links).
197 (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.
198 (3) Search results now properly paginated, with working 'back' and 'forward' buttons.
199 (4) Fixed ancient unicode bug in eat_dump.py.
200 For bot.py you will need a registered nick on fleanode (or wherever
201 it is used.) There are no fleanode-specific hacks in the bot, ergo
202 it can be stood up behind ZNC (although this has not been tested.)
203
204 #####################
205 Aug. 2020 Update #2 :
206 #####################
207
208 (1) Additional 'back' and 'forward' buttons at the bottom of search result pages.
209 (2) Fix era1/2 link diddler regexps to handle all (AFAIK) known variants correctly.
210 ####################
211 Not Yet Implemented
212 ####################
213
214 Certain important features are presently unimplemented, in no order:
215 (1) Backlinkage.
216 (3) Double-quoted search terms.
217 (4) Paste archiving.
218 (5) Multi-headed IRC bot for weather resistance.
219 (6) Informative eggogology for bot commands.
220 (7) Automatic synchronization with mirrors
- 14D7AC73F3C61335C9BB7AE63F70401B2342E82091D600A40653C4A86CBAE5D6BDF8619A377A85D48D02EBB6B3F0BD1563751319BD02D4786C7EC6D4844DE27A
+ 801C95BA92D8D9F576A2EB6076742F9B6198A9D7973D7E07B0B72F2E513E65C3FD946FD8D7F1ACBDC6E783BBE31B699E5C5EA5BA4E10A7740BDB353333E51696
logotron/apacheism/example.vhost
(1 . 5)(1 . 6)
225 Listen 80
226
227 # Path where your python libraries live. Check permissions to ensure it is accessible by apache
228 WSGIPythonPath /home/yourbotuser/logger/
229
230 <VirtualHost *:80>
(12 . 11)(13 . 11)
232 WSGIDaemonProcess yourbotuser user=yourbotuser group=apache threads=16
233
234 WSGIProcessGroup %{GLOBAL}
235
236
237 WSGIScriptAlias / /home/yourbotuser/logger/yourbotuser.wsgi
238
239
240 Alias /static/ /home/yourbotuser/logger/static/
241
242
243 <Directory /home/yourbotuser/logger/static>
244 Require all granted
245 </Directory>
- 80D24124B12055C919F68583A83445477D13680DB62960B9AC57A6D7CA066F52973E9FC1BC3F80ECF6B541D0784CFBFA10195792F2A9D786C57143720584F88A
+ D33D6B379DC88808FD72E9579D23818B7A2611D24F48E07ED4EBA2DCDC97E228D7F45C34F939FC282146ED3E89F320D709CF023711C9DBB0394DFD39B36DCF4C
logotron/bot.py
(14 . 7)(14 . 7)
250 ##############################################################################
251
252 # Version. If changing this program, always set this to same # as in MANIFEST
253 Ver = 597858
254 Ver = 684804
255
256 ##############################################################################
257
(74 . 6)(74 . 7)
259 DB_DEBUG = cfg.get("db", "db_debug")
260 # Logism:
261 Base_URL = cfg.get("logotron", "base_url")
262 App_Root = cfg.get("logotron", "app_root")
263 Era = int(cfg.get("logotron", "era"))
264 NewChan_Idx = int(cfg.get("logotron", "newchan_idx"))
265 Src_URL = cfg.get("logotron", "src_url")
(142 . 7)(143 . 7)
267 def init_socket():
268 global sock
269 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
270
271
272 # Disable Nagle's algorithm for transmit operations
273 sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
274 # Disable Nagle's algorithm for receive operation, Linux-only
(150 . 7)(151 . 7)
276 sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_QUICKACK, 1)
277 except Exception as e:
278 logging.warning(e)
279
280
281 connected = False
282
283 def deinit_socket():
(250 . 32)(251 . 32)
285 global time_last_conn
286 global time_last_recv
287 global sock
288
289
290 # Initialize a socket
291 init_socket()
292
293
294 # Connect to one among the specified servers, in given priority :
295 while not connected:
296 connected = connect_any(Servers, Port)
297
298 # Save time of last successful connect
299 time_last_conn = datetime.now()
300
301
302 # Auth to server
303 send("NICK %s\r\n" % Nick)
304 send("USER %s %s %s :%s\r\n" % (Nick, Nick, Nick, Nick))
305
306
307 # If this is a production bot, rather than test, there will be a PW:
308 if Pass != "":
309 send("NICKSERV IDENTIFY %s %s\r\n" % (Nick, Pass))
310
311
312 time.sleep(Join_Delay) # wait to join until fleanode eats auth
313
314
315 # Join selected channels
316 for chan in Channels:
317 logging.info("Joining channel '%s'..." % chan)
318 send("JOIN #%s\r\n" % chan)
319
320
321 while connected:
322 try:
323 data = sock.recv(Buf_Size)
(355 . 7)(356 . 8)
325
326 # Get perma-URL corresponding to given log line
327 def line_url(l):
328 return "{0}log/{1}/{2}#{3}".format(Base_URL,
329 return "{0}{1}{2}/{3}#{4}".format(Base_URL,
330 App_Root,
331 l['chan'],
332 l['t'].strftime(Date_Short_Format),
333 l['idx'])
(378 . 7)(380 . 7)
335 if arg == "":
336 speak(chan, "Required argument: USER")
337 return
338
339
340 # Perform query:
341 seen_line = query_db(
342 '''select t, idx, payload, chan from loglines where
(387 . 7)(389 . 7)
344
345 # Where output will go
346 result = ""
347
348
349 # If user has been seen in THE CURRENT chan:
350 if seen_line != None:
351 time_txt = seen_line['t'].strftime(Date_Long_Format)
(399 . 7)(401 . 7)
353 else:
354 # If user has never been seen in this chan:
355 result = "The user %s has never been seen in #%s." % (arg, chan)
356
357
358 # Speak the result into the chan where command was issued
359 speak(chan, result)
360
(408 . 7)(410 . 7)
362 if arg == "":
363 speak(chan, "Required argument: USER")
364 return
365
366
367 # Perform query:
368 seen_line = query_db(
369 '''select t, idx, payload, chan from loglines where speaker=%s
(417 . 7)(419 . 7)
371
372 # Where output will go
373 result = ""
374
375
376 # If user has been seen in ANY logged chan:
377 if seen_line != None:
378 time_txt = seen_line['t'].strftime(Date_Long_Format)
(474 . 20)(476 . 20)
380 '''select idx from loglines where chan=%s
381 and idx = (select max(idx) from loglines where chan=%s) ;''',
382 [chan, chan], one=True)
383
384
385 # Was this chan unseen previously?
386 if last_idx == None:
387 cur_idx = NewChan_Idx # Then use the config'd start index
388 else:
389 cur_idx = last_idx['idx'] + 1 # Otherwise, get the next idx
390
391
392 logging.debug("Adding log line with index: %s" % cur_idx)
393
394 # Set up the insert
395 exec_db('''insert into loglines (idx, t, chan, era,
396 speaker, self, payload) values (%s, %s, %s, %s, %s, %s, %s) ; ''',
397 [cur_idx, time, chan, Era, speaker, action, payload])
398
399
400 # Fire
401 commit_db()
402 except Exception as e:
(497 . 7)(499 . 10)
404
405
406 # RE for finding log refs
407 logref_re = re.compile(Base_URL + """log\/([^/]+)/[^/]+#(\d+)""")
408 if App_Root == '/':
409 logref_re = re.compile(Base_URL + """([^/]+)/[^/]+#(\d+)""")
410 else:
411 logref_re = re.compile(Base_URL + App_Root.strip('/') + """\/([^/]+)/[^/]+#(\d+)""")
412
413 # All valid received lines end up here
414 def eat_logline(user, chan, text, action):
(506 . 10)(511 . 10)
416 logging.warning(
417 "Received martian : '%s' : '%s'" % (chan, text))
418 return
419
420
421 # First, add the line to the log:
422 save_line(datetime.now(), chan, user, action, text)
423
424
425 # Then, see if the line was a command for this bot:
426 if text.startswith(Prefix):
427 cmd = text.partition(Prefix)[2].strip()
(555 . 7)(560 . 7)
429 time_txt,
430 ref_line['speaker'],
431 ref_line['payload'])
432
433
434 # Speak the line echo into the chan where ref was seen
435 speak(chan, my_line)
436
- DE1CC45EEEF929259DE9B60C09C535FD3440B09735785AA6225F8CE35EF6FD5CDAFDDB928991AECC399607EE442370CF08634484BFA70A522785E29350449282
+ 3C87BE3C66E1A7E6A11C10770127EE5D0C7815854634E8A4248C08265F4011C1387F7E57311718219C9E3EE1471EEF866772E952CD445D4A2792594F2629116D
logotron/eat_dump.py
(67 . 13)(67 . 13)
441 except IndexError:
442 payload = ""
443 self_speak = True
444
445 ## Handle uniturds using the phf algorithm
446
447 ## Handle uniturds using the phf algorithm
448 try:
449 payload = payload.decode("utf-8")
450 except UnicodeDecodeError:
451 payload = payload.decode('latin-1')
452
453
454 ## Put in DB:
455 try:
456 exec_db('''insert into loglines (idx, t, chan, era, speaker, self, payload)
- B17807009FA72380C472EEAC1C72FBCE4A009C541443610520EC2D406530B65EF3C85D4E640D27AC5A61753F4A56F172FD163A224BED5EF2D614E1FF758D04A8
+ E30960D97FF96AEDDD33D35B1CC16F99A35023A942846BB87F691A0BF66764B6A5BC1EB979F51AC0A6410D0893693E199B719B17C9DB1881EA4DF459632D722B
logotron/nsabot.conf
(55 . 8)(55 . 16)
461 # Base URL of logtron site (change to yours!)
462 base_url = http://logs.nosuchlabs.com/
463
464 # Root of the logger after 'base_url'
465 # Leave as /log/ to display the logger at base_url/log/ (e.g. http://example.com/log/)
466 # Set to / to display the logger at base_url (e.g. http://example.com)
467 # Or set to any other value between two slashes
468 app_root = /log/
469
470 css_file = classic.css
471
472 # Other people's bots (for colouration strictly)
473 bots = a111, deedbot, feedbot, auctionbot, lobbesbot
474 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
475
476 # Days of inactivity after which chan is hidden by default
477 days_hide = 14
- 3413F8F586BCF01D2C230B537F987EB38C2C25F1BD133875128B441B1746C5901E8D573215169B22391F24BA71F1B3858ECB50C40C1010D4C4A1F07F56682711
+ F79F7C4C71AD9151C80FEED185ACBF54B531EAAFE10DB7A1A3A1E559176EA8C821114AD03DF77CEBCD4AF83EE3E64C2ADB6A2EEC52B8E152DC8A0926B37F59B9
logotron/reader.py
(7 . 20)(7 . 17)
482 psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
483 psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY)
484 import time
485 import datetime
486 from datetime import timedelta
487 from datetime import timedelta, datetime
488 import sys
489 reload(sys)
490 sys.setdefaultencoding('utf8')
491 import os
492 import threading
493 import re
494 from datetime import datetime
495 from urlparse import urljoin
496 from flask import Flask, request, session, url_for, redirect, Response, \
497 render_template, abort, g, flash, _app_ctx_stack, make_response, \
498 jsonify
499 from flask import Flask
500 ##############################################################################
501
502 ##############################################################################
(49 . 6)(46 . 8)
504 DB_DEBUG = int(cfg.get("db", "db_debug"))
505 # Logism:
506 Base_URL = cfg.get("logotron", "base_url")
507 App_Root = cfg.get("logotron", "app_root")
508 CSS_File = cfg.get("logotron", "css_file")
509 Era = int(cfg.get("logotron", "era"))
510 DEBUG = int(cfg.get("logotron", "www_dbg"))
511 Max_Raw_Ln = int(cfg.get("logotron", "max_raw"))
(126 . 13)(125 . 13)
513 ## Get base URL
514 def get_base():
515 if DEBUG:
516 return request.host_url
517 return Base_URL
518
519 return request.host_url.rstrip('/')
520 return Base_URL.rstrip('/')
521
522 # Get perma-URL corresponding to given log line
523 def line_url(l):
524 return "{0}log/{1}/{2}#{3}".format(get_base(),
525 return "{0}{1}{2}/{3}#{4}".format(get_base(),
526 App_Root,
527 l['chan'],
528 l['t'].strftime(Date_Short_Format),
529 l['idx'])
(141 . 28)(140 . 24)
531 # Get current time
532 now = datetime.now()
533 # Data for channel display :
534 chan_tbl = {}
535 chan_list = []
536 chan_idx = 0
537 for chan in Channels:
538 chan_tbl[chan] = {}
539 chan_tbl[chan]['show'] = False
540
541 chan_formed = chan
542 if chan == selected_chan:
543 chan_formed = "<span class='highlight'>" + chan + "</span>"
544
545 chan_tbl[chan]['link'] = """<a href="{0}log/{1}"><b>{2}</b></a>""".format(
546 get_base(), chan, chan_formed)
547
548 last_time = query_db(
549 '''select t, idx from loglines where chan=%s
550 and idx = (select max(idx) from loglines where chan=%s) ;''',
551 [chan, chan], one=True)
552
553 last_time_txt = ""
554 time_field = ""
555 last_time_url = ""
556 if last_time != None:
557 span = (now - last_time['t'])
558 days = span.days
559
560 # Only add to the list if it should be visible, otherwise continue
561 if days > Days_Hide and chan != selected_chan and not show_all_chans:
562 continue
563
564 hours = span.seconds/3600
565 minutes = (span.seconds%3600)/60
566
(172 . 36)(167 . 24)
568 last_time_txt += '%dh ' % hours
569 if minutes != 0:
570 last_time_txt += '%dm' % minutes
571
572 time_field = """<i><a href="{0}log/{1}/{2}#{3}">{4}</a></i>""".format(
573
574 last_time_url = "{0}{1}{2}/{3}#{4}".format(
575 get_base(),
576 App_Root,
577 chan,
578 last_time['t'].strftime(Date_Short_Format),
579 last_time['idx'],
580 last_time_txt)
581 last_time['idx'])
582
583 if (days <= Days_Hide) or (chan == selected_chan) or show_all_chans:
584 chan_tbl[chan]['show'] = True
585 chan_list.append({ 'name': chan })
586
587 chan_tbl[chan]['time'] = time_field
588
589 ## Generate channel selector bar :
590 s = """<table align="center" class="chantable"><tr>"""
591 for chan in Channels:
592 if chan_tbl[chan]['show']:
593 s += """<th>{0}</th>""".format(chan_tbl[chan]['link'])
594 s += "</tr><tr>"
595 ## Generate last-activ. links for above :
596 for chan in Channels:
597 if chan_tbl[chan]['show']:
598 s += """<td>{0}</td>""".format(chan_tbl[chan]['time'])
599 # wrap up:
600 s += "</tr></table>"
601 return s
602 chan_list[chan_idx]['last_time_url'] = last_time_url
603 chan_list[chan_idx]['last_time_txt'] = last_time_txt
604 chan_list[chan_idx]['chan_url'] = "{0}{1}{2}{3}".format(
605 get_base(), App_Root, chan, '/' if chan == Default_Chan else '')
606
607 chan_idx += 1
608
609 # Make above callable from inside htm templater:
610 app.jinja_env.globals.update(gen_chanlist=gen_chanlist)
611 return chan_list
612
613
614 # HTML Tag Regex
(257 . 7)(240 . 7)
616 ## Format given log line for display
617 def format_logline(l, highlights = [], select=[], showchan=False):
618 payload = html_escape(l['payload'])
619
620
621 # Format ordinary links:
622 payload = re.sub(stdlinks_re,
623 r'<a href="\1" target=\'_blank\'>\1</a>', payload)
(265 . 20)(248 . 20)
625 # Now also format [link][text] links :
626 payload = re.sub(boxlinks_re,
627 r'<a href="\1" target=\'_blank\'>\2</a>', payload)
628
629
630 # For ancient logs strictly: substitute orig. link with our logger :
631 if l['era'] < 3:
632 payload = re.sub(era1_re,
633 r'<a href="/ilog/trilema/\2', payload)
634
635
636 # Adjust era 2 links in all cases:
637 payload = re.sub(era2_re,
638 r'<a href="/ilog/trilema/\2', payload)
639
640
641 # If this is a search result, illuminate the matched strings:
642 if highlights != []:
643 payload = highlight_text(highlights, payload)
644
645
646 bot = ""
647 if l['speaker'] in Bots:
648 bot = " bot"
(297 . 15)(280 . 15)
650
651 if showchan:
652 speaker = '<small>(' + l['chan'] + ')</small> ' + speaker
653
654
655 # If 'action', annotate:
656 if l['self']:
657 separator = ""
658 payload = "<i>" + payload + "</i>"
659 speaker = "<i>" + speaker + "</i>"
660
661
662 # HTMLize the given line :
663 s = ("<div id='{0}' class='{6}{5}'>"
664 s = ("<div id='{0}' class='logline {6}{5}'>"
665 "<a class='nick' title='{2}'"
666 " href=\"{3}\">{1}</a>{7} {4}</div>").format(l['idx'],
667 speaker,
(318 . 7)(301 . 7)
669 return s
670
671 # Make above callable from inside htm templater:
672 app.jinja_env.globals.update(format_logline=format_logline)
673 app.jinja_env.globals.update(format_logline=format_logline)
674
675
676 @app.route('/rnd/<chan>')
(335 . 10)(318 . 9)
678 return redirect(line_url(rnd_line))
679
680
681 @app.route('/log/<chan>/<date>')
682 @app.route('/log/<chan>', defaults={'date': None})
683 @app.route('/log/', defaults={'chan': Default_Chan, 'date': None})
684 @app.route('/log', defaults={'chan': Default_Chan, 'date': None})
685 @app.route('%s<chan>/<date>' % App_Root)
686 @app.route('%s<chan>' % App_Root, defaults={'date': None})
687 @app.route('%s' % App_Root, defaults={'chan': Default_Chan, 'date': None})
688 def log(chan, date):
689 # Handle rubbish chan:
690 if chan not in Channels:
(353 . 13)(335 . 13)
692
693 # Get possible 'show all'
694 show_all = request.args.get('all', default = 0, type = int)
695
696
697 # Get current time
698 now = datetime.now()
699
700 # Whether we are viewing 'current' tail
701 tail = False
702
703
704 # If viewing 'current' log:
705 if date == None:
706 date = now.strftime(Date_Short_Format)
(370 . 7)(352 . 7)
708 day_start = datetime.strptime(date, Date_Short_Format)
709 except Exception, e:
710 return redirect(url_for('log'))
711
712
713 # Determine the end of the interval being shown
714 day_end = day_start + timedelta(days=1)
715
(383 . 43)(365 . 51)
717 '''select * from loglines where chan=%s
718 and t between %s and %s order by idx asc;''',
719 [chan, day_start, day_end], one=False)
720
721
722 # Optional 'reverse gear' knob:
723 if rev == 1:
724 lines.reverse()
725
726
727 # Generate navbar for the given date:
728 prev_day = ""
729 next_day = ""
730
731
732 prev_t = query_db(
733 '''select t from loglines where chan=%s
734 and t < %s order by idx desc limit 1;''',
735 [chan, day_start], one=True)
736
737
738 if prev_t != None:
739 prev_day = prev_t['t'].strftime(Date_Short_Format)
740
741
742 if not tail:
743 next_t = query_db(
744 '''select t from loglines where chan=%s
745 and t > %s order by idx asc limit 1;''',
746 [chan, day_end], one=True)
747
748
749 if next_t != None:
750 next_day = next_t['t'].strftime(Date_Short_Format)
751
752
753 # Generate url for css file based on config value
754 css_url = url_for('static', filename=CSS_File)
755
756 chan_list = gen_chanlist(chan, show_all)
757
758 # Return the HTMLized text
759 return render_template('log.html',
760 chan = chan,
761 loglines = lines,
762 sel = (sel_start, sel_end),
763 date = date,
764 prev_day = prev_day,
765 next_day = next_day,
766 rev = not rev,
767 show_all = show_all,
768 idle_day = Days_Hide)
769 css_url = css_url,
770 app_root = App_Root,
771 chan = chan,
772 chan_list = chan_list,
773 loglines = lines,
774 sel = (sel_start, sel_end),
775 date = date,
776 prev_day = prev_day,
777 next_day = next_day,
778 rev = not rev,
779 idle_day = Days_Hide,
780 show_all = show_all)
781
782
783 # Primarily for use with 'era 1' and 'era 2' :
(438 . 26)(428 . 26)
785 # If given chan/idx not found:
786 if item == None:
787 return redirect(url_for('log'))
788
789
790 # Determine date where item appears in log :
791 item_date = item['t'].strftime(Date_Short_Format)
792
793
794 # Go there:
795 return redirect("/log/" + chan + "/" + item_date + "#" + idx)
796
797 return redirect(App_Root + chan + "/" + item_date + "#" + idx)
798
799
800 @app.route('/log-raw/<chan>')
801 def rawlog(chan):
802 res = ""
803
804
805 # Handle rubbish chan:
806 if chan not in Channels:
807 return Response("EGGOG: No such Channel!", mimetype='text/plain')
808
809
810 # Get start and end indices:
811 idx_start = request.args.get('istart', default = 0, type = int)
812 idx_end = request.args.get('iend', default = 0, type = int)
813
814
815 # Malformed bounds?
816 if idx_start > idx_end:
817 return Response("EGGOG: Start must precede End!",
(473 . 7)(463 . 7)
819 '''select * from loglines where chan=%s
820 and idx between %s and %s order by idx asc;''',
821 [chan, idx_start, idx_end], one=False)
822
823
824 # Retrieve raw lines in classical Phf format:
825 for l in lines:
826 action = ""
(486 . 7)(476 . 7)
828 action,
829 speaker,
830 l['payload'])
831
832
833 # Return plain text:
834 return Response(res, mimetype='text/plain')
835
(507 . 7)(497 . 11)
837 chan = request.args.get('chan', default = Default_Chan, type = str)
838 query = request.args.get('q', default = '', type = str)
839 offset = request.args.get('after', default = 0, type = int)
840
841 show_all = 0
842
843 if len(query) < Min_Query_Length:
844 return redirect(url_for('log'))
845
846 # channels to search in
847 chans = []
848
(519 . 6)(513 . 7)
850 chans = Channels
851 legend = "<i>all logged channels</i>"
852 showchan = True
853 show_all = 1
854 else:
855 # Handle possible rubbish chan:
856 if chan not in Channels:
(527 . 7)(522 . 7)
858 # search in selected channel only
859 chans = [chan]
860 legend = chan
861
862
863 nres = 0
864 searchres = []
865 tokens_orig = []
(545 . 11)(540 . 11)
867 from_users.append(t.split(':')[1]) # Record user for 'from' query
868 else:
869 tokens_standard.append(t)
870
871
872 from_users = ['%' + sanitize_speaker(t) + '%' for t in from_users]
873 tokens_orig = [re_escape(t) for t in tokens_standard]
874 tokens_formed = ['%' + t + '%' for t in tokens_orig]
875
876
877 # Query is usable; perform the search on DB and get the finds
878 if from_users == []:
879 searchres = query_db(
(571 . 23)(566 . 29)
881 tokens_formed,
882 Max_Search_Results,
883 offset], one=False)
884
885
886 # Number of search results returned in this query
887 nres = len(searchres)
888
889 # Whether to display 'back' button :
890 back = (offset != 0)
891
892
893 # Whether to display 'forward' button :
894 forw = (nres == Max_Search_Results)
895
896
897 # Starting index of search results
898 sres = offset
899
900
901 # Ending index of search results
902 eres = offset + min(nres, Max_Search_Results)
903
904
905 # Generate url for css file based on config value
906 css_url = url_for('static', filename=CSS_File)
907
908 chan_list = gen_chanlist(chan, show_all)
909
910 return render_template('searchres.html',
911 css_url = css_url,
912 query = query,
913 hquery = html_escape(query),
914 legend = legend,
(597 . 9)(598 . 11)
916 forw = forw,
917 psize = Max_Search_Results,
918 chan = chan,
919 chan_list = chan_list,
920 tokens = tokens_orig,
921 loglines = searchres,
922 showchan = showchan)
923 showchan = showchan,
924 show_all = show_all)
925
926 # Comment this out if you don't have one
927 @app.route('/favicon.ico')
-
+ 32E9AFB62FFC8974DAAFFC380B1242D2A6CB0DA5A859CB3D50FB5D6925FA863D1261106ADA02E7984C96BD4BD42F57D2145EB28500B044EA223F4F1142BCDC21
logotron/release_notes.txt
(0 . 0)(1 . 140)
932 ###################
933 2021 May 24 Update:
934 ###################
935 ------------------------------------------------------------------------------------------------------------------------------------------
936 684804 frontend_updates billymg "Changes to HTML/CSS to allow for easier theming. Added config knobs for custom log root and CSS file."
937 ------------------------------------------------------------------------------------------------------------------------------------------
938
939 (1) Tuned HTML and moved CSS to its own file at static/classic.css
940 (2) Added knob to config to specify CSS filename
941 (3) Added knob to config to specify app root
942 (4) Fixed server error when handling search queries less than Min_Query_Length
943 (5) Show/Hide idle chans link now doesn't lose the current day if other than tail
944 (6) Updated README.txt and added this release_notes.txt document
945
946
947 ####################
948 2020 Aug. Update #2:
949 ####################
950 ----------------------------------------------------------------------------
951 641794 aug2020_errata asciilifeform "Stylistic fixes for previous patch."
952 ----------------------------------------------------------------------------
953
954 (1) Additional 'back' and 'forward' buttons at the bottom of search result pages.
955 (2) Fix era1/2 link diddler regexps to handle all (AFAIK) known variants correctly.
956
957
958 ####################
959 2020 Aug. Update #1:
960 ####################
961 --------------------------------------------------------------------------------------------------------------------------------------------------------
962 641773 searchpg_oldlinks asciilifeform "Paginated search; automatic replacement of ancient logger link targets; fix for unicode barf in eat_dump.py."
963 --------------------------------------------------------------------------------------------------------------------------------------------------------
964
965 (1) Implemented fetch-by-channel-and-index (i.e. /ilog/chan/lineindex links).
966 (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.
967 (3) Search results now properly paginated, with working 'back' and 'forward' buttons.
968 (4) Fixed ancient unicode bug in eat_dump.py.
969
970
971 ####################
972 2020 Jul. Update #3:
973 ####################
974 ---------------------------------------------------------------------------------------------------------------
975 641484 navbar_date_auto asciilifeform "Automatically skip empty days in navbar. Handle 'dawn of time' case."
976 ---------------------------------------------------------------------------------------------------------------
977
978 (1) Automatically skip empty days in date navigation bar. At the same time, handle empty days correctly when these are requested.
979 (2) Handle 'dawn of time' case for any given channel, to avoid infinite loop insanity from search engines.
980
981
982 ####################
983 2020 Jul. Update #2:
984 ####################
985 -------------------------------------------------------------------------------
986 641046 search_all_chans asciilifeform "Button to search in all logged chans"
987 -------------------------------------------------------------------------------
988
989 (1) Fix unclosed 'table' tag from earlier.
990 (2) 'Search all chans' button.
991
992
993 ####################
994 2020 Jul. Update #1:
995 ####################
996 -------------------------------------------------------------------------------
997 640165 hide_inactive asciilifeform "Configurable default-hide of idle chans"
998 -------------------------------------------------------------------------------
999
1000 (1) Hide by default chans where no activity in days_hide day (set in config.)
1001 (2) Added 'join as guest' link (via 'kiwi' wwwtronic IRC client)
1002 (3) Example Apache-WSGI configs.
1003
1004
1005 ###################
1006 2019 Oct. 4 Update:
1007 ###################
1008
1009 (1) WWW: Removed ill-conceived 'Tape' knob.
1010 (2) Bot: Added silent-disconnection detector.
1011
1012 Now requires the following value in bot config e.g.:
1013
1014 [irc]
1015 disc_t = 180
1016
1017 This represents the currently-recommended interval: 3 minutes.
1018 If inactivity on the incoming line (incl. pingism) exceeds this interval,
1019 a disconnection is forced. The normal reconnection routine will execute.
1020
1021 To disable forced disconnects, set disc_t to zero.
1022
1023
1024 ####################
1025 2019 Sep. 28 Update:
1026 ####################
1027
1028 (1) Bot: incorporated reconnector fixes.
1029 (2) Bot: 'seen', 'seen-all', and 'version' knobs implemented.
1030 (3) WWW: up/down arrows for mouse-driven operation on pocket comps
1031 (4) WWW: double-quotes prevent breakup of search query
1032 (Note: still needs improvement to force matching on word boundaries)
1033 (5) WWW: demarkation of "action" lines
1034 (6) WWW: Raw 'Tape" knob:
1035
1036 e.g. http://logs.nosuchlabs.com/tape?istart=1937934&iend=1937941
1037 returns:
1038
1039 trilema;1937934;1569444908;asciilifeform;achtung panzers! piz pipe down ?!
1040 trilema;1937935;1569444920;diana_coman;seems so
1041 trilema;1937936;1569444957;asciilifeform;paging BingoBoingo !
1042 trilema;1937937;1569444974;diana_coman;well, apparetly he was connected from piz too, lol
1043 trilema;1937938;1569444982;diana_coman;ossasepia.com (pizarro) is down too, yes
1044 trilema;1937939;1569445010;asciilifeform;dulap unreachable for 1st time since year+ ago when bb elbowed the mains cord
1045 trilema;1937940;1569445073;diana_coman;apparently back on
1046 trilema;1937941;1569445079;diana_coman;wb BingoBoingo !
1047
1048 Still needs a variant of 'eater' that will eat these.
1049
1050
1051 ####################
1052 2019 Aug. 16 Update:
1053 ####################
1054
1055 (1) Bot: added Phf's algo for uniturd processing ('fallback to latin-1').
1056 (2) Bot: May leave IRC password blank in config for use with unregged bots
1057 (3) WWW: 'Checkerboard' colouring; footer links.
1058 (4) WWW: Links in log lines now open in new browser tab (a la Phf's log)
1059 (5) WWW: Can now be loaded 'in reverse gear', http://...../log?rev=1
1060 (6) WWW: 'Random' knob, takes reader to an arbitrary moment in past log.
1061
1062
1063 ####################
1064 2019 Aug. 11 Update:
1065 ####################
1066
1067 (1) Multi-line selection is implemented.
1068
1069 (2) User may request export of raw log lines in Phf-classical format.
1070 E.g. :
1071 http://logs.nosuchlabs.com/log-raw/trilema?istart=1925000&iend=1925500
-
+ DA0FF8B2F9A180FAB9855B9F86CB73BA056090992DE825F970172766FEBDC6CF34986F1C2ED2EBD089208F8BDC4D0EFB926819927A4145D1BCC3D5A29C205E6F
logotron/static/classic.css
(0 . 0)(1 . 98)
1076 .chan-list {
1077 display: table;
1078 list-style: none;
1079 margin: 10px auto 18px auto;
1080 padding: 5px;
1081 border: 1px solid black;
1082 border-spacing: 25px 0;
1083 }
1084
1085 .chan-link-wrapper {
1086 display: inline-block;
1087 margin: 2px 15px;
1088 }
1089
1090 .chan-link {
1091 display: inline-block;
1092 font-weight: bold;
1093 }
1094
1095 .chan-link-wrapper-active .chan-link {
1096 background: yellow;
1097 padding: 1px;
1098 }
1099
1100 .chan-last-active-link {
1101 display: block;
1102 font-style: italic;
1103 }
1104
1105 .chan-filters {
1106 text-align: center;
1107 font-style: italic;
1108 font-size: smaller;
1109 }
1110
1111 .search-form {
1112 text-align: center;
1113 }
1114
1115 .paginator {
1116 margin: 20px 0;
1117 }
1118
1119 .nick {
1120 font-weight: bold;
1121 text-decoration: none;
1122 color: black;
1123 }
1124
1125 .nick:visited {
1126 color: black;
1127 }
1128
1129 .bot,
1130 .bot .nick,
1131 .bot a {
1132 color: #777;
1133 font-weight: normal;
1134 }
1135
1136 .loglines {
1137 word-wrap: break-word;
1138 }
1139
1140 .loglines div:nth-child(2n+1) {
1141 background: #f5f5f5;
1142 }
1143
1144 .loglines div:target {
1145 background: lightyellow;
1146 }
1147
1148 img.inline {
1149 margin: 0.5em auto 1em auto;
1150 display: block;
1151 border: 1px solid black;
1152 width: 34em;
1153 }
1154
1155 img.hist {
1156 margin: 0.5em auto 1em auto;
1157 }
1158
1159 .annotations a {
1160 text-decoration: none;
1161 }
1162
1163 .footer {
1164 text-align: center;
1165 }
1166
1167 .jump {
1168 float:right;
1169 }
1170
1171 .highlight {
1172 background: yellow;
1173 }
-
+ 23B3FCDE9E6F472619C9DC5AB61A54BA16BCC45184E72E8C6F15B1397F56E771C66D22C3FBA478A76298C7A499BF9C9C875FC4F80AA53619B77CE4636FB49CEB
logotron/templates/chan-nav-table.html
(0 . 0)(1 . 21)
1178 <table class='chan-list' align="center">
1179 <thead>
1180 <tr>
1181 {% for chan_item in chan_list %}
1182 <th>
1183 <a class='chan-link {% if chan_item.name == chan %}chan-link-active{% endif %}'
1184 href='{{ chan_item.chan_url }}'><b>{{ chan_item.name }}</b></a>
1185 </th>
1186 {% endfor %}
1187 </tr>
1188 </thead>
1189 <tbody>
1190 <tr>
1191 {% for chan_item in chan_list %}
1192 <td>
1193 <a class='chan-last-active-link' href='{{ chan_item.last_time_url }}'>{{ chan_item.last_time_txt }}</a>
1194 </td>
1195 {% endfor %}
1196 </tr>
1197 </tbody>
1198 </table>
- 6812442D036EF3F18CD4F148F2E7FE1B5833AFD87555625293783246B10224E946A7C89FB8B762B82FD02B7A1B1A86ABD6E1B006B095B254AC9EA446F3ACD003
+ 68FB1BD53530A683F6796169325527D8A720CC306F143C276EB7677720DECED935D780117AD3E75E9BAED2AACF91FD70812050C35ED09E94B684308F5D8006CC
logotron/templates/layout.html
(1 . 161)(1 . 59)
1203 <html>
1204
1205 <head>
1206 <title>
1207 {% block title %}
1208 {% endblock %}
1209 </title>
1210 <title>{% block title %}{% endblock %}</title>
1211
1212 <meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />
1213 <meta name='viewport' content='width=device-width, initial-scale=1' />
1214 <style type='text/css'>
1215 table.chantable {
1216 margin-left:auto;
1217 margin-right:auto;
1218 padding : 5px;
1219 border : 1px solid black;
1220 border-spacing : 25px 0;
1221 }
1222
1223 .nick {
1224 font-weight: bold;
1225 text-decoration: none;
1226 color: black;
1227 }
1228
1229 .nick:visited {
1230 color: black;
1231 }
1232
1233 .re {
1234 display: none;
1235 }
1236
1237 div:hover .re {
1238 display: inline;
1239 }
1240
1241 div {
1242 }
1243
1244 .bot,
1245 .bot .nick,
1246 .bot a {
1247 color: #777;
1248 font-weight: normal;
1249 }
1250
1251 .loglines {
1252 word-wrap: break-word;
1253 }
1254
1255 .loglines div:nth-child(2n) {
1256 }
1257
1258 .loglines div:nth-child(2n+1) {
1259 background: #f5f5f5;
1260 }
1261
1262 .mention {
1263 background: lightyellow !important;;
1264 }
1265
1266 :target,
1267 .active {
1268 background: lightyellow !important;;
1269 }
1270
1271 img.inline {
1272 margin: 0.5em auto 1em auto;
1273 display: block;
1274 border: 1px solid black;
1275 width: 34em;
1276 }
1277
1278 img.hist {
1279 margin: 0.5em auto 1em auto;
1280 }
1281
1282 .annotations a {
1283 text-decoration: none;
1284 }
1285
1286 .navbarblock {
1287 margin-bottom: 1em;
1288 }
1289
1290 .navbar {
1291 float:left;
1292 }
1293
1294 .jump {
1295 float:right;
1296 }
1297
1298 .highlight {
1299 background: yellow !important;;
1300 padding: 1px;
1301 }
1302
1303 </style>
1304 <link rel="stylesheet" href="{{ css_url }}" />
1305 </head>
1306
1307 <body>
1308 <a name="head"></a>
1309
1310 <a name="head">
1311 <div class="chan-nav">
1312 {% include 'chan-nav-table.html' %}
1313
1314 <p>
1315 <table align="center" style="width:100%">
1316 <tr>
1317 <td>
1318 {% if chan != 'all' %}
1319 {{ gen_chanlist( chan, show_all ) | safe }}
1320 {% else %}
1321 {{ gen_chanlist( chan, True ) | safe }}
1322 {% endif %}
1323 </td>
1324 </tr>
1325 </table>
1326
1327 </p>
1328
1329 {% if chan != 'all' %}
1330 <div align="center">
1331 <small>
1332 {% if chan != 'all' %}
1333 <div class="chan-filters">
1334 {% if not show_all %}
1335 <a href="/log/{{ chan }}?all=1">
1336 <i>Show Idle (>{{ idle_day }} d.) Chans</i>
1337 </a>
1338 <a href="?all=1">Show Idle (>{{ idle_day }} d.) Chans</a>
1339 {% else %}
1340 <a href="/log/{{ chan }}">
1341 <i>Hide Idle (>{{ idle_day }} d.) Chans</i>
1342 </a>
1343 <a href="?all=0">Hide Idle (>{{ idle_day }} d.) Chans</a>
1344 {% endif %}
1345 </small>
1346 </div>
1347 {% endif %}
1348 </div>
1349 {% endif %}
1350
1351 <hr>
1352
1353 <form align="center" id="search" method='get' action='/log-search'>
1354 <input type='text' name='q' value='{{ query }}' maxlength='2048' spellcheck='false' size='55' value='' />
1355 {% if chan != 'all' %}
1356 <button type='submit' name='chan' value='{{chan}}'>search {{chan}}</button>
1357 {% endif %}
1358 <button type='submit' name='chan' value='all'>search all chans</button>
1359 </form>
1360 <div class="content">
1361 <hr />
1362
1363 <hr>
1364
1365 {% block body %}{% endblock %}
1366 <form class="search-form" id="search" method='get' action='/log-search'>
1367 <input type='text' class="seach-query-input" name='q' value='{{ query }}' maxlength='2048' spellcheck='false' size='55' value='' />
1368 <span class="search-form-buttons">
1369 {% if chan != 'all' %}
1370 <button type='submit' name='chan' value='{{chan}}'>search {{chan}}</button>
1371 {% endif %}
1372 <button type='submit' name='chan' value='all'>search all chans</button>
1373 </span>
1374 </form>
1375
1376 <hr>
1377 <hr />
1378
1379 <div align="center">
1380 {% if chan != 'all' %}<a href="https://kiwiirc.com/nextclient/irc.freenode.net/{{ chan }}">Visit #{{ chan }} as Guest</a> | <a href="/rnd/{{ chan }}">Random({{ chan }})</a> | {% endif %}<a href="{{ url_for('static', filename='log_db.gz') }}">Download hourly DB snapshot</a> | <a href="http://www.loper-os.org/?p=3452">Get Source Code</a>
1381 {% block body %}{% endblock %}
1382
1383 <hr />
1384 </div>
1385
1386 <a name="tail">
1387 <div class="footer">
1388 {% if chan != 'all' %}
1389 <a href="https://kiwiirc.com/nextclient/irc.freenode.net/{{ chan }}">Visit #{{ chan }} as Guest</a> |
1390 <a href="/rnd/{{ chan }}">Random({{ chan }})</a> |
1391 {% endif %}
1392 <a href="{{ url_for('static', filename='log_db.gz') }}">Download hourly DB snapshot</a> |
1393 <a href="http://www.loper-os.org/?p=3452">Get Source Code</a>
1394 </div>
1395
1396 <a name="tail"></a>
1397 </body>
1398
1399 </html>
-
+ 47C5F0D26C9B58B487DBD36123EF58C9877968C71DF56C7CBABD3AE4839F5490715915E76E527C2C20360CF79722C9112E736ABDE1E77E77AD55D5577FDC1ACB
logotron/templates/log-pagination-links.html
(0 . 0)(1 . 9)
1404 {% if prev_day != '' %}
1405 <a href='{{ app_root }}{{ chan }}/{{ prev_day }}'>← {{ prev_day }}</a>
1406 {% else %}
1407 <i>Dawn of Time</i>
1408 {% endif %}
1409
1410 {% if next_day != '' %}
1411 | <a href='{{ app_root }}{{ chan }}/{{ next_day }}'>{{ next_day }} →</a>
1412 {% endif %}
- 0F906A5DB0ED7F390774FCC6F1D3C09DEA5C2924DDB57DCDE22CCBBFF24BF9440820A23F3063058DBC84C14743CD494EEEB90A0C721988B2DBDF4B629290BFD0
+ F0D6BC05AA0801E18623C6D5C4B70A39499ECFD950D55B0E1691304A4B64AB5189C82CC9842161D70DCEC8018D0915372B10223127139B639CCD138898B09042
logotron/templates/log.html
(6 . 11)(6 . 10)
1417
1418 {% block body %}
1419
1420 <div id="navbarblock">
1421 <p class='navbar'>{% if prev_day != '' %}<a href='/log/{{ chan }}/{{ prev_day }}'>← {{ prev_day }}</a>{% else %}<i>Dawn of Time</i>{% endif %} {% if next_day != '' %}| <a href='/log/{{ chan }}/{{ next_day }}'>{{ next_day }} →</a>{% endif %}</p>
1422 <p class='jump'><a href="#tail">↓</a></p>
1423 <div class="paginator">
1424 {% include 'log-pagination-links.html' %}
1425 <a class='jump jump-down' href="#tail">↓</a>
1426 </div>
1427 <div style="clear: both;"></div>
1428
1429 <div class='loglines'>
1430 {% for l in loglines %}
(18 . 10)(17 . 9)
1432 {% endfor %}
1433 </div>
1434
1435 <div id="navbarblock">
1436 <p class='navbar'>{% if prev_day != '' %}<a href='/log/{{ chan }}/{{ prev_day }}'>← {{ prev_day }}</a>{% else %}<i>Dawn of Time</i>{% endif %} {% if next_day != '' %}| <a href='/log/{{ chan }}/{{ next_day }}'>{{ next_day }} →</a>{% endif %}</p>
1437 <p class='jump'><a href="#head">↑</a></p>
1438 <div class="paginator">
1439 {% include 'log-pagination-links.html' %}
1440 <a class='jump jump-up' href="#head">↑</a>
1441 </div>
1442 <div style="clear: both;"></div>
1443
1444 {% endblock %}
- 6F80E4416E80A5233DBB3C37868C7D34619F38BB3633C6C6F39D5223679720D9BC92D76F58E06A5E995F04F1DC6363CA566A70281438843EE6E45903F8B25759
+ 747FB9409945D5151214E86350FB445F26CD552FDD1A94D2180049C5514682F662DEAE59C6C9AA5EEB10DF2310C3AA25B198CD96994CCE32F0D1FEC8047B5231
logotron/templates/searchres.html
(6 . 31)(6 . 42)
1449
1450 {% block body %}
1451
1452 <div id="navbarblock">
1453 <div align="center"><span>
1454 {% if back %}<a href="/log-search?q={{ query }}&chan={{ chan }}&after={{ sres - psize }}">←</a> | {% endif %}
1455 Results <b>{{ sres + 1 }} ... {{ eres }}</b> found in {{ legend | safe }} for <b>'{{ hquery }}'</b> :{% if forw %} |
1456 <a href="/log-search?q={{ query }}&chan={{ chan }}&after={{ eres }}">→</a>
1457 <div class="paginator">
1458 <div align="center">
1459 {% if back %}
1460 <a href="/log-search?q={{ query }}&chan={{ chan }}&after={{ sres - psize }}">←</a> |
1461 {% endif %}
1462
1463 Results <b>{{ sres + 1 }} ... {{ eres }}</b> found in {{ legend | safe }} for <b>'{{ hquery }}'</b>
1464
1465 {% if forw %}
1466 | <a href="/log-search?q={{ query }}&chan={{ chan }}&after={{ eres }}">→</a>
1467 {% endif %}
1468 </span></div>
1469 </div>
1470 </div>
1471 <div style="clear: both;"></div>
1472
1473 <hr>
1474
1475 {% for l in loglines %}
1476 {{ format_logline(l, tokens, [], showchan) | safe }}
1477 {% endfor %}
1478 <div class='loglines'>
1479 {% for l in loglines %}
1480 {{ format_logline(l, tokens, [], showchan) | safe }}
1481 {% endfor %}
1482 </div>
1483
1484 <hr>
1485
1486 <div id="navbarblock">
1487 <div align="center"><span>
1488 {% if back %}<a href="/log-search?q={{ query }}&chan={{ chan }}&after={{ sres - psize }}">←</a> | {% endif %}{% if forw %}
1489 <div class="paginator">
1490 <div align="center">
1491 {% if back %}
1492 <a href="/log-search?q={{ query }}&chan={{ chan }}&after={{ sres - psize }}">←</a>
1493 {% endif %}
1494
1495 {% if back or forw %} | {% endif %}
1496
1497 {% if forw %}
1498 <a href="/log-search?q={{ query }}&chan={{ chan }}&after={{ eres }}">→</a>
1499 {% endif %}
1500 </span></div>
1501 </div>
1502 </div>
1503 <div style="clear: both;"></div>
1504
1505 {% endblock %}