@lordbex/thelounge 4.4.4-blowfish → 4.5.0-blowfish-pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/defaults/config.js +31 -2
- package/dist/package.json +93 -91
- package/dist/server/client.js +188 -194
- package/dist/server/clientManager.js +65 -63
- package/dist/server/command-line/index.js +44 -43
- package/dist/server/command-line/install.js +37 -70
- package/dist/server/command-line/outdated.js +12 -17
- package/dist/server/command-line/start.js +25 -26
- package/dist/server/command-line/storage.js +26 -31
- package/dist/server/command-line/uninstall.js +16 -23
- package/dist/server/command-line/upgrade.js +20 -26
- package/dist/server/command-line/users/add.js +33 -40
- package/dist/server/command-line/users/edit.js +18 -24
- package/dist/server/command-line/users/index.js +12 -16
- package/dist/server/command-line/users/list.js +11 -39
- package/dist/server/command-line/users/remove.js +16 -22
- package/dist/server/command-line/users/reset.js +34 -35
- package/dist/server/command-line/utils.js +231 -87
- package/dist/server/config.js +61 -52
- package/dist/server/helper.js +29 -28
- package/dist/server/identification.js +39 -34
- package/dist/server/index.js +1 -3
- package/dist/server/log.js +19 -16
- package/dist/server/models/chan.js +36 -33
- package/dist/server/models/msg.js +15 -19
- package/dist/server/models/network.js +88 -86
- package/dist/server/models/prefix.js +4 -7
- package/dist/server/models/user.js +5 -10
- package/dist/server/path-helper.js +8 -0
- package/dist/server/plugins/auth/ldap.js +177 -112
- package/dist/server/plugins/auth/local.js +10 -15
- package/dist/server/plugins/auth.js +6 -35
- package/dist/server/plugins/changelog.js +30 -27
- package/dist/server/plugins/clientCertificate.js +33 -37
- package/dist/server/plugins/dev-server.js +15 -21
- package/dist/server/plugins/inputs/action.js +9 -14
- package/dist/server/plugins/inputs/away.js +1 -3
- package/dist/server/plugins/inputs/ban.js +9 -14
- package/dist/server/plugins/inputs/blow.js +9 -14
- package/dist/server/plugins/inputs/connect.js +5 -10
- package/dist/server/plugins/inputs/ctcp.js +7 -12
- package/dist/server/plugins/inputs/disconnect.js +1 -3
- package/dist/server/plugins/inputs/ignore.js +23 -29
- package/dist/server/plugins/inputs/ignorelist.js +12 -18
- package/dist/server/plugins/inputs/index.js +8 -34
- package/dist/server/plugins/inputs/invite.js +7 -12
- package/dist/server/plugins/inputs/kick.js +7 -12
- package/dist/server/plugins/inputs/kill.js +1 -3
- package/dist/server/plugins/inputs/list.js +1 -3
- package/dist/server/plugins/inputs/mode.js +10 -15
- package/dist/server/plugins/inputs/msg.js +13 -18
- package/dist/server/plugins/inputs/mute.js +9 -15
- package/dist/server/plugins/inputs/nick.js +9 -14
- package/dist/server/plugins/inputs/notice.js +5 -7
- package/dist/server/plugins/inputs/part.js +11 -16
- package/dist/server/plugins/inputs/quit.js +7 -13
- package/dist/server/plugins/inputs/rainbow.js +55 -0
- package/dist/server/plugins/inputs/raw.js +1 -3
- package/dist/server/plugins/inputs/rejoin.js +7 -12
- package/dist/server/plugins/inputs/topic.js +7 -12
- package/dist/server/plugins/inputs/whois.js +1 -3
- package/dist/server/plugins/irc-events/away.js +14 -20
- package/dist/server/plugins/irc-events/cap.js +16 -22
- package/dist/server/plugins/irc-events/chghost.js +14 -13
- package/dist/server/plugins/irc-events/connection.js +61 -63
- package/dist/server/plugins/irc-events/ctcp.js +22 -28
- package/dist/server/plugins/irc-events/error.js +20 -26
- package/dist/server/plugins/irc-events/help.js +7 -13
- package/dist/server/plugins/irc-events/info.js +7 -13
- package/dist/server/plugins/irc-events/invite.js +7 -13
- package/dist/server/plugins/irc-events/join.js +30 -27
- package/dist/server/plugins/irc-events/kick.js +21 -17
- package/dist/server/plugins/irc-events/link.js +75 -96
- package/dist/server/plugins/irc-events/list.js +23 -26
- package/dist/server/plugins/irc-events/message.js +46 -52
- package/dist/server/plugins/irc-events/mode.js +66 -63
- package/dist/server/plugins/irc-events/modelist.js +29 -35
- package/dist/server/plugins/irc-events/motd.js +10 -16
- package/dist/server/plugins/irc-events/names.js +3 -6
- package/dist/server/plugins/irc-events/nick.js +26 -23
- package/dist/server/plugins/irc-events/part.js +19 -15
- package/dist/server/plugins/irc-events/quit.js +17 -14
- package/dist/server/plugins/irc-events/sasl.js +9 -15
- package/dist/server/plugins/irc-events/spgroups.js +38 -0
- package/dist/server/plugins/irc-events/spjoin.js +52 -0
- package/dist/server/plugins/irc-events/topic.js +12 -18
- package/dist/server/plugins/irc-events/unhandled.js +12 -12
- package/dist/server/plugins/irc-events/welcome.js +7 -13
- package/dist/server/plugins/irc-events/whois.js +20 -24
- package/dist/server/plugins/massEventAggregator.js +214 -0
- package/dist/server/plugins/messageStorage/sqlite.js +322 -141
- package/dist/server/plugins/messageStorage/text.js +21 -26
- package/dist/server/plugins/packages/index.js +105 -74
- package/dist/server/plugins/packages/publicClient.js +7 -16
- package/dist/server/plugins/packages/themes.js +11 -16
- package/dist/server/plugins/storage.js +28 -33
- package/dist/server/plugins/sts.js +12 -17
- package/dist/server/plugins/uploader.js +40 -43
- package/dist/server/plugins/webpush.js +23 -51
- package/dist/server/server.js +318 -271
- package/dist/server/storageCleaner.js +29 -37
- package/dist/server/utils/fish.js +7 -14
- package/dist/shared/irc.js +3 -6
- package/dist/shared/linkify.js +7 -14
- package/dist/shared/types/chan.js +6 -9
- package/dist/shared/types/changelog.js +1 -2
- package/dist/shared/types/config.js +1 -2
- package/dist/shared/types/mention.js +1 -2
- package/dist/shared/types/msg.js +3 -5
- package/dist/shared/types/network.js +1 -2
- package/dist/shared/types/storage.js +1 -2
- package/dist/shared/types/user.js +1 -2
- package/index.js +14 -10
- package/package.json +93 -91
- package/public/css/style.css +9 -6
- package/public/css/style.css.map +1 -1
- package/public/fonts/font-awesome/fa-brands-400.ttf +0 -0
- package/public/fonts/font-awesome/fa-brands-400.woff2 +0 -0
- package/public/fonts/font-awesome/fa-duotone-900.ttf +0 -0
- package/public/fonts/font-awesome/fa-duotone-900.woff2 +0 -0
- package/public/fonts/font-awesome/fa-light-300.ttf +0 -0
- package/public/fonts/font-awesome/fa-light-300.woff2 +0 -0
- package/public/fonts/font-awesome/fa-regular-400.ttf +0 -0
- package/public/fonts/font-awesome/fa-regular-400.woff2 +0 -0
- package/public/fonts/font-awesome/fa-solid-900.ttf +0 -0
- package/public/fonts/font-awesome/fa-solid-900.woff2 +0 -0
- package/public/fonts/font-awesome/fa-thin-100.ttf +0 -0
- package/public/fonts/font-awesome/fa-thin-100.woff2 +0 -0
- package/public/fonts/font-awesome/fa-v4compatibility.ttf +0 -0
- package/public/fonts/font-awesome/fa-v4compatibility.woff2 +0 -0
- package/public/js/bundle.js +1 -1
- package/public/js/bundle.js.map +1 -1
- package/public/js/bundle.vendor.js +1 -1
- package/public/js/bundle.vendor.js.LICENSE.txt +24 -6
- package/public/js/bundle.vendor.js.map +1 -1
- package/public/service-worker.js +1 -1
- package/public/themes/default.css +1 -1
- package/public/themes/morning.css +1 -1
- package/dist/webpack.config.js +0 -224
|
@@ -1,20 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const msg_1 = __importDefault(require("../../models/msg"));
|
|
7
|
-
const msg_2 = require("../../../shared/types/msg");
|
|
8
|
-
exports.default = (function (irc, network) {
|
|
9
|
-
const client = this;
|
|
10
|
-
irc.on("invite", function (data) {
|
|
1
|
+
import Msg from "../../models/msg.js";
|
|
2
|
+
import { MessageType } from "../../../shared/types/msg.js";
|
|
3
|
+
export default (function (irc, network) {
|
|
4
|
+
irc.on("invite", (data) => {
|
|
11
5
|
let chan = network.getChannel(data.channel);
|
|
12
6
|
if (typeof chan === "undefined") {
|
|
13
7
|
chan = network.getLobby();
|
|
14
8
|
}
|
|
15
9
|
const invitedYou = data.invited === irc.user.nick;
|
|
16
|
-
const msg = new
|
|
17
|
-
type:
|
|
10
|
+
const msg = new Msg({
|
|
11
|
+
type: MessageType.INVITE,
|
|
18
12
|
time: data.time,
|
|
19
13
|
from: chan.getUser(data.nick),
|
|
20
14
|
target: chan.getUser(data.invited),
|
|
@@ -22,6 +16,6 @@ exports.default = (function (irc, network) {
|
|
|
22
16
|
highlight: invitedYou,
|
|
23
17
|
invitedYou: invitedYou,
|
|
24
18
|
});
|
|
25
|
-
chan.pushMessage(
|
|
19
|
+
chan.pushMessage(this, msg);
|
|
26
20
|
});
|
|
27
21
|
});
|
|
@@ -1,53 +1,56 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const user_1 = __importDefault(require("../../models/user"));
|
|
8
|
-
const msg_2 = require("../../../shared/types/msg");
|
|
9
|
-
const chan_1 = require("../../../shared/types/chan");
|
|
10
|
-
exports.default = (function (irc, network) {
|
|
11
|
-
const client = this;
|
|
12
|
-
irc.on("join", function (data) {
|
|
1
|
+
import Msg from "../../models/msg.js";
|
|
2
|
+
import User from "../../models/user.js";
|
|
3
|
+
import { MessageType } from "../../../shared/types/msg.js";
|
|
4
|
+
import { ChanState } from "../../../shared/types/chan.js";
|
|
5
|
+
export default (function (irc, network) {
|
|
6
|
+
irc.on("join", (data) => {
|
|
13
7
|
let chan = network.getChannel(data.channel);
|
|
14
8
|
if (typeof chan === "undefined") {
|
|
15
|
-
chan =
|
|
9
|
+
chan = this.createChannel({
|
|
16
10
|
name: data.channel,
|
|
17
|
-
state:
|
|
11
|
+
state: ChanState.JOINED,
|
|
18
12
|
});
|
|
19
|
-
|
|
13
|
+
this.emit("join", {
|
|
20
14
|
network: network.uuid,
|
|
21
15
|
chan: chan.getFilteredClone(true),
|
|
22
16
|
shouldOpen: false,
|
|
23
17
|
index: network.addChannel(chan),
|
|
24
18
|
});
|
|
25
|
-
|
|
26
|
-
chan.loadMessages(
|
|
19
|
+
this.save();
|
|
20
|
+
chan.loadMessages(this, network);
|
|
27
21
|
// Request channels' modes
|
|
28
22
|
network.irc.raw("MODE", chan.name);
|
|
29
23
|
}
|
|
30
24
|
else if (data.nick === irc.user.nick) {
|
|
31
|
-
chan.state =
|
|
32
|
-
|
|
25
|
+
chan.state = ChanState.JOINED;
|
|
26
|
+
this.emit("channel:state", {
|
|
33
27
|
chan: chan.id,
|
|
34
28
|
state: chan.state,
|
|
35
29
|
});
|
|
36
30
|
}
|
|
37
|
-
const user = new
|
|
38
|
-
const msg = new
|
|
31
|
+
const user = new User({ nick: data.nick });
|
|
32
|
+
const msg = new Msg({
|
|
39
33
|
time: data.time,
|
|
40
34
|
from: user,
|
|
41
35
|
hostmask: data.ident + "@" + data.hostname,
|
|
42
36
|
gecos: data.gecos,
|
|
43
37
|
account: data.account,
|
|
44
|
-
type:
|
|
38
|
+
type: MessageType.JOIN,
|
|
45
39
|
self: data.nick === irc.user.nick,
|
|
46
40
|
});
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
41
|
+
// User list update callback - executed regardless of buffering
|
|
42
|
+
const updateUserList = () => {
|
|
43
|
+
chan.setUser(new User({ nick: data.nick }));
|
|
44
|
+
this.emit("users", {
|
|
45
|
+
chan: chan.id,
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
// Try to process through mass event aggregator
|
|
49
|
+
const wasBuffered = this.massEventAggregator.processMessage(network, chan, msg, updateUserList);
|
|
50
|
+
if (!wasBuffered) {
|
|
51
|
+
// Not in mass event mode - process normally
|
|
52
|
+
chan.pushMessage(this, msg);
|
|
53
|
+
updateUserList();
|
|
54
|
+
}
|
|
52
55
|
});
|
|
53
56
|
});
|
|
@@ -1,21 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const msg_1 = __importDefault(require("../../models/msg"));
|
|
7
|
-
const msg_2 = require("../../../shared/types/msg");
|
|
8
|
-
const chan_1 = require("../../../shared/types/chan");
|
|
9
|
-
exports.default = (function (irc, network) {
|
|
10
|
-
const client = this;
|
|
11
|
-
irc.on("kick", function (data) {
|
|
1
|
+
import Msg from "../../models/msg.js";
|
|
2
|
+
import { MessageType } from "../../../shared/types/msg.js";
|
|
3
|
+
import { ChanState } from "../../../shared/types/chan.js";
|
|
4
|
+
export default (function (irc, network) {
|
|
5
|
+
irc.on("kick", (data) => {
|
|
12
6
|
const chan = network.getChannel(data.channel);
|
|
13
7
|
if (typeof chan === "undefined") {
|
|
14
8
|
return;
|
|
15
9
|
}
|
|
16
10
|
const user = chan.getUser(data.kicked);
|
|
17
|
-
const msg = new
|
|
18
|
-
type:
|
|
11
|
+
const msg = new Msg({
|
|
12
|
+
type: MessageType.KICK,
|
|
19
13
|
time: data.time,
|
|
20
14
|
from: chan.getUser(data.nick),
|
|
21
15
|
target: user,
|
|
@@ -23,17 +17,27 @@ exports.default = (function (irc, network) {
|
|
|
23
17
|
highlight: data.kicked === irc.user.nick,
|
|
24
18
|
self: data.nick === irc.user.nick,
|
|
25
19
|
});
|
|
26
|
-
|
|
20
|
+
// Self kicks should not be buffered and need special handling
|
|
27
21
|
if (data.kicked === irc.user.nick) {
|
|
22
|
+
chan.pushMessage(this, msg);
|
|
28
23
|
chan.users = new Map();
|
|
29
|
-
chan.state =
|
|
30
|
-
|
|
24
|
+
chan.state = ChanState.PARTED;
|
|
25
|
+
this.emit("channel:state", {
|
|
31
26
|
chan: chan.id,
|
|
32
27
|
state: chan.state,
|
|
33
28
|
});
|
|
29
|
+
return;
|
|
34
30
|
}
|
|
35
|
-
|
|
31
|
+
// User list update callback - executed regardless of buffering
|
|
32
|
+
const updateUserList = () => {
|
|
36
33
|
chan.removeUser(user);
|
|
34
|
+
};
|
|
35
|
+
// Try to process through mass event aggregator
|
|
36
|
+
const wasBuffered = this.massEventAggregator.processMessage(network, chan, msg, updateUserList);
|
|
37
|
+
if (!wasBuffered) {
|
|
38
|
+
// Not in mass event mode - process normally
|
|
39
|
+
chan.pushMessage(this, msg);
|
|
40
|
+
updateUserList();
|
|
37
41
|
}
|
|
38
42
|
});
|
|
39
43
|
});
|
|
@@ -1,47 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
-
};
|
|
28
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
const cheerio = __importStar(require("cheerio"));
|
|
30
|
-
const got_1 = __importDefault(require("got"));
|
|
31
|
-
const url_1 = require("url");
|
|
32
|
-
const mime_types_1 = __importDefault(require("mime-types"));
|
|
33
|
-
const log_1 = __importDefault(require("../../log"));
|
|
34
|
-
const config_1 = __importDefault(require("../../config"));
|
|
35
|
-
const linkify_1 = require("../../../shared/linkify");
|
|
36
|
-
const storage_1 = __importDefault(require("../storage"));
|
|
1
|
+
import * as cheerio from "cheerio";
|
|
2
|
+
import { URL } from "url";
|
|
3
|
+
import mime from "mime-types";
|
|
4
|
+
import log from "../../log.js";
|
|
5
|
+
import Config from "../../config.js";
|
|
6
|
+
import { findLinksWithSchema } from "../../../shared/linkify.js";
|
|
7
|
+
import storage from "../storage.js";
|
|
37
8
|
const currentFetchPromises = new Map();
|
|
38
9
|
const imageTypeRegex = /^image\/.+/;
|
|
39
10
|
const mediaTypeRegex = /^(audio|video)\/.+/;
|
|
40
|
-
function
|
|
41
|
-
if (!
|
|
11
|
+
export default function (client, chan, msg, cleanText) {
|
|
12
|
+
if (!Config.values.prefetch) {
|
|
42
13
|
return;
|
|
43
14
|
}
|
|
44
|
-
msg.previews =
|
|
15
|
+
msg.previews = findLinksWithSchema(cleanText).reduce((cleanLinks, link) => {
|
|
45
16
|
const url = normalizeURL(link.link);
|
|
46
17
|
// If the URL is invalid and cannot be normalized, don't fetch it
|
|
47
18
|
if (!url) {
|
|
@@ -65,7 +36,7 @@ function default_1(client, chan, msg, cleanText) {
|
|
|
65
36
|
shown: null,
|
|
66
37
|
};
|
|
67
38
|
cleanLinks.push(preview);
|
|
68
|
-
const urlObj = new
|
|
39
|
+
const urlObj = new URL(url);
|
|
69
40
|
if ((urlObj.hostname.endsWith("youtube.com") && urlObj.pathname.includes("watch")) ||
|
|
70
41
|
urlObj.hostname.endsWith("youtu.be")) {
|
|
71
42
|
fetchYoutube(url, msg, chan, preview, client);
|
|
@@ -76,7 +47,6 @@ function default_1(client, chan, msg, cleanText) {
|
|
|
76
47
|
return cleanLinks;
|
|
77
48
|
}, []);
|
|
78
49
|
}
|
|
79
|
-
exports.default = default_1;
|
|
80
50
|
function fetchUrl(url, msg, chan, preview, client) {
|
|
81
51
|
fetch(url, {
|
|
82
52
|
accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
|
@@ -139,7 +109,7 @@ function parseHtml(preview, res, client) {
|
|
|
139
109
|
if (preview.body.length) {
|
|
140
110
|
preview.body = preview.body.substr(0, 300);
|
|
141
111
|
}
|
|
142
|
-
if (!
|
|
112
|
+
if (!Config.values.prefetchStorage && Config.values.disableMediaPreview) {
|
|
143
113
|
resolve(res);
|
|
144
114
|
return;
|
|
145
115
|
}
|
|
@@ -157,7 +127,7 @@ function parseHtml(preview, res, client) {
|
|
|
157
127
|
.then((resThumb) => {
|
|
158
128
|
if (resThumb !== null &&
|
|
159
129
|
imageTypeRegex.test(resThumb.type) &&
|
|
160
|
-
resThumb.size <=
|
|
130
|
+
resThumb.size <= Config.values.prefetchMaxImageSize * 1024) {
|
|
161
131
|
preview.thumbActualUrl = thumb;
|
|
162
132
|
}
|
|
163
133
|
resolve(resThumb);
|
|
@@ -170,11 +140,10 @@ function parseHtml(preview, res, client) {
|
|
|
170
140
|
});
|
|
171
141
|
});
|
|
172
142
|
}
|
|
173
|
-
// TODO: type $
|
|
174
143
|
function parseHtmlMedia($, preview, client) {
|
|
175
144
|
return new Promise((resolve, reject) => {
|
|
176
|
-
if (
|
|
177
|
-
reject();
|
|
145
|
+
if (Config.values.disableMediaPreview) {
|
|
146
|
+
reject(new Error("Media preview is disabled"));
|
|
178
147
|
return;
|
|
179
148
|
}
|
|
180
149
|
let foundMedia = false;
|
|
@@ -185,7 +154,7 @@ function parseHtmlMedia($, preview, client) {
|
|
|
185
154
|
if (openGraphType &&
|
|
186
155
|
!openGraphType.startsWith("video") &&
|
|
187
156
|
!openGraphType.startsWith("music")) {
|
|
188
|
-
reject();
|
|
157
|
+
reject(new Error("Open Graph type is not video or music"));
|
|
189
158
|
return;
|
|
190
159
|
}
|
|
191
160
|
["video", "audio"].forEach((type) => {
|
|
@@ -218,7 +187,7 @@ function parseHtmlMedia($, preview, client) {
|
|
|
218
187
|
})
|
|
219
188
|
.then((resMedia) => {
|
|
220
189
|
if (resMedia === null || !mediaTypeRegex.test(resMedia.type)) {
|
|
221
|
-
return reject();
|
|
190
|
+
return reject(new Error("Invalid media type"));
|
|
222
191
|
}
|
|
223
192
|
preview.type = type;
|
|
224
193
|
preview.media = mediaUrl;
|
|
@@ -231,7 +200,7 @@ function parseHtmlMedia($, preview, client) {
|
|
|
231
200
|
});
|
|
232
201
|
});
|
|
233
202
|
if (!foundMedia) {
|
|
234
|
-
reject();
|
|
203
|
+
reject(new Error("No media found"));
|
|
235
204
|
}
|
|
236
205
|
});
|
|
237
206
|
}
|
|
@@ -254,13 +223,13 @@ function parse(msg, chan, preview, res, client) {
|
|
|
254
223
|
case "image/jxl":
|
|
255
224
|
case "image/webp":
|
|
256
225
|
case "image/avif":
|
|
257
|
-
if (!
|
|
226
|
+
if (!Config.values.prefetchStorage && Config.values.disableMediaPreview) {
|
|
258
227
|
return removePreview(msg, preview);
|
|
259
228
|
}
|
|
260
|
-
if (res.size >
|
|
229
|
+
if (res.size > Config.values.prefetchMaxImageSize * 1024) {
|
|
261
230
|
preview.type = "error";
|
|
262
231
|
preview.error = "image-too-big";
|
|
263
|
-
preview.maxSize =
|
|
232
|
+
preview.maxSize = Config.values.prefetchMaxImageSize * 1024;
|
|
264
233
|
}
|
|
265
234
|
else {
|
|
266
235
|
preview.type = "image";
|
|
@@ -284,7 +253,7 @@ function parse(msg, chan, preview, res, client) {
|
|
|
284
253
|
if (!preview.link.startsWith("https://")) {
|
|
285
254
|
break;
|
|
286
255
|
}
|
|
287
|
-
if (
|
|
256
|
+
if (Config.values.disableMediaPreview) {
|
|
288
257
|
return removePreview(msg, preview);
|
|
289
258
|
}
|
|
290
259
|
preview.type = "audio";
|
|
@@ -297,7 +266,7 @@ function parse(msg, chan, preview, res, client) {
|
|
|
297
266
|
if (!preview.link.startsWith("https://")) {
|
|
298
267
|
break;
|
|
299
268
|
}
|
|
300
|
-
if (
|
|
269
|
+
if (Config.values.disableMediaPreview) {
|
|
301
270
|
return removePreview(msg, preview);
|
|
302
271
|
}
|
|
303
272
|
preview.type = "video";
|
|
@@ -315,13 +284,16 @@ function parse(msg, chan, preview, res, client) {
|
|
|
315
284
|
function handlePreview(client, chan, msg, preview, res) {
|
|
316
285
|
const thumb = preview.thumbActualUrl || "";
|
|
317
286
|
delete preview.thumbActualUrl;
|
|
318
|
-
if (!thumb.length || !
|
|
287
|
+
if (!thumb.length || !Config.values.prefetchStorage) {
|
|
319
288
|
preview.thumb = thumb;
|
|
320
289
|
return emitPreview(client, chan, msg, preview);
|
|
321
290
|
}
|
|
291
|
+
if (!res) {
|
|
292
|
+
return emitPreview(client, chan, msg, preview);
|
|
293
|
+
}
|
|
322
294
|
// Get the correct file extension for the provided content-type
|
|
323
295
|
// This is done to prevent user-input being stored in the file name (extension)
|
|
324
|
-
const extension =
|
|
296
|
+
const extension = mime.extension(res.type);
|
|
325
297
|
if (!extension) {
|
|
326
298
|
// For link previews, drop the thumbnail
|
|
327
299
|
// For other types, do not display preview at all
|
|
@@ -330,7 +302,7 @@ function handlePreview(client, chan, msg, preview, res) {
|
|
|
330
302
|
}
|
|
331
303
|
return emitPreview(client, chan, msg, preview);
|
|
332
304
|
}
|
|
333
|
-
|
|
305
|
+
storage.store(res.data, extension, (uri) => {
|
|
334
306
|
preview.thumb = uri;
|
|
335
307
|
emitPreview(client, chan, msg, preview);
|
|
336
308
|
});
|
|
@@ -381,69 +353,76 @@ function fetch(uri, headers) {
|
|
|
381
353
|
if (promise) {
|
|
382
354
|
return promise;
|
|
383
355
|
}
|
|
384
|
-
const prefetchTimeout =
|
|
356
|
+
const prefetchTimeout = Config.values.prefetchTimeout;
|
|
385
357
|
if (!prefetchTimeout) {
|
|
386
|
-
|
|
358
|
+
log.warn("prefetchTimeout is missing from your The Lounge configuration, defaulting to 5000 ms");
|
|
387
359
|
}
|
|
388
360
|
promise = new Promise((resolve, reject) => {
|
|
389
361
|
let buffer = Buffer.from("");
|
|
390
362
|
let contentLength = 0;
|
|
391
363
|
let contentType;
|
|
392
|
-
let limit =
|
|
364
|
+
let limit = Config.values.prefetchMaxImageSize * 1024;
|
|
393
365
|
try {
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
timeout: prefetchTimeout || 5000, // milliseconds
|
|
366
|
+
const requestOptions = {
|
|
367
|
+
method: "GET",
|
|
397
368
|
headers: getRequestHeaders(headers),
|
|
398
|
-
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
.
|
|
402
|
-
|
|
403
|
-
|
|
369
|
+
signal: AbortSignal.timeout(prefetchTimeout || 5000),
|
|
370
|
+
};
|
|
371
|
+
globalThis
|
|
372
|
+
.fetch(uri, requestOptions)
|
|
373
|
+
.then(async (response) => {
|
|
374
|
+
contentLength = parseInt(response.headers.get("content-length") || "0", 10);
|
|
375
|
+
contentType = response.headers.get("content-type") || undefined;
|
|
404
376
|
if (contentType && imageTypeRegex.test(contentType)) {
|
|
405
377
|
// response is an image
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
378
|
+
if (contentLength > limit || !Config.values.prefetchStorage) {
|
|
379
|
+
resolve({
|
|
380
|
+
data: Buffer.alloc(0),
|
|
381
|
+
type: contentType,
|
|
382
|
+
size: contentLength,
|
|
383
|
+
});
|
|
384
|
+
return;
|
|
410
385
|
}
|
|
411
386
|
}
|
|
412
387
|
else if (contentType && mediaTypeRegex.test(contentType)) {
|
|
413
|
-
// We don't need to download the file any further
|
|
414
|
-
|
|
388
|
+
// We don't need to download the file any further
|
|
389
|
+
resolve({ data: Buffer.alloc(0), type: contentType, size: contentLength });
|
|
390
|
+
return;
|
|
415
391
|
}
|
|
416
392
|
else {
|
|
417
|
-
// if not image, limit download to the max search size
|
|
418
|
-
// twitter.com sends opengraph meta tags within ~20kb of data for individual tweets, the default is set to 50.
|
|
419
|
-
// for sites like Youtube the og tags are in the first 300K and hence this is configurable by the admin
|
|
393
|
+
// if not image, limit download to the max search size
|
|
420
394
|
limit =
|
|
421
|
-
"prefetchMaxSearchSize" in
|
|
422
|
-
?
|
|
423
|
-
:
|
|
424
|
-
50 * 1024;
|
|
395
|
+
"prefetchMaxSearchSize" in Config.values
|
|
396
|
+
? Config.values.prefetchMaxSearchSize * 1024
|
|
397
|
+
: 50 * 1024;
|
|
425
398
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
399
|
+
if (!response.body) {
|
|
400
|
+
throw new Error("Response body is null");
|
|
401
|
+
}
|
|
402
|
+
const reader = response.body.getReader();
|
|
403
|
+
while (true) {
|
|
404
|
+
const { done, value } = await reader.read();
|
|
405
|
+
if (done) {
|
|
406
|
+
break;
|
|
407
|
+
}
|
|
408
|
+
const chunkBuffer = Buffer.from(value);
|
|
409
|
+
buffer = Buffer.concat([buffer, chunkBuffer]);
|
|
410
|
+
if (buffer.length >= limit) {
|
|
411
|
+
await reader.cancel();
|
|
412
|
+
break;
|
|
413
|
+
}
|
|
432
414
|
}
|
|
433
|
-
})
|
|
434
|
-
.on("end", () => gotStream.destroy())
|
|
435
|
-
.on("close", () => {
|
|
436
415
|
let type = "";
|
|
437
|
-
// If we downloaded more data then specified in Content-Length, use real data size
|
|
438
416
|
const size = contentLength > buffer.length ? contentLength : buffer.length;
|
|
439
417
|
if (contentType) {
|
|
440
418
|
type = contentType.split(/ *; */).shift() || "";
|
|
441
419
|
}
|
|
442
420
|
resolve({ data: buffer, type, size });
|
|
443
|
-
})
|
|
421
|
+
})
|
|
422
|
+
.catch((e) => reject(e instanceof Error ? e : new Error(String(e))));
|
|
444
423
|
}
|
|
445
424
|
catch (e) {
|
|
446
|
-
return reject(e);
|
|
425
|
+
return reject(e instanceof Error ? e : new Error(String(e)));
|
|
447
426
|
}
|
|
448
427
|
});
|
|
449
428
|
const removeCache = () => currentFetchPromises.delete(cacheKey);
|
|
@@ -453,7 +432,7 @@ function fetch(uri, headers) {
|
|
|
453
432
|
}
|
|
454
433
|
function normalizeURL(link, baseLink, disallowHttp = false) {
|
|
455
434
|
try {
|
|
456
|
-
const url = new
|
|
435
|
+
const url = new URL(link, baseLink);
|
|
457
436
|
// Only fetch http and https links
|
|
458
437
|
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
459
438
|
return undefined;
|
|
@@ -469,7 +448,7 @@ function normalizeURL(link, baseLink, disallowHttp = false) {
|
|
|
469
448
|
url.hash = "";
|
|
470
449
|
return url.toString();
|
|
471
450
|
}
|
|
472
|
-
catch
|
|
451
|
+
catch {
|
|
473
452
|
// if an exception was thrown, the url is not valid
|
|
474
453
|
}
|
|
475
454
|
return undefined;
|
|
@@ -1,35 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const chan_1 = require("../../../shared/types/chan");
|
|
4
|
-
exports.default = (function (irc, network) {
|
|
5
|
-
const client = this;
|
|
1
|
+
import { ChanType, SpecialChanType } from "../../../shared/types/chan.js";
|
|
2
|
+
export default (function (irc, network) {
|
|
6
3
|
const MAX_CHANS = 500;
|
|
7
|
-
irc.on("channel list start", function () {
|
|
8
|
-
network.chanCache = [];
|
|
9
|
-
updateListStatus({
|
|
10
|
-
text: "Loading channel list, this can take a moment...",
|
|
11
|
-
});
|
|
12
|
-
});
|
|
13
|
-
irc.on("channel list", function (channels) {
|
|
14
|
-
Array.prototype.push.apply(network.chanCache, channels);
|
|
15
|
-
updateListStatus({
|
|
16
|
-
text: `Loaded ${network.chanCache.length} channels...`,
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
irc.on("channel list end", function () {
|
|
20
|
-
updateListStatus(network.chanCache.sort((a, b) => b.num_users - a.num_users).slice(0, MAX_CHANS));
|
|
21
|
-
network.chanCache = [];
|
|
22
|
-
});
|
|
23
4
|
function updateListStatus(msg) {
|
|
24
5
|
let chan = network.getChannel("Channel List");
|
|
25
6
|
if (typeof chan === "undefined") {
|
|
26
|
-
chan =
|
|
27
|
-
type:
|
|
28
|
-
special:
|
|
7
|
+
chan = this.createChannel({
|
|
8
|
+
type: ChanType.SPECIAL,
|
|
9
|
+
special: SpecialChanType.CHANNELLIST,
|
|
29
10
|
name: "Channel List",
|
|
30
11
|
data: msg,
|
|
31
12
|
});
|
|
32
|
-
|
|
13
|
+
this.emit("join", {
|
|
33
14
|
network: network.uuid,
|
|
34
15
|
chan: chan.getFilteredClone(true),
|
|
35
16
|
shouldOpen: false,
|
|
@@ -38,10 +19,26 @@ exports.default = (function (irc, network) {
|
|
|
38
19
|
}
|
|
39
20
|
else {
|
|
40
21
|
chan.data = msg;
|
|
41
|
-
|
|
22
|
+
this.emit("msg:special", {
|
|
42
23
|
chan: chan.id,
|
|
43
24
|
data: msg,
|
|
44
25
|
});
|
|
45
26
|
}
|
|
46
27
|
}
|
|
28
|
+
irc.on("channel list start", () => {
|
|
29
|
+
network.chanCache = [];
|
|
30
|
+
updateListStatus.call(this, {
|
|
31
|
+
text: "Loading channel list, this can take a moment...",
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
irc.on("channel list", (channels) => {
|
|
35
|
+
Array.prototype.push.apply(network.chanCache, channels);
|
|
36
|
+
updateListStatus.call(this, {
|
|
37
|
+
text: `Loaded ${network.chanCache.length} channels...`,
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
irc.on("channel list end", () => {
|
|
41
|
+
updateListStatus.call(this, network.chanCache.sort((a, b) => b.num_users - a.num_users).slice(0, MAX_CHANS));
|
|
42
|
+
network.chanCache = [];
|
|
43
|
+
});
|
|
47
44
|
});
|