|
@ -10,6 +10,9 @@ type MessageFrag = { |
|
|
} | { |
|
|
} | { |
|
|
type: 'text', |
|
|
type: 'text', |
|
|
content: string |
|
|
content: string |
|
|
|
|
|
} | { |
|
|
|
|
|
type: 'error', |
|
|
|
|
|
content: string |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
type IrcUser = { |
|
|
type IrcUser = { |
|
@ -31,6 +34,8 @@ type IrcMessage = { |
|
|
type: 'message', |
|
|
type: 'message', |
|
|
user: IrcUser, |
|
|
user: IrcUser, |
|
|
content: MessageFrag[] |
|
|
content: MessageFrag[] |
|
|
|
|
|
} | { |
|
|
|
|
|
type: 'day_change' |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
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; |
|
|
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; |
|
@ -52,6 +57,12 @@ function parseMessage(m: string): MessageFrag[] { |
|
|
|
|
|
|
|
|
function parse(s: string): IrcMessage { |
|
|
function parse(s: string): IrcMessage { |
|
|
s = s.trimEnd(); |
|
|
s = s.trimEnd(); |
|
|
|
|
|
if (s.startsWith("--- Day changed")) { |
|
|
|
|
|
return { |
|
|
|
|
|
type: "day_change" |
|
|
|
|
|
}; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
const time = s.slice(0, 5); |
|
|
const time = s.slice(0, 5); |
|
|
switch (s[6]) { |
|
|
switch (s[6]) { |
|
|
case '-': |
|
|
case '-': |
|
@ -81,14 +92,21 @@ function parse(s: string): IrcMessage { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
throw `couldn't parse message ${s}`; |
|
|
|
|
|
|
|
|
return { |
|
|
|
|
|
time: "no:ts", |
|
|
|
|
|
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}` } }); |
|
|
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 = { |
|
|
type MsgGroup = { |
|
|
user?: IrcUser, |
|
|
user?: IrcUser, |
|
|
type: "status" | "message" | "action", |
|
|
|
|
|
|
|
|
type: "status" | "message" | "action" | "day_change", |
|
|
messages: IrcMessage[] |
|
|
messages: IrcMessage[] |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -100,7 +118,7 @@ function group(messages: IrcMessage[]): MsgGroup[] { |
|
|
let type: "status" | "action" | "message" | undefined = undefined; |
|
|
let type: "status" | "action" | "message" | undefined = undefined; |
|
|
|
|
|
|
|
|
for (const m of messages) { |
|
|
for (const m of messages) { |
|
|
if (m.type === "status") { |
|
|
|
|
|
|
|
|
if (m.type === "status" || m.type === "day_change") { |
|
|
groups.push({ |
|
|
groups.push({ |
|
|
user: discrim, |
|
|
user: discrim, |
|
|
type: type ?? "message", |
|
|
type: type ?? "message", |
|
@ -156,12 +174,20 @@ const render = (c: MessageFrag): JSX.Element => { |
|
|
return JSX.createTextNode(escapeHtmlShitty(c.content)); |
|
|
return JSX.createTextNode(escapeHtmlShitty(c.content)); |
|
|
} else if (c.type === "url") { |
|
|
} else if (c.type === "url") { |
|
|
return <a href={c.link}>{escapeHtmlShitty(c.link)}</a>; |
|
|
return <a href={c.link}>{escapeHtmlShitty(c.link)}</a>; |
|
|
|
|
|
} else if (c.type === "error") { |
|
|
|
|
|
return <span style="color: red; font-family: monospace">{escapeHtmlShitty(c.content)}</span>; |
|
|
} else { |
|
|
} else { |
|
|
throw "impossible!" // thanks TypeScript
|
|
|
throw "impossible!" // thanks TypeScript
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const Message = ({ message, inclTs }: { message: IrcMessage, inclTs?: boolean }) => { |
|
|
const Message = ({ message, inclTs }: { message: IrcMessage, inclTs?: boolean }) => { |
|
|
|
|
|
if (message.type === "day_change") { |
|
|
|
|
|
return <div class="mg-sep"> |
|
|
|
|
|
<span>Today</span> |
|
|
|
|
|
</div> |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
const prefix = message.type === "action" ? "* " : ""; |
|
|
const prefix = message.type === "action" ? "* " : ""; |
|
|
return <div class={`mg-m mg-${message.type}`}> |
|
|
return <div class={`mg-m mg-${message.type}`}> |
|
|
{ |
|
|
{ |
|
@ -173,6 +199,8 @@ const Message = ({ message, inclTs }: { message: IrcMessage, inclTs?: boolean }) |
|
|
|
|
|
|
|
|
function renderMessageGroup(m: MsgGroup): HtmlEnt { |
|
|
function renderMessageGroup(m: MsgGroup): HtmlEnt { |
|
|
const u = m.user; |
|
|
const u = m.user; |
|
|
|
|
|
const m0 = m.messages[0]; |
|
|
|
|
|
|
|
|
if (u !== undefined) { |
|
|
if (u !== undefined) { |
|
|
return <div class="mg-user"> |
|
|
return <div class="mg-user"> |
|
|
<div class="mg-pfp"> |
|
|
<div class="mg-pfp"> |
|
@ -181,7 +209,7 @@ function renderMessageGroup(m: MsgGroup): HtmlEnt { |
|
|
<div class="mg-contents"> |
|
|
<div class="mg-contents"> |
|
|
<span class="mg-ts-u"> |
|
|
<span class="mg-ts-u"> |
|
|
<span class="mg-ts-u-u"> {m.user?.name} </span> |
|
|
<span class="mg-ts-u-u"> {m.user?.name} </span> |
|
|
<span class="mg-ts-u-ts"> {m.messages[0].time} </span> |
|
|
|
|
|
|
|
|
<span class="mg-ts-u-ts"> {m0.type === "day_change" ? "what" : m0.time} </span> |
|
|
</span> |
|
|
</span> |
|
|
|
|
|
|
|
|
<Message message={m.messages[0]} inclTs={false} /> |
|
|
<Message message={m.messages[0]} inclTs={false} /> |
|
|