mirror of
https://github.com/thelounge/thelounge.git
synced 2026-07-30 22:55:50 -04:00
Closes #5052 Multi-line input is currently sent as one PRIVMSG per line, so a paste arrives as N unrelated messages. On networks that support [draft/multiline](https://ircv3.net/specs/extensions/multiline) it can be sent as a single BATCH and stays one message everywhere. ## Summary Sending goes through `sayMultiline()` in `plugins/inputs/msg.ts` when the network advertises limits we can respect (`multilineLimits()`); if the batch is over max-bytes/max-lines the send falls back to individual messages, and the local echo follows whichever path was actually taken so the sender sees what everyone else got. `Client.input()` stops splitting on newlines when the target network has the cap. Inbound batches arrive from irc-framework already reassembled and are flagged `multiline` on the `Msg`, which drives line-by-line rendering (`parse()` joins lines with `<br>`), one entry per message in the text logs, and a first-non-empty-line notification preview. `MULTILINE_*` standard replies get readable text. No pin bump: `master` already points at the irc-framework commit that carries multiline (kiwiirc/irc-framework#414). ## Test Plan - New unit tests for the send path (batch, fallback on max-bytes, no limits advertised, blank lines), the text log format, `parse()`, and the standard replies. - Ran the server against a local IRCd advertising `draft/multiline` and confirmed on the wire: a 3-line message goes out as one `BATCH … draft/multiline` with three frames; with the cap advertised bare (no limits) or with `max-bytes` too small it degrades to three plain PRIVMSGs and the local echo degrades with it; a CRLF paste sends clean frames and echoes without stray `\r`; an inbound batch from another user renders as one message. - Not verified: rendering was checked through unit tests rather than in a browser.
104 lines
2.1 KiB
TypeScript
104 lines
2.1 KiB
TypeScript
import _ from "lodash";
|
|
import {MessageType, LinkPreview, UserInMessage} from "../../shared/types/msg";
|
|
|
|
class Msg {
|
|
from!: UserInMessage;
|
|
id!: number;
|
|
msgid?: string;
|
|
replyTo?: string;
|
|
replyToNick?: string;
|
|
replyToText?: string;
|
|
previews!: LinkPreview[];
|
|
text!: string;
|
|
type!: MessageType;
|
|
self!: boolean;
|
|
time!: Date;
|
|
hostmask!: string;
|
|
target!: UserInMessage;
|
|
// TODO: new_nick is only on MessageType.NICK,
|
|
// we should probably make Msgs that extend this class and use those
|
|
// throughout. I'll leave any similar fields below.
|
|
new_nick!: string;
|
|
highlight?: boolean;
|
|
showInActive?: boolean;
|
|
new_ident!: string;
|
|
new_host!: string;
|
|
ctcpMessage!: string;
|
|
command!: string;
|
|
invitedYou!: boolean;
|
|
gecos!: string;
|
|
account!: boolean;
|
|
|
|
// these are all just for error:
|
|
error!: string;
|
|
nick!: string;
|
|
channel!: string;
|
|
reason!: string;
|
|
|
|
raw_modes!: any;
|
|
when!: Date;
|
|
whois!: any;
|
|
users!: string[];
|
|
statusmsgGroup?: string;
|
|
params!: string[];
|
|
multiline?: boolean;
|
|
|
|
constructor(attr?: Partial<Msg>) {
|
|
// Some properties need to be copied in the Msg object instead of referenced
|
|
if (attr) {
|
|
["from", "target"].forEach((prop) => {
|
|
if (attr[prop]) {
|
|
this[prop] = {
|
|
mode: attr[prop].mode,
|
|
nick: attr[prop].nick,
|
|
};
|
|
}
|
|
});
|
|
}
|
|
|
|
_.defaults(this, attr, {
|
|
from: {},
|
|
id: 0,
|
|
previews: [],
|
|
text: "",
|
|
type: MessageType.MESSAGE,
|
|
self: false,
|
|
});
|
|
|
|
if (this.time) {
|
|
this.time = new Date(this.time);
|
|
} else {
|
|
this.time = new Date();
|
|
}
|
|
}
|
|
|
|
findPreview(link: string) {
|
|
return this.previews.find((preview) => preview.link === link);
|
|
}
|
|
|
|
isLoggable() {
|
|
if (this.type === MessageType.TOPIC) {
|
|
// Do not log topic that is sent on channel join
|
|
return !!this.from.nick;
|
|
}
|
|
|
|
switch (this.type) {
|
|
case MessageType.MONOSPACE_BLOCK:
|
|
case MessageType.ERROR:
|
|
case MessageType.TOPIC_SET_BY:
|
|
case MessageType.MODE_CHANNEL:
|
|
case MessageType.MODE_USER:
|
|
case MessageType.RAW:
|
|
case MessageType.WHOIS:
|
|
case MessageType.PLUGIN:
|
|
return false;
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
export default Msg;
|
|
|
|
export type Message = Msg;
|