converts irc logs to html files that look like discord
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

193 lines
4.5 KiB

import { escapeHtmlShitty, HtmlEnt, JSX } from './jsx';
const { readFileSync } = require("fs");
const contents = readFileSync(0);
const lines: string[] = contents.toString().split('\n');
type IrcUser = {
mode?: '+' | '@' | ' ',
name: string
};
type IrcMessage = {
time: string,
type: 'status',
content: string
} | {
time: string,
type: 'action',
user: IrcUser,
content: string
} | {
time: string,
type: 'message',
user: IrcUser,
content: string
};
function parse(s: string): IrcMessage {
s = s.trimEnd();
const time = s.slice(0, 5);
switch(s[6]) {
case '-':
return {
time,
type: 'status',
content: 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: s.slice(10 + name.length)
}
}
case ' ': {
const name = s.slice(8).split(' ')[1];
return {
time,
type: 'action',
user: { name },
content: s.slice(10 + name.length)
}
}
}
throw `couldn't 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",
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") {
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 Message = ( {message, inclTs }: { message: IrcMessage, inclTs?: boolean }) => {
let txt = escapeHtmlShitty(message.content);
if (message.type === "action") {
txt = "* " + txt;
}
return <div class={ `mg-m mg-${message.type}` }>
{
(inclTs === undefined || inclTs === true) ? <span class="mg-ts"> { message.time } </span> : undefined
}
<span class={ `mg-txt mg-${message.type}` }> { txt } </span>
</div>
}
function renderMessageGroup(m: MsgGroup): HtmlEnt {
const u = m.user;
console.error(m);
if (u !== undefined) {
return <div class="mg-user">
<div class="mg-pfp">
<img src={ `https://offtopia.org/pages/people/${u.name.toLocaleLowerCase()}.jpg` } alt={ `${u.name}'s profile picture`} />
</div>
<div class="mg-contents">
<span class="mg-ts-u">
<span class="mg-ts-u-u"> { m.user?.name } </span>
<span class="mg-ts-u-ts"> { m.messages[0].time } </span>
</span>
<Message message={m.messages[0]} inclTs={false} />
{
m.messages.slice(1).map(x =>
<Message message={x} />
)
}
</div>
</div>
} else {
if (m.messages.length < 1) {
return <span />
}
return <div class="mg-no-user">
{ m.messages.map(x => <Message message={x} />) }
</div>;
}
}
const rendered = groups.map(renderMessageGroup);
const elem = <html>
<body>
{ rendered }
<svg id="squircle-container" width="0" height="0">
<clipPath id="squircle" clipPathUnits="objectBoundingBox">
<path fill="red" stroke="none" d="M 0,0.5 C 0,0 0,0 0.5,0 S 1,0 1,0.5 1,1 0.5,1 0,1 0,0.5" />
</clipPath>
</svg>
</body>
<style>
{ readFileSync("style.css") }
</style>
</html>;
console.log(elem.toHtmlStr());