import { escapeHtmlShitty, HtmlEnt, JSX } from './jsx'; const { readFileSync } = require("fs"); const contents = readFileSync(0); const lines: string[] = contents.toString().split('\n'); type MessageFrag = { type: 'url', link: string } | { type: 'text', content: string } | { type: 'error', content: string }; type IrcUser = { mode?: '+' | '@' | ' ', name: string }; type IrcMessage = { time: string, type: 'status', content: MessageFrag[] } | { time: string, type: 'action', user: IrcUser, content: MessageFrag[] } | { time: string, type: 'message', user: IrcUser, content: MessageFrag[] } | { type: 'day_change' } | { type: 'redacted' }; const URL = /(((?:https?|gopher):\/\/|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/|spotify:track:)((?:\(?[^\s()<>]+\)?)*[^ \s`!\[\]{};:\'".,<>?\xab\xbb\u201c\u201d\u2018\u2019]))/i; function parseMessage(m: string): MessageFrag[] { m = m.trimEnd(); const parts: MessageFrag[] = []; let match: RegExpMatchArray | null; while ((match = m.match(URL)) != null) { parts.push({ type: 'text', content: m.slice(0, match.index) }); parts.push({ type: 'url', link: match[0] }); m = m.slice((match.index ?? 0) + match[0].length); } if (m !== "") { parts.push({ type: 'text', content: m }); } return parts; } function parse(s: string): IrcMessage { s = s.trimEnd(); if (s.startsWith("--- Day changed")) { return { type: "day_change" }; } else if (s === "[REDACTED]") { return { type: "redacted" }; } const time = s.slice(0, 5); switch (s[6]) { case '-': return { time, type: 'status', content: parseMessage(s.slice(10)) }; case '<': { const mode = s[7]; if (mode !== '@' && mode !== '+' && mode !== ' ') throw `invalid mode: ${mode}`; const name = s.slice(8).split('>')[0]; return { time, type: 'message', user: { mode, name }, content: parseMessage(s.slice(10 + name.length)) } } case ' ': { const name = s.slice(8).split(' ')[1]; return { time, type: 'action', user: { name }, content: parseMessage(s.slice(10 + name.length)) } } } return { time: "error", type: 'status', content: [{ type: 'error', content: `could not parse message: ${s}` }] }; }; const messages = lines.map(l => l.trim()).filter(l => l !== '.' && l !== "").map((v, i) => { try { return parse(v) } catch (e) { throw `${e} ${i}` } }); type MsgGroup = { user?: IrcUser, type: "status" | "message" | "action" | "day_change" | "redacted", messages: IrcMessage[] } function group(messages: IrcMessage[]): MsgGroup[] { const groups: MsgGroup[] = []; let group: IrcMessage[] = []; let discrim: IrcUser | undefined = undefined; let type: "status" | "action" | "message" | undefined = undefined; for (const m of messages) { if (m.type === "status" || m.type === "day_change" || m.type == "redacted") { groups.push({ user: discrim, type: type ?? "message", messages: group }); discrim = undefined; type = "status"; group = []; groups.push({ type: m.type, messages: [m] }) continue; } else if (discrim !== undefined && (m.user.mode === undefined || m.user.mode === discrim.mode) && m.user.name === discrim.name) { group.push(m) } else { if (group.length !== 0) groups.push({ user: discrim, type: type ?? "message", messages: group }); group = [m]; discrim = m.user; type = m.type; } } if (group.length !== 0) { if (type === "status") { groups.push({ user: undefined, type, messages: group }) } else if (discrim !== undefined) { groups.push({ user: discrim, type: type ?? "message", messages: group }); } } return groups; } const groups = group(messages); const render = (c: MessageFrag): JSX.Element => { if (c.type === "text") { return JSX.createTextNode(escapeHtmlShitty(c.content)); } else if (c.type === "url") { return {escapeHtmlShitty(c.link)}; } else if (c.type === "error") { return {escapeHtmlShitty(c.content)}; } else { throw "impossible!" // thanks TypeScript } } const Message = ({ message, inclTs }: { message: IrcMessage, inclTs?: boolean }) => { if (message.type === "day_change") { return
Today
}; if (message.type === "redacted") { return
[REDACTED]
; } return
{ (inclTs === undefined || inclTs === true) ? {message.time} : undefined } { message.content.map(c => render(c)) }
} function renderMessageGroup(m: MsgGroup): HtmlEnt { const u = m.user; const m0 = m.messages[0]; if (u !== undefined) { return
{`${u.name}'s
{m.user?.name} {(m0.type === "day_change" || m0.type === "redacted") ? "what" : m0.time} { m.messages.slice(1).map(x => ) }
} else { if (m.messages.length < 1) { return
} return
{m.messages.map(x => )}
; } } const rendered = groups.map(renderMessageGroup); const elem = {rendered} ; console.log(elem.toHtmlStr());