Skip to content

Commit

Permalink
Merge pull request #15 from PMunch/master
Browse files Browse the repository at this point in the history
Modernise NimBot
  • Loading branch information
Araq authored Jun 3, 2020
2 parents 98cc031 + 1d80bb8 commit 9d705dd
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 66 deletions.
36 changes: 18 additions & 18 deletions src/irclog.nim
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,37 @@ from xmltree import escape
import json except to

type
TLogger* = object of TObject # Items get erased when new day starts.
startTime*: TTimeInfo
TLogger* = object of RootObj # Items get erased when new day starts.
startTime*: DateTime
logFilepath*: string
logFile*: TFile
logFile*: File
PLogger* = ref TLogger

const
webFP = {fpUserRead, fpUserWrite, fpUserExec,
fpGroupRead, fpGroupExec, fpOthersRead, fpOthersExec}

proc loadLogger*(f: string): PLogger =
new(result)
new result
let logs = readFile(f)
let lines = logs.splitLines()
# Line 1: Start time
result.startTime = fromSeconds(to[float](lines[0])).getGMTime()
result.startTime = fromUnixFloat(to[float](lines[0])).utc()
if not open(result.logFile, f, fmAppend):
echo("Warning: Could not open logger: " & f)
result.logFilepath = f.splitFile.dir

proc writeFlush(file: TFile, s: string) =
proc writeFlush(file: File, s: string) =
file.write(s)
file.flushFile()

proc newLogger*(logFilepath: string): PLogger =
let startTime = getTime().getGMTime()
let log = logFilepath / startTime.format("dd'-'MM'-'yyyy'.logs'")
let startTime = getTime().utc()
let log = logFilepath / startTime.format("dd'-'MM'-'yyyy'.json'")
if existsFile(log):
result = loadLogger(log)
else:
new(result)
new result
result.startTime = startTime
result.logFilepath = logFilepath
doAssert open(result.logFile, log, fmAppend)
Expand All @@ -45,30 +45,30 @@ proc `$`(s: seq[string]): string =
strutils.escape(x)
result = "[" & join(escaped, ",") & "]"

proc writeLog(logger: PLogger, msg: TIRCEvent) =
logger.logFile.writeFlush($$(time: getTime(), msg: msg) & "\n")
proc writeLog(logger: PLogger, msg: IRCEvent) =
logger.logFile.writeFlush($(%*{"time": getTime(), "msg": msg}) & "\n")

proc log*(logger: PLogger, msg: TIRCEvent) =
proc log*(logger: PLogger, msg: IRCEvent) =
if msg.origin != "#nim" and msg.cmd notin {MQuit, MNick}: return
if getTime().getGMTime().yearday != logger.startTime.yearday:
if getTime().utc().yearday != logger.startTime.yearday:
# It's time to cycle to next day.
# Reset logger.
logger.logFile.close()
logger.startTime = getTime().getGMTime()
let log = logger.logFilepath / logger.startTime.format("dd'-'MM'-'yyyy'.logs'")
logger.startTime = getTime().utc()
let log = logger.logFilepath / logger.startTime.format("dd'-'MM'-'yyyy'.json'")
doAssert open(logger.logFile, log, fmAppend)
# Write start time
logger.logFile.writeFlush($epochTime() & "\n")

case msg.cmd
of MPrivMsg, MJoin, MPart, MNick, MQuit: # TODO: MTopic? MKick?
#logger.items.add((getTime(), msg))
#logger.save(logger.logFilepath / logger.startTime.format("dd'-'MM'-'yyyy'.json'"))
writeLog(logger, msg)
else: nil
else: discard

proc log*(logger: PLogger, nick, msg, chan: string) =
var m: TIRCEvent
var m: IRCEvent
m.typ = EvMsg
m.cmd = MPrivMsg
m.params = @[chan, msg]
Expand Down
164 changes: 148 additions & 16 deletions src/irclogrender.nim
Original file line number Diff line number Diff line change
@@ -1,41 +1,172 @@
import irc, htmlgen, times, strutils, marshal, os, xmltree, re
from jester import PRequest, makeUri
import irc, htmlgen, times, strutils, marshal, os, xmltree, re, json
from jester import Request, makeUri
import irclog
import strtabs

type
# These legacy types are required to properly marshal the old format so old
# logs can still be read. The only differences are the timestamps and the new
# json format for strtabs.
LegacyIrcEvent = object
case typ: IrcEventType
of EvConnected:
nil
of EvDisconnected:
nil
of EvTimeout:
nil
of EvMsg:
cmd: IrcMType
nick, user, host, servername: string
numeric: string
tags: LegacyStringTableRef
params: seq[string]
origin: string
raw: string
timestamp: int64
LegacyStringTableRef = ref StringTableObj
LegacyEntry = tuple[time: int64, msg: LegacyIRCEvent]

Entry = tuple[time: Time, msg: IRCEvent]
TLogRenderer = object of TLogger
items*: seq[tuple[time: TTime, msg: TIRCEvent]] ## Only used for HTML gen
items*: seq[Entry] ## Only used for HTML gen
PLogRenderer* = ref TLogRenderer

proc toNewEntry(entry: LegacyEntry): Entry =
result.time = fromUnix(entry.time)
result.msg = IRCEvent(
typ: entry.msg.typ
)
if result.msg.typ == EvMsg:
result.msg.cmd = entry.msg.cmd
result.msg.nick = entry.msg.nick
result.msg.user = entry.msg.user
result.msg.host = entry.msg.host
result.msg.servername = entry.msg.servername
result.msg.numeric = entry.msg.numeric
result.msg.tags = cast[StringTableRef](entry.msg.tags)
result.msg.params = entry.msg.params
result.msg.origin = entry.msg.origin
result.msg.raw = entry.msg.raw
result.msg.timestamp = entry.msg.timestamp.fromUnix

proc loadRenderer*(f: string): PLogRenderer =
new(result)
new result
result.items = @[]
let logs = readFile(f)
let lines = logs.splitLines()
var i = 1
# Line 1: Start time
result.startTime = fromSeconds(to[float](lines[0])).getGMTime()
result.startTime = fromUnixFloat(to[float](lines[0])).utc()

result.logFilepath = f.splitFile.dir
echo "Reading file: ", f, ": ", f.endsWith(".logs")
while i < lines.len:
if lines[i] != "":
result.items.add(to[tuple[time: TTime, msg: TIRCEvent]](lines[i]))
if f.endsWith(".logs"):
result.items.add(marshal.to[LegacyEntry](lines[i]).toNewEntry)
elif f.endsWith(".json"):
result.items.add(json.to(lines[i].parseJson, Entry))
inc i

const IRCColours = [
"#e6e6e6",
"#000000",
"#bd93f9",
"#50fa7b",
"#ff5555",
"#ff5555",
"#ff79c6",
"#ffb86c",
"#f1fa8c",
"#50fa7b",
"#8be9fd",
"#8be9fd",
"#bd93f9",
"#ff79c6",
"#b3b3b3",
"#cccccc"
]

proc colourMessage(msg: string): string =
var
currentChar = 0
c: char
openedTags = 0
currentStyle = ""
bold, italic, underline = false
template switchStyle(style: var bool, css: string) =
if not style:
currentStyle &= css
style = true
else:
result &= "</span>"
openedTags -= 1
style = false
while currentChar < msg.len:
c = msg[currentChar]
inc currentChar
case ord c:
of 0x02: switchStyle bold, "font-weight: bold;"
of 0x1D: switchStyle italic, "font-style: italic;"
of 0x1F: switchStyle underline, "text-decoration: underline;"
of 0x03:
let colourCode = to[int](msg[currentChar..^1])
currentStyle &= "color: " & IRCColours[colourCode] & ";"
currentChar += 2
of 0x0F:
result &= "</span>".repeat openedTags
openedTags = 0
bold = false
italic = false
underline = false
else:
if currentStyle.len != 0:
result &= "<span style=\"" & currentStyle & "\">"
openedTags += 1
currentStyle = ""
result &= c
result &= "</span>".repeat openedTags

const NickColours = [
"#6272a4",
"#8be9fd",
"#50fa7b",
"#ffb86c",
"#ff79c6",
"#bd93f9",
"#ff5555",
"#f1fa8c",
"#6272a4",
"#8be9fd",
"#50fa7b",
"#ffb86c",
"#ff79c6",
"#bd93f9",
"#ff5555",
"#f1fa8c"
]
proc colourNick(msg: string): string =
var hash = 0
for c in msg:
hash += ord c
"<span style=\"color: " & NickColours[hash mod 16] & "\">" & msg & "</span>"

proc renderMessage(msg: string): string =
# Transforms anything that looks like a hyperlink into one in the HTML.
let pattern = re"(https?|ftp)://[^\s/$.?#].[^\s]*"
let pattern = re"(https?|ftp)://[^\s/$.?#].[^\s\x02\x1D\x1F\x03\x0F]*"
result = ""

var i = 0
while true:
let (first, last) = msg.findBounds(pattern, start = i)
if first == -1: break
echo(msg[i .. first-1], "|", msg[first .. last])
#echo(msg[i .. first-1], "|", msg[first .. last])
result.add(xmltree.escape(msg[i .. first-1]))
result.add(a(href=msg[first .. last], xmltree.escape(msg[first .. last])))
i = last+1
result.add(xmltree.escape(msg[i .. ^1]))
result = result.colourMessage()

proc renderItems(logger: PLogRenderer, isToday: bool): string =
result = ""
Expand All @@ -53,17 +184,17 @@ proc renderItems(logger: PLogRenderer, isToday: bool): string =
of MKick:
c = "kick"
else:
nil
discard
var message = i.msg.params[i.msg.params.len-1]
if message.startswith("\x01ACTION "):
c = "action"
message = message[8 .. ^2]

let timestamp = i.time.getGMTime().format("HH':'mm':'ss")
let timestamp = i.time.utc().format("HH':'mm':'ss")
let prefix = if isToday: logger.startTime.format("dd'-'MM'-'yyyy'.html'") & "#" else: "#"
if c == "":
result.add(tr(td(a(id=timestamp, href=prefix & timestamp, class="time", timestamp)),
td(class="nick", xmltree.escape(i.msg.nick)),
td(class="nick", xmltree.escape(i.msg.nick).colourNick),
td(id="M" & timestamp, class="msg", message.renderMessage)))
else:
case c
Expand All @@ -87,15 +218,15 @@ proc renderItems(logger: PLogRenderer, isToday: bool): string =
td(class="nick", "*"),
td(id="M" & timestamp, class="msg", xmltree.escape(message))))

proc renderHtml*(logger: PLogRenderer, req: jester.PRequest): string =
let today = getTime().getGMTime()
proc renderHtml*(logger: PLogRenderer, req: jester.Request): string =
let today = getTime().utc()
let isToday = logger.startTime.monthday == today.monthday and
logger.startTime.month == today.month and
logger.startTime.year == today.year
let previousDay = logger.startTime - (initInterval(days=1))
let previousDay = logger.startTime - (initTimeInterval(days=1))
let prevUrl = req.makeUri(previousDay.format("dd'-'MM'-'yyyy'.html'"),
absolute = false)
let nextDay = logger.startTime + (initInterval(days=1))
let nextDay = logger.startTime + (initTimeInterval(days=1))
let nextUrl =
if isToday: ""
else: req.makeUri(nextDay.format("dd'-'MM'-'yyyy'.html'"), absolute = false)
Expand All @@ -105,12 +236,13 @@ proc renderHtml*(logger: PLogRenderer, req: jester.PRequest): string =
meta(content="text/html; charset=UTF-8", `http-equiv` = "Content-Type"),
link(rel="stylesheet", href=req.makeUri("css/boilerplate.css", absolute = false)),
link(rel="stylesheet", href=req.makeUri("css/log.css", absolute = false)),
link(rel="stylesheet", href="https://fonts.googleapis.com/css?family=Lato:400,600,900", type="text/css"),
script(src="js/log.js", type="text/javascript")
),
body(
htmlgen.`div`(id="controls",
a(href=prevUrl, "<<"),
span(logger.startTime.format("dd'-'MM'-'yyyy")),
span(logger.startTime.format(" dd'-'MM'-'yyyy ")),
(if nextUrl == "": span(">>") else: a(href=nextUrl, ">>"))
),
hr(),
Expand Down
Loading

0 comments on commit 9d705dd

Please sign in to comment.