@lordbex/thelounge 4.4.3-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.
Files changed (141) hide show
  1. package/README.md +31 -7
  2. package/dist/defaults/config.js +31 -2
  3. package/dist/package.json +94 -91
  4. package/dist/server/client.js +188 -194
  5. package/dist/server/clientManager.js +75 -72
  6. package/dist/server/command-line/index.js +44 -43
  7. package/dist/server/command-line/install.js +37 -70
  8. package/dist/server/command-line/outdated.js +12 -17
  9. package/dist/server/command-line/start.js +25 -26
  10. package/dist/server/command-line/storage.js +26 -31
  11. package/dist/server/command-line/uninstall.js +16 -23
  12. package/dist/server/command-line/upgrade.js +20 -26
  13. package/dist/server/command-line/users/add.js +33 -40
  14. package/dist/server/command-line/users/edit.js +18 -24
  15. package/dist/server/command-line/users/index.js +12 -16
  16. package/dist/server/command-line/users/list.js +11 -39
  17. package/dist/server/command-line/users/remove.js +16 -22
  18. package/dist/server/command-line/users/reset.js +34 -35
  19. package/dist/server/command-line/utils.js +231 -87
  20. package/dist/server/config.js +61 -52
  21. package/dist/server/helper.js +29 -28
  22. package/dist/server/identification.js +39 -34
  23. package/dist/server/index.js +1 -3
  24. package/dist/server/log.js +19 -16
  25. package/dist/server/models/chan.js +36 -33
  26. package/dist/server/models/msg.js +15 -19
  27. package/dist/server/models/network.js +102 -104
  28. package/dist/server/models/prefix.js +4 -7
  29. package/dist/server/models/user.js +5 -10
  30. package/dist/server/path-helper.js +8 -0
  31. package/dist/server/plugins/auth/ldap.js +177 -112
  32. package/dist/server/plugins/auth/local.js +10 -15
  33. package/dist/server/plugins/auth.js +6 -35
  34. package/dist/server/plugins/changelog.js +30 -27
  35. package/dist/server/plugins/clientCertificate.js +33 -37
  36. package/dist/server/plugins/dev-server.js +15 -21
  37. package/dist/server/plugins/inputs/action.js +9 -14
  38. package/dist/server/plugins/inputs/away.js +1 -3
  39. package/dist/server/plugins/inputs/ban.js +9 -14
  40. package/dist/server/plugins/inputs/blow.js +9 -14
  41. package/dist/server/plugins/inputs/connect.js +5 -10
  42. package/dist/server/plugins/inputs/ctcp.js +7 -12
  43. package/dist/server/plugins/inputs/disconnect.js +1 -3
  44. package/dist/server/plugins/inputs/ignore.js +23 -29
  45. package/dist/server/plugins/inputs/ignorelist.js +12 -18
  46. package/dist/server/plugins/inputs/index.js +8 -34
  47. package/dist/server/plugins/inputs/invite.js +7 -12
  48. package/dist/server/plugins/inputs/kick.js +7 -12
  49. package/dist/server/plugins/inputs/kill.js +1 -3
  50. package/dist/server/plugins/inputs/list.js +1 -3
  51. package/dist/server/plugins/inputs/mode.js +10 -15
  52. package/dist/server/plugins/inputs/msg.js +13 -18
  53. package/dist/server/plugins/inputs/mute.js +9 -15
  54. package/dist/server/plugins/inputs/nick.js +9 -14
  55. package/dist/server/plugins/inputs/notice.js +5 -7
  56. package/dist/server/plugins/inputs/part.js +11 -16
  57. package/dist/server/plugins/inputs/quit.js +7 -13
  58. package/dist/server/plugins/inputs/rainbow.js +55 -0
  59. package/dist/server/plugins/inputs/raw.js +1 -3
  60. package/dist/server/plugins/inputs/rejoin.js +7 -12
  61. package/dist/server/plugins/inputs/topic.js +7 -12
  62. package/dist/server/plugins/inputs/whois.js +1 -3
  63. package/dist/server/plugins/irc-events/away.js +14 -20
  64. package/dist/server/plugins/irc-events/cap.js +16 -22
  65. package/dist/server/plugins/irc-events/chghost.js +14 -13
  66. package/dist/server/plugins/irc-events/connection.js +61 -63
  67. package/dist/server/plugins/irc-events/ctcp.js +22 -28
  68. package/dist/server/plugins/irc-events/error.js +20 -26
  69. package/dist/server/plugins/irc-events/help.js +7 -13
  70. package/dist/server/plugins/irc-events/info.js +7 -13
  71. package/dist/server/plugins/irc-events/invite.js +7 -13
  72. package/dist/server/plugins/irc-events/join.js +30 -27
  73. package/dist/server/plugins/irc-events/kick.js +21 -17
  74. package/dist/server/plugins/irc-events/link.js +122 -109
  75. package/dist/server/plugins/irc-events/list.js +23 -26
  76. package/dist/server/plugins/irc-events/message.js +46 -52
  77. package/dist/server/plugins/irc-events/mode.js +66 -63
  78. package/dist/server/plugins/irc-events/modelist.js +29 -35
  79. package/dist/server/plugins/irc-events/motd.js +10 -16
  80. package/dist/server/plugins/irc-events/names.js +3 -6
  81. package/dist/server/plugins/irc-events/nick.js +26 -23
  82. package/dist/server/plugins/irc-events/part.js +19 -15
  83. package/dist/server/plugins/irc-events/quit.js +17 -14
  84. package/dist/server/plugins/irc-events/sasl.js +9 -15
  85. package/dist/server/plugins/irc-events/spgroups.js +38 -0
  86. package/dist/server/plugins/irc-events/spjoin.js +52 -0
  87. package/dist/server/plugins/irc-events/topic.js +12 -18
  88. package/dist/server/plugins/irc-events/unhandled.js +12 -12
  89. package/dist/server/plugins/irc-events/welcome.js +7 -13
  90. package/dist/server/plugins/irc-events/whois.js +20 -24
  91. package/dist/server/plugins/massEventAggregator.js +214 -0
  92. package/dist/server/plugins/messageStorage/sqlite.js +322 -141
  93. package/dist/server/plugins/messageStorage/text.js +21 -26
  94. package/dist/server/plugins/packages/index.js +105 -74
  95. package/dist/server/plugins/packages/publicClient.js +7 -16
  96. package/dist/server/plugins/packages/themes.js +11 -16
  97. package/dist/server/plugins/storage.js +28 -33
  98. package/dist/server/plugins/sts.js +12 -17
  99. package/dist/server/plugins/uploader.js +40 -43
  100. package/dist/server/plugins/webpush.js +23 -51
  101. package/dist/server/server.js +318 -271
  102. package/dist/server/storageCleaner.js +29 -37
  103. package/dist/server/utils/fish.js +349 -389
  104. package/dist/shared/irc.js +3 -6
  105. package/dist/shared/linkify.js +7 -14
  106. package/dist/shared/types/chan.js +6 -9
  107. package/dist/shared/types/changelog.js +1 -2
  108. package/dist/shared/types/config.js +1 -2
  109. package/dist/shared/types/mention.js +1 -2
  110. package/dist/shared/types/msg.js +3 -5
  111. package/dist/shared/types/network.js +1 -2
  112. package/dist/shared/types/storage.js +1 -2
  113. package/dist/shared/types/user.js +1 -2
  114. package/index.js +14 -10
  115. package/package.json +94 -91
  116. package/public/css/style.css +9 -6
  117. package/public/css/style.css.map +1 -1
  118. package/public/fonts/font-awesome/fa-brands-400.ttf +0 -0
  119. package/public/fonts/font-awesome/fa-brands-400.woff2 +0 -0
  120. package/public/fonts/font-awesome/fa-duotone-900.ttf +0 -0
  121. package/public/fonts/font-awesome/fa-duotone-900.woff2 +0 -0
  122. package/public/fonts/font-awesome/fa-light-300.ttf +0 -0
  123. package/public/fonts/font-awesome/fa-light-300.woff2 +0 -0
  124. package/public/fonts/font-awesome/fa-regular-400.ttf +0 -0
  125. package/public/fonts/font-awesome/fa-regular-400.woff2 +0 -0
  126. package/public/fonts/font-awesome/fa-solid-900.ttf +0 -0
  127. package/public/fonts/font-awesome/fa-solid-900.woff2 +0 -0
  128. package/public/fonts/font-awesome/fa-thin-100.ttf +0 -0
  129. package/public/fonts/font-awesome/fa-thin-100.woff2 +0 -0
  130. package/public/fonts/font-awesome/fa-v4compatibility.ttf +0 -0
  131. package/public/fonts/font-awesome/fa-v4compatibility.woff2 +0 -0
  132. package/public/js/bundle.js +1 -1
  133. package/public/js/bundle.js.map +1 -1
  134. package/public/js/bundle.vendor.js +1 -1
  135. package/public/js/bundle.vendor.js.LICENSE.txt +24 -6
  136. package/public/js/bundle.vendor.js.map +1 -1
  137. package/public/js/loading-error-handlers.js +1 -1
  138. package/public/service-worker.js +1 -1
  139. package/public/themes/default.css +1 -1
  140. package/public/themes/morning.css +1 -1
  141. package/dist/webpack.config.js +0 -224
@@ -1,20 +1,14 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
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 msg_1.default({
17
- type: msg_2.MessageType.INVITE,
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(client, msg);
19
+ chan.pushMessage(this, msg);
26
20
  });
27
21
  });
@@ -1,53 +1,56 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const msg_1 = __importDefault(require("../../models/msg"));
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 = client.createChannel({
9
+ chan = this.createChannel({
16
10
  name: data.channel,
17
- state: chan_1.ChanState.JOINED,
11
+ state: ChanState.JOINED,
18
12
  });
19
- client.emit("join", {
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
- client.save();
26
- chan.loadMessages(client, network);
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 = chan_1.ChanState.JOINED;
32
- client.emit("channel:state", {
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 user_1.default({ nick: data.nick });
38
- const msg = new msg_1.default({
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: msg_2.MessageType.JOIN,
38
+ type: MessageType.JOIN,
45
39
  self: data.nick === irc.user.nick,
46
40
  });
47
- chan.pushMessage(client, msg);
48
- chan.setUser(new user_1.default({ nick: data.nick }));
49
- client.emit("users", {
50
- chan: chan.id,
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
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
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
- 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 msg_1.default({
18
- type: msg_2.MessageType.KICK,
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
- chan.pushMessage(client, msg);
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 = chan_1.ChanState.PARTED;
30
- client.emit("channel:state", {
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
- else {
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
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
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 default_1(client, chan, msg, cleanText) {
41
- if (!config_1.default.values.prefetch) {
11
+ export default function (client, chan, msg, cleanText) {
12
+ if (!Config.values.prefetch) {
42
13
  return;
43
14
  }
44
- msg.previews = (0, linkify_1.findLinksWithSchema)(cleanText).reduce((cleanLinks, link) => {
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,23 +36,56 @@ function default_1(client, chan, msg, cleanText) {
65
36
  shown: null,
66
37
  };
67
38
  cleanLinks.push(preview);
68
- fetch(url, {
69
- accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
70
- language: client.config.browser?.language || "",
71
- })
72
- .then((res) => {
73
- parse(msg, chan, preview, res, client);
74
- })
75
- .catch((err) => {
76
- preview.type = "error";
77
- preview.error = "message";
78
- preview.message = err.message;
79
- emitPreview(client, chan, msg, preview);
80
- });
39
+ const urlObj = new URL(url);
40
+ if ((urlObj.hostname.endsWith("youtube.com") && urlObj.pathname.includes("watch")) ||
41
+ urlObj.hostname.endsWith("youtu.be")) {
42
+ fetchYoutube(url, msg, chan, preview, client);
43
+ }
44
+ else {
45
+ fetchUrl(url, msg, chan, preview, client);
46
+ }
81
47
  return cleanLinks;
82
48
  }, []);
83
49
  }
84
- exports.default = default_1;
50
+ function fetchUrl(url, msg, chan, preview, client) {
51
+ fetch(url, {
52
+ accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
53
+ language: client.config.browser?.language || "",
54
+ })
55
+ .then((res) => {
56
+ parse(msg, chan, preview, res, client);
57
+ })
58
+ .catch((err) => {
59
+ preview.type = "error";
60
+ preview.error = "message";
61
+ preview.message = err.message;
62
+ emitPreview(client, chan, msg, preview);
63
+ });
64
+ }
65
+ function fetchYoutube(url, msg, chan, preview, client) {
66
+ const api_url = `https://www.youtube.com/oembed?url=${encodeURIComponent(url)}&format=json`;
67
+ fetch(api_url, {
68
+ accept: "application/json",
69
+ language: client.config.browser?.language || "",
70
+ })
71
+ .then((res) => {
72
+ const data = JSON.parse(res.data.toString());
73
+ let author = data.author_name || "";
74
+ author = author ? ` ~ ${author}` : "";
75
+ preview.type = "link";
76
+ preview.link = url;
77
+ preview.thumbActualUrl = data.thumbnail_url || "";
78
+ preview.head = data.title || "";
79
+ preview.body = author;
80
+ handlePreview(client, chan, msg, preview, res);
81
+ })
82
+ .catch((err) => {
83
+ preview.type = "error";
84
+ preview.error = "message";
85
+ preview.message = err.message;
86
+ emitPreview(client, chan, msg, preview);
87
+ });
88
+ }
85
89
  function parseHtml(preview, res, client) {
86
90
  // TODO:
87
91
  // eslint-disable-next-line @typescript-eslint/no-misused-promises
@@ -105,7 +109,7 @@ function parseHtml(preview, res, client) {
105
109
  if (preview.body.length) {
106
110
  preview.body = preview.body.substr(0, 300);
107
111
  }
108
- if (!config_1.default.values.prefetchStorage && config_1.default.values.disableMediaPreview) {
112
+ if (!Config.values.prefetchStorage && Config.values.disableMediaPreview) {
109
113
  resolve(res);
110
114
  return;
111
115
  }
@@ -123,7 +127,7 @@ function parseHtml(preview, res, client) {
123
127
  .then((resThumb) => {
124
128
  if (resThumb !== null &&
125
129
  imageTypeRegex.test(resThumb.type) &&
126
- resThumb.size <= config_1.default.values.prefetchMaxImageSize * 1024) {
130
+ resThumb.size <= Config.values.prefetchMaxImageSize * 1024) {
127
131
  preview.thumbActualUrl = thumb;
128
132
  }
129
133
  resolve(resThumb);
@@ -136,11 +140,10 @@ function parseHtml(preview, res, client) {
136
140
  });
137
141
  });
138
142
  }
139
- // TODO: type $
140
143
  function parseHtmlMedia($, preview, client) {
141
144
  return new Promise((resolve, reject) => {
142
- if (config_1.default.values.disableMediaPreview) {
143
- reject();
145
+ if (Config.values.disableMediaPreview) {
146
+ reject(new Error("Media preview is disabled"));
144
147
  return;
145
148
  }
146
149
  let foundMedia = false;
@@ -151,7 +154,7 @@ function parseHtmlMedia($, preview, client) {
151
154
  if (openGraphType &&
152
155
  !openGraphType.startsWith("video") &&
153
156
  !openGraphType.startsWith("music")) {
154
- reject();
157
+ reject(new Error("Open Graph type is not video or music"));
155
158
  return;
156
159
  }
157
160
  ["video", "audio"].forEach((type) => {
@@ -184,7 +187,7 @@ function parseHtmlMedia($, preview, client) {
184
187
  })
185
188
  .then((resMedia) => {
186
189
  if (resMedia === null || !mediaTypeRegex.test(resMedia.type)) {
187
- return reject();
190
+ return reject(new Error("Invalid media type"));
188
191
  }
189
192
  preview.type = type;
190
193
  preview.media = mediaUrl;
@@ -197,7 +200,7 @@ function parseHtmlMedia($, preview, client) {
197
200
  });
198
201
  });
199
202
  if (!foundMedia) {
200
- reject();
203
+ reject(new Error("No media found"));
201
204
  }
202
205
  });
203
206
  }
@@ -220,13 +223,13 @@ function parse(msg, chan, preview, res, client) {
220
223
  case "image/jxl":
221
224
  case "image/webp":
222
225
  case "image/avif":
223
- if (!config_1.default.values.prefetchStorage && config_1.default.values.disableMediaPreview) {
226
+ if (!Config.values.prefetchStorage && Config.values.disableMediaPreview) {
224
227
  return removePreview(msg, preview);
225
228
  }
226
- if (res.size > config_1.default.values.prefetchMaxImageSize * 1024) {
229
+ if (res.size > Config.values.prefetchMaxImageSize * 1024) {
227
230
  preview.type = "error";
228
231
  preview.error = "image-too-big";
229
- preview.maxSize = config_1.default.values.prefetchMaxImageSize * 1024;
232
+ preview.maxSize = Config.values.prefetchMaxImageSize * 1024;
230
233
  }
231
234
  else {
232
235
  preview.type = "image";
@@ -250,7 +253,7 @@ function parse(msg, chan, preview, res, client) {
250
253
  if (!preview.link.startsWith("https://")) {
251
254
  break;
252
255
  }
253
- if (config_1.default.values.disableMediaPreview) {
256
+ if (Config.values.disableMediaPreview) {
254
257
  return removePreview(msg, preview);
255
258
  }
256
259
  preview.type = "audio";
@@ -263,7 +266,7 @@ function parse(msg, chan, preview, res, client) {
263
266
  if (!preview.link.startsWith("https://")) {
264
267
  break;
265
268
  }
266
- if (config_1.default.values.disableMediaPreview) {
269
+ if (Config.values.disableMediaPreview) {
267
270
  return removePreview(msg, preview);
268
271
  }
269
272
  preview.type = "video";
@@ -281,13 +284,16 @@ function parse(msg, chan, preview, res, client) {
281
284
  function handlePreview(client, chan, msg, preview, res) {
282
285
  const thumb = preview.thumbActualUrl || "";
283
286
  delete preview.thumbActualUrl;
284
- if (!thumb.length || !config_1.default.values.prefetchStorage) {
287
+ if (!thumb.length || !Config.values.prefetchStorage) {
285
288
  preview.thumb = thumb;
286
289
  return emitPreview(client, chan, msg, preview);
287
290
  }
291
+ if (!res) {
292
+ return emitPreview(client, chan, msg, preview);
293
+ }
288
294
  // Get the correct file extension for the provided content-type
289
295
  // This is done to prevent user-input being stored in the file name (extension)
290
- const extension = mime_types_1.default.extension(res.type);
296
+ const extension = mime.extension(res.type);
291
297
  if (!extension) {
292
298
  // For link previews, drop the thumbnail
293
299
  // For other types, do not display preview at all
@@ -296,7 +302,7 @@ function handlePreview(client, chan, msg, preview, res) {
296
302
  }
297
303
  return emitPreview(client, chan, msg, preview);
298
304
  }
299
- storage_1.default.store(res.data, extension, (uri) => {
305
+ storage.store(res.data, extension, (uri) => {
300
306
  preview.thumb = uri;
301
307
  emitPreview(client, chan, msg, preview);
302
308
  });
@@ -330,7 +336,7 @@ function getRequestHeaders(headers) {
330
336
  const formattedHeaders = {
331
337
  // Certain websites like Amazon only add <meta> tags to known bots,
332
338
  // lets pretend to be them to get the metadata
333
- "User-Agent": "Mozilla/5.0 (compatible; The Lounge IRC Client; +https://github.com/thelounge/thelounge)" +
339
+ "User-Agent": "Mozilla/5.0 (compatible; The Lounge IRC Client; +https://github.com/lordbex/thelounge)" +
334
340
  " facebookexternalhit/1.1 Twitterbot/1.0",
335
341
  Accept: headers.accept || "*/*",
336
342
  "X-Purpose": "preview",
@@ -347,69 +353,76 @@ function fetch(uri, headers) {
347
353
  if (promise) {
348
354
  return promise;
349
355
  }
350
- const prefetchTimeout = config_1.default.values.prefetchTimeout;
356
+ const prefetchTimeout = Config.values.prefetchTimeout;
351
357
  if (!prefetchTimeout) {
352
- log_1.default.warn("prefetchTimeout is missing from your The Lounge configuration, defaulting to 5000 ms");
358
+ log.warn("prefetchTimeout is missing from your The Lounge configuration, defaulting to 5000 ms");
353
359
  }
354
360
  promise = new Promise((resolve, reject) => {
355
361
  let buffer = Buffer.from("");
356
362
  let contentLength = 0;
357
363
  let contentType;
358
- let limit = config_1.default.values.prefetchMaxImageSize * 1024;
364
+ let limit = Config.values.prefetchMaxImageSize * 1024;
359
365
  try {
360
- const gotStream = got_1.default.stream(uri, {
361
- retry: 0,
362
- timeout: prefetchTimeout || 5000, // milliseconds
366
+ const requestOptions = {
367
+ method: "GET",
363
368
  headers: getRequestHeaders(headers),
364
- localAddress: config_1.default.values.bind,
365
- });
366
- gotStream
367
- .on("response", function (res) {
368
- contentLength = parseInt(res.headers["content-length"], 10) || 0;
369
- contentType = res.headers["content-type"];
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;
370
376
  if (contentType && imageTypeRegex.test(contentType)) {
371
377
  // response is an image
372
- // if Content-Length header reports a size exceeding the prefetch limit, abort fetch
373
- // and if file is not to be stored we don't need to download further either
374
- if (contentLength > limit || !config_1.default.values.prefetchStorage) {
375
- gotStream.destroy();
378
+ if (contentLength > limit || !Config.values.prefetchStorage) {
379
+ resolve({
380
+ data: Buffer.alloc(0),
381
+ type: contentType,
382
+ size: contentLength,
383
+ });
384
+ return;
376
385
  }
377
386
  }
378
387
  else if (contentType && mediaTypeRegex.test(contentType)) {
379
- // We don't need to download the file any further after we received content-type header
380
- gotStream.destroy();
388
+ // We don't need to download the file any further
389
+ resolve({ data: Buffer.alloc(0), type: contentType, size: contentLength });
390
+ return;
381
391
  }
382
392
  else {
383
- // if not image, limit download to the max search size, since we need only meta tags
384
- // twitter.com sends opengraph meta tags within ~20kb of data for individual tweets, the default is set to 50.
385
- // 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
386
394
  limit =
387
- "prefetchMaxSearchSize" in config_1.default.values
388
- ? config_1.default.values.prefetchMaxSearchSize * 1024
389
- : // set to the previous size if config option is unset
390
- 50 * 1024;
395
+ "prefetchMaxSearchSize" in Config.values
396
+ ? Config.values.prefetchMaxSearchSize * 1024
397
+ : 50 * 1024;
391
398
  }
392
- })
393
- .on("error", (e) => reject(e))
394
- .on("data", (data) => {
395
- buffer = Buffer.concat([buffer, data], buffer.length + data.length);
396
- if (buffer.length >= limit) {
397
- gotStream.destroy();
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
+ }
398
414
  }
399
- })
400
- .on("end", () => gotStream.destroy())
401
- .on("close", () => {
402
415
  let type = "";
403
- // If we downloaded more data then specified in Content-Length, use real data size
404
416
  const size = contentLength > buffer.length ? contentLength : buffer.length;
405
417
  if (contentType) {
406
418
  type = contentType.split(/ *; */).shift() || "";
407
419
  }
408
420
  resolve({ data: buffer, type, size });
409
- });
421
+ })
422
+ .catch((e) => reject(e instanceof Error ? e : new Error(String(e))));
410
423
  }
411
424
  catch (e) {
412
- return reject(e);
425
+ return reject(e instanceof Error ? e : new Error(String(e)));
413
426
  }
414
427
  });
415
428
  const removeCache = () => currentFetchPromises.delete(cacheKey);
@@ -419,7 +432,7 @@ function fetch(uri, headers) {
419
432
  }
420
433
  function normalizeURL(link, baseLink, disallowHttp = false) {
421
434
  try {
422
- const url = new url_1.URL(link, baseLink);
435
+ const url = new URL(link, baseLink);
423
436
  // Only fetch http and https links
424
437
  if (url.protocol !== "http:" && url.protocol !== "https:") {
425
438
  return undefined;
@@ -435,7 +448,7 @@ function normalizeURL(link, baseLink, disallowHttp = false) {
435
448
  url.hash = "";
436
449
  return url.toString();
437
450
  }
438
- catch (e) {
451
+ catch {
439
452
  // if an exception was thrown, the url is not valid
440
453
  }
441
454
  return undefined;
@@ -1,35 +1,16 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
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 = client.createChannel({
27
- type: chan_1.ChanType.SPECIAL,
28
- special: chan_1.SpecialChanType.CHANNELLIST,
7
+ chan = this.createChannel({
8
+ type: ChanType.SPECIAL,
9
+ special: SpecialChanType.CHANNELLIST,
29
10
  name: "Channel List",
30
11
  data: msg,
31
12
  });
32
- client.emit("join", {
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
- client.emit("msg:special", {
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
  });