@lordbex/thelounge 4.4.4-blowfish → 4.5.1-blowfish

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 (140) hide show
  1. package/README.md +2 -2
  2. package/dist/defaults/config.js +31 -2
  3. package/dist/package.json +93 -91
  4. package/dist/server/client.js +188 -194
  5. package/dist/server/clientManager.js +65 -63
  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 +88 -86
  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 +75 -96
  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 +7 -14
  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 +93 -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/service-worker.js +1 -1
  138. package/public/themes/default.css +1 -1
  139. package/public/themes/morning.css +1 -1
  140. package/dist/webpack.config.js +0 -224
@@ -1,50 +1,23 @@
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 lodash_1 = __importDefault(require("lodash"));
30
- const ua_parser_js_1 = __importDefault(require("ua-parser-js"));
31
- const uuid_1 = require("uuid");
32
- const escapeRegExp_1 = __importDefault(require("lodash/escapeRegExp"));
33
- const crypto_1 = __importDefault(require("crypto"));
34
- const chalk_1 = __importDefault(require("chalk"));
35
- const log_1 = __importDefault(require("./log"));
36
- const chan_1 = __importDefault(require("./models/chan"));
37
- const msg_1 = __importDefault(require("./models/msg"));
38
- const config_1 = __importDefault(require("./config"));
39
- const irc_1 = require("../shared/irc");
40
- const msg_2 = require("../shared/types/msg");
41
- const inputs_1 = __importDefault(require("./plugins/inputs"));
42
- const publicClient_1 = __importDefault(require("./plugins/packages/publicClient"));
43
- const sqlite_1 = __importDefault(require("./plugins/messageStorage/sqlite"));
44
- const text_1 = __importDefault(require("./plugins/messageStorage/text"));
45
- const network_1 = __importDefault(require("./models/network"));
46
- const storageCleaner_1 = require("./storageCleaner");
47
- const chan_2 = require("../shared/types/chan");
1
+ import _ from "lodash";
2
+ import { UAParser } from "ua-parser-js";
3
+ import { v4 as uuidv4 } from "uuid";
4
+ import escapeRegExp from "lodash/escapeRegExp.js";
5
+ import crypto from "crypto";
6
+ import colors from "chalk";
7
+ import log from "./log.js";
8
+ import Chan from "./models/chan.js";
9
+ import Msg from "./models/msg.js";
10
+ import Config from "./config.js";
11
+ import { condensedTypes } from "../shared/irc.js";
12
+ import { MessageType } from "../shared/types/msg.js";
13
+ import inputs from "./plugins/inputs/index.js";
14
+ import PublicClient from "./plugins/packages/publicClient.js";
15
+ import SqliteMessageStorage from "./plugins/messageStorage/sqlite.js";
16
+ import TextFileMessageStorage from "./plugins/messageStorage/text.js";
17
+ import Network from "./models/network.js";
18
+ import { StorageCleaner } from "./storageCleaner.js";
19
+ import { ChanType } from "../shared/types/chan.js";
20
+ import MassEventAggregator from "./plugins/massEventAggregator.js";
48
21
  const events = [
49
22
  "away",
50
23
  "cap",
@@ -68,6 +41,8 @@ const events = [
68
41
  "part",
69
42
  "quit",
70
43
  "sasl",
44
+ "spgroups",
45
+ "spjoin",
71
46
  "topic",
72
47
  "welcome",
73
48
  "whois",
@@ -88,10 +63,11 @@ class Client {
88
63
  highlightRegex;
89
64
  highlightExceptionRegex;
90
65
  messageProvider;
66
+ massEventAggregator;
91
67
  fileHash;
92
68
  constructor(manager, name, config = {}) {
93
- this.id = (0, uuid_1.v4)();
94
- lodash_1.default.merge(this, {
69
+ this.id = uuidv4();
70
+ _.merge(this, {
95
71
  awayMessage: "",
96
72
  lastActiveChannel: -1,
97
73
  attachedClients: {},
@@ -107,74 +83,74 @@ class Client {
107
83
  highlightExceptionRegex: null,
108
84
  messageProvider: undefined,
109
85
  });
110
- const client = this;
111
- client.config.log = Boolean(client.config.log);
112
- client.config.password = String(client.config.password);
113
- if (!config_1.default.values.public && client.config.log) {
114
- if (config_1.default.values.messageStorage.includes("sqlite")) {
115
- client.messageProvider = new sqlite_1.default(client.name);
116
- if (config_1.default.values.storagePolicy.enabled) {
117
- log_1.default.info(`Activating storage cleaner. Policy: ${config_1.default.values.storagePolicy.deletionPolicy}. MaxAge: ${config_1.default.values.storagePolicy.maxAgeDays} days`);
118
- const cleaner = new storageCleaner_1.StorageCleaner(client.messageProvider);
86
+ this.config.log = Boolean(this.config.log);
87
+ this.config.password = String(this.config.password);
88
+ // Initialize mass event aggregator for handling netsplits/mass reconnects
89
+ this.massEventAggregator = new MassEventAggregator(this);
90
+ if (!Config.values.public && this.config.log) {
91
+ if (Config.values.messageStorage.includes("sqlite")) {
92
+ this.messageProvider = new SqliteMessageStorage(this.name);
93
+ if (Config.values.storagePolicy.enabled) {
94
+ log.info(`Activating storage cleaner. Policy: ${Config.values.storagePolicy.deletionPolicy}. MaxAge: ${Config.values.storagePolicy.maxAgeDays} days`);
95
+ const cleaner = new StorageCleaner(this.messageProvider);
119
96
  cleaner.start();
120
97
  }
121
- client.messageStorage.push(client.messageProvider);
98
+ this.messageStorage.push(this.messageProvider);
122
99
  }
123
- if (config_1.default.values.messageStorage.includes("text")) {
124
- client.messageStorage.push(new text_1.default(client.name));
100
+ if (Config.values.messageStorage.includes("text")) {
101
+ this.messageStorage.push(new TextFileMessageStorage(this.name));
125
102
  }
126
- for (const messageStorage of client.messageStorage) {
127
- messageStorage.enable().catch((e) => log_1.default.error(e));
103
+ for (const messageStorage of this.messageStorage) {
104
+ messageStorage.enable().catch((e) => log.error(e));
128
105
  }
129
106
  }
130
- if (!lodash_1.default.isPlainObject(client.config.sessions)) {
131
- client.config.sessions = {};
107
+ if (!_.isPlainObject(this.config.sessions)) {
108
+ this.config.sessions = {};
132
109
  }
133
- if (!lodash_1.default.isPlainObject(client.config.clientSettings)) {
134
- client.config.clientSettings = {};
110
+ if (!_.isPlainObject(this.config.clientSettings)) {
111
+ this.config.clientSettings = {};
135
112
  }
136
- if (!lodash_1.default.isPlainObject(client.config.browser)) {
137
- client.config.browser = {};
113
+ if (!_.isPlainObject(this.config.browser)) {
114
+ this.config.browser = {};
138
115
  }
139
- if (client.config.clientSettings.awayMessage) {
140
- client.awayMessage = client.config.clientSettings.awayMessage;
116
+ if (this.config.clientSettings.awayMessage) {
117
+ this.awayMessage = this.config.clientSettings.awayMessage;
141
118
  }
142
- client.config.clientSettings.searchEnabled = client.messageProvider !== undefined;
143
- client.compileCustomHighlights();
144
- lodash_1.default.forOwn(client.config.sessions, (session) => {
119
+ this.config.clientSettings.searchEnabled = this.messageProvider !== undefined;
120
+ this.compileCustomHighlights();
121
+ _.forOwn(this.config.sessions, (session) => {
145
122
  if (session.pushSubscription) {
146
123
  this.registerPushSubscription(session, session.pushSubscription, true);
147
124
  }
148
125
  });
149
126
  }
150
127
  connect() {
151
- const client = this;
152
- if (client.networks.length !== 0) {
153
- throw new Error(`${client.name} is already connected`);
128
+ if (this.networks.length !== 0) {
129
+ throw new Error(`${this.name} is already connected`);
154
130
  }
155
- (client.config.networks || []).forEach((network) => client.connectToNetwork(network, true));
131
+ (this.config.networks || []).forEach((network) => this.connectToNetwork(network, true));
156
132
  // Networks are stored directly in the client object
157
133
  // We don't need to keep it in the config object
158
- delete client.config.networks;
159
- if (client.name) {
160
- log_1.default.info(`User ${chalk_1.default.bold(client.name)} loaded`);
134
+ delete this.config.networks;
135
+ if (this.name) {
136
+ log.info(`User ${colors.bold(this.name)} loaded`);
161
137
  // Networks are created instantly, but to reduce server load on startup
162
138
  // We randomize the IRC connections and channel log loading
163
- let delay = client.manager.clients.length * 500;
164
- client.networks.forEach((network) => {
139
+ let delay = this.manager.clients.length * 500;
140
+ this.networks.forEach((network) => {
165
141
  setTimeout(() => {
166
- network.channels.forEach((channel) => channel.loadMessages(client, network));
142
+ network.channels.forEach((channel) => channel.loadMessages(this, network));
167
143
  if (!network.userDisconnected && network.irc) {
168
144
  network.irc.connect();
169
145
  }
170
146
  }, delay);
171
147
  delay += 1000 + Math.floor(Math.random() * 1000);
172
148
  });
173
- client.fileHash = client.manager.getDataToSave(client).newHash;
149
+ this.fileHash = this.manager.getDataToSave(this).newHash;
174
150
  }
175
151
  }
176
152
  createChannel(attr) {
177
- const chan = new chan_1.default(attr);
153
+ const chan = new Chan(attr);
178
154
  chan.id = this.idChan++;
179
155
  return chan;
180
156
  }
@@ -187,7 +163,7 @@ class Client {
187
163
  let network = null;
188
164
  let chan = null;
189
165
  for (const n of this.networks) {
190
- chan = lodash_1.default.find(n.channels, { id: channelId });
166
+ chan = _.find(n.channels, { id: channelId });
191
167
  if (chan) {
192
168
  network = n;
193
169
  break;
@@ -199,100 +175,100 @@ class Client {
199
175
  return false;
200
176
  }
201
177
  networkFromConfig(args) {
202
- const client = this;
203
178
  let channels = [];
204
179
  if (Array.isArray(args.channels)) {
205
180
  let badChanConf = false;
206
181
  args.channels.forEach((chan) => {
207
- const type = chan_2.ChanType[(chan.type || "channel").toUpperCase()];
182
+ const type = ChanType[(chan.type || "channel").toUpperCase()];
208
183
  if (!chan.name || !type) {
209
184
  badChanConf = true;
210
185
  return;
211
186
  }
212
- channels.push(client.createChannel({
187
+ channels.push(this.createChannel({
213
188
  name: chan.name,
214
189
  key: chan.key || "",
215
190
  type: type,
216
191
  muted: chan.muted,
192
+ pinned: chan.pinned,
217
193
  }));
218
194
  });
219
- if (badChanConf && client.name) {
220
- log_1.default.warn("User '" +
221
- client.name +
195
+ if (badChanConf && this.name) {
196
+ log.warn("User '" +
197
+ this.name +
222
198
  "' on network '" +
223
- String(args.name) +
199
+ (args.name || "") +
224
200
  "' has an invalid channel which has been ignored");
225
201
  }
226
202
  // `join` is kept for backwards compatibility when updating from versions <2.0
227
203
  // also used by the "connect" window
228
204
  }
229
205
  else if (args.join) {
230
- channels = args.join
206
+ channels = (args.join || "")
231
207
  .replace(/,/g, " ")
232
208
  .split(/\s+/g)
233
209
  .map((chan) => {
234
210
  if (!chan.match(/^[#&!+]/)) {
235
211
  chan = `#${chan}`;
236
212
  }
237
- return client.createChannel({
213
+ return this.createChannel({
238
214
  name: chan,
239
215
  });
240
216
  });
241
217
  }
242
- // TODO; better typing for args
243
- return new network_1.default({
218
+ // Handle commands - can be string[] or string
219
+ const commands = Array.isArray(args.commands) ? args.commands : [];
220
+ return new Network({
244
221
  uuid: args.uuid,
245
- name: String(args.name || (config_1.default.values.lockNetwork ? config_1.default.values.defaults.name : "") || ""),
246
- host: String(args.host || ""),
247
- port: parseInt(String(args.port), 10),
222
+ name: args.name || (Config.values.lockNetwork ? Config.values.defaults.name : "") || "",
223
+ host: args.host || "",
224
+ port: parseInt(String(args.port ?? 6667), 10),
248
225
  tls: !!args.tls,
249
226
  userDisconnected: !!args.userDisconnected,
250
227
  rejectUnauthorized: !!args.rejectUnauthorized,
251
- password: String(args.password || ""),
252
- nick: String(args.nick || ""),
253
- username: String(args.username || ""),
254
- realname: String(args.realname || ""),
255
- leaveMessage: String(args.leaveMessage || ""),
256
- sasl: String(args.sasl || ""),
257
- saslAccount: String(args.saslAccount || ""),
258
- saslPassword: String(args.saslPassword || ""),
259
- commands: args.commands || [],
228
+ password: args.password || "",
229
+ nick: args.nick || "",
230
+ username: args.username || "",
231
+ realname: args.realname || "",
232
+ leaveMessage: args.leaveMessage || "",
233
+ sasl: args.sasl || "",
234
+ saslAccount: args.saslAccount || "",
235
+ saslPassword: args.saslPassword || "",
236
+ commands: commands,
260
237
  channels: channels,
261
- ignoreList: args.ignoreList ? args.ignoreList : [],
238
+ ignoreList: args.ignoreList || [],
262
239
  proxyEnabled: !!args.proxyEnabled,
263
- proxyHost: String(args.proxyHost || ""),
264
- proxyPort: parseInt(args.proxyPort, 10),
265
- proxyUsername: String(args.proxyUsername || ""),
266
- proxyPassword: String(args.proxyPassword || ""),
240
+ proxyHost: args.proxyHost || "",
241
+ proxyPort: parseInt(String(args.proxyPort ?? 1080), 10),
242
+ proxyUsername: args.proxyUsername || "",
243
+ proxyPassword: args.proxyPassword || "",
267
244
  fishGlobalKey: String(args.fishGlobalKey || ""),
268
245
  fishKeys: args.fishKeys || {},
269
246
  });
270
247
  }
271
248
  connectToNetwork(args, isStartup = false) {
272
- const client = this;
273
249
  // Get channel id for lobby before creating other channels for nicer ids
274
- const lobbyChannelId = client.idChan++;
250
+ const lobbyChannelId = this.idChan++;
275
251
  const network = this.networkFromConfig(args);
276
252
  // Set network lobby channel id
277
253
  network.getLobby().id = lobbyChannelId;
278
- client.networks.push(network);
279
- client.emit("network", {
254
+ this.networks.push(network);
255
+ this.emit("network", {
280
256
  network: network.getFilteredClone(this.lastActiveChannel, -1),
281
257
  });
282
- if (!network.validate(client)) {
258
+ if (!network.validate(this)) {
283
259
  return;
284
260
  }
285
- network.createIrcFramework(client);
261
+ network.createIrcFramework(this);
286
262
  // TODO
287
263
  // eslint-disable-next-line @typescript-eslint/no-misused-promises
288
264
  events.forEach(async (plugin) => {
289
- (await Promise.resolve(`${`./plugins/irc-events/${plugin}`}`).then(s => __importStar(require(s)))).default.apply(client, [
265
+ (await import(`./plugins/irc-events/${plugin}.js`)).default.apply(this, [
290
266
  network.irc,
291
267
  network,
292
268
  ]);
293
269
  });
294
270
  if (network.userDisconnected) {
295
- network.getLobby().pushMessage(client, new msg_1.default({
271
+ network.getLobby().pushMessage(this, new Msg({
296
272
  text: "You have manually disconnected from this network before, use the /connect command to connect again.",
297
273
  }), true);
298
274
  }
@@ -302,12 +278,12 @@ class Client {
302
278
  network.irc.connect();
303
279
  }
304
280
  if (!isStartup) {
305
- client.save();
306
- network.channels.forEach((channel) => channel.loadMessages(client, network));
281
+ this.save();
282
+ network.channels.forEach((channel) => channel.loadMessages(this, network));
307
283
  }
308
284
  }
309
285
  generateToken(callback) {
310
- crypto_1.default.randomBytes(64, (err, buf) => {
286
+ crypto.randomBytes(64, (err, buf) => {
311
287
  if (err) {
312
288
  throw err;
313
289
  }
@@ -315,11 +291,12 @@ class Client {
315
291
  });
316
292
  }
317
293
  calculateTokenHash(token) {
318
- return crypto_1.default.createHash("sha512").update(token).digest("hex");
294
+ return crypto.createHash("sha512").update(token).digest("hex");
319
295
  }
320
296
  updateSession(token, ip, request) {
321
- const client = this;
322
- const agent = (0, ua_parser_js_1.default)(request.headers["user-agent"] || "");
297
+ const userAgent = request.headers["user-agent"];
298
+ const parser = new UAParser(typeof userAgent === "string" ? userAgent : "");
299
+ const agent = parser.getResult();
323
300
  let friendlyAgent = "";
324
301
  if (agent.browser.name) {
325
302
  friendlyAgent = `${agent.browser.name} ${agent.browser.major || ""}`;
@@ -333,36 +310,33 @@ class Client {
333
310
  friendlyAgent += ` ${agent.os.version}`;
334
311
  }
335
312
  }
336
- client.config.sessions[token] = lodash_1.default.assign(client.config.sessions[token], {
313
+ this.config.sessions[token] = _.assign(this.config.sessions[token], {
337
314
  lastUse: Date.now(),
338
315
  ip: ip,
339
316
  agent: friendlyAgent,
340
317
  });
341
- client.save();
318
+ this.save();
342
319
  }
343
320
  setPassword(hash, callback) {
344
- const client = this;
345
- const oldHash = client.config.password;
346
- client.config.password = hash;
347
- client.manager.saveUser(client, function (err) {
321
+ const oldHash = this.config.password;
322
+ this.config.password = hash;
323
+ this.manager.saveUser(this, (err) => {
348
324
  if (err) {
349
325
  // If user file fails to write, reset it back
350
- client.config.password = oldHash;
326
+ this.config.password = oldHash;
351
327
  return callback(false);
352
328
  }
353
329
  return callback(true);
354
330
  });
355
331
  }
356
332
  input(data) {
357
- const client = this;
358
333
  data.text.split("\n").forEach((line) => {
359
334
  data.text = line;
360
- client.inputLine(data);
335
+ this.inputLine(data);
361
336
  });
362
337
  }
363
338
  inputLine(data) {
364
- const client = this;
365
- const target = client.find(data.target);
339
+ const target = this.find(data.target);
366
340
  if (!target) {
367
341
  return;
368
342
  }
@@ -372,9 +346,9 @@ class Client {
372
346
  let text = data.text;
373
347
  // This is either a normal message or a command escaped with a leading '/'
374
348
  if (text.charAt(0) !== "/" || text.charAt(1) === "/") {
375
- if (target.chan.type === chan_2.ChanType.LOBBY) {
376
- target.chan.pushMessage(this, new msg_1.default({
377
- type: msg_2.MessageType.ERROR,
349
+ if (target.chan.type === ChanType.LOBBY) {
350
+ target.chan.pushMessage(this, new Msg({
351
+ type: MessageType.ERROR,
378
352
  text: "Messages can not be sent to lobbies.",
379
353
  }));
380
354
  return;
@@ -389,27 +363,27 @@ class Client {
389
363
  const irc = target.network.irc;
390
364
  const connected = irc?.connected;
391
365
  const emitFailureDisconnected = () => {
392
- target.chan.pushMessage(this, new msg_1.default({
393
- type: msg_2.MessageType.ERROR,
366
+ target.chan.pushMessage(this, new Msg({
367
+ type: MessageType.ERROR,
394
368
  text: "You are not connected to the IRC network, unable to send your command.",
395
369
  }));
396
370
  };
397
- const plugin = inputs_1.default.userInputs.get(cmd);
371
+ const plugin = inputs.userInputs.get(cmd);
398
372
  if (plugin) {
399
373
  if (!connected && !plugin.allowDisconnected) {
400
374
  emitFailureDisconnected();
401
375
  return;
402
376
  }
403
- plugin.input.apply(client, [target.network, target.chan, cmd, args]);
377
+ plugin.input.apply(this, [target.network, target.chan, cmd, args]);
404
378
  return;
405
379
  }
406
- const extPlugin = inputs_1.default.pluginCommands.get(cmd);
380
+ const extPlugin = inputs.pluginCommands.get(cmd);
407
381
  if (extPlugin) {
408
382
  if (!connected && !extPlugin.allowDisconnected) {
409
383
  emitFailureDisconnected();
410
384
  return;
411
385
  }
412
- extPlugin.input(new publicClient_1.default(client, extPlugin.packageInfo), { network: target.network, chan: target.chan }, cmd, args);
386
+ extPlugin.input(new PublicClient(this, extPlugin.packageInfo), { network: target.network, chan: target.chan }, cmd, args);
413
387
  return;
414
388
  }
415
389
  if (!connected) {
@@ -427,7 +401,7 @@ class Client {
427
401
  // Ensure we don't have empty strings in the list of highlights
428
402
  const highlightsTokens = customHighlightString
429
403
  .split(",")
430
- .map((highlight) => (0, escapeRegExp_1.default)(highlight.trim()))
404
+ .map((highlight) => escapeRegExp(highlight.trim()))
431
405
  .filter((highlight) => highlight.length > 0);
432
406
  if (highlightsTokens.length === 0) {
433
407
  return null;
@@ -438,15 +412,20 @@ class Client {
438
412
  this.highlightExceptionRegex = compileHighlightRegex(this.config.clientSettings.highlightExceptions);
439
413
  }
440
414
  more(data) {
441
- const client = this;
442
- const target = client.find(data.target);
415
+ const target = this.find(data.target);
443
416
  if (!target) {
444
417
  return null;
445
418
  }
446
419
  const chan = target.chan;
447
420
  let messages = [];
448
421
  let index = 0;
449
- // If client requests -1, send last 100 messages
422
+ const enhancedSearch = Boolean(this.config.clientSettings.searchEnabled &&
423
+ this.config.clientSettings.enableEnhancedSearch);
424
+ // Use batch sizes for "more" requests, batch sizes are based on enableEnhancedSearch setting
425
+ // When enableEnhancedSearch = true send upto 10000 messages at a time
426
+ const batchSize = enhancedSearch ? 1000 : 100;
427
+ const maxBatchSize = enhancedSearch ? 10000 : 1000;
428
+ // If client requests -1, send last batch of messages
450
429
  if (data.lastId < 0) {
451
430
  index = chan.messages.length;
452
431
  }
@@ -457,23 +436,23 @@ class Client {
457
436
  if (index > 0) {
458
437
  let startIndex = index;
459
438
  if (data.condensed) {
460
- // Limit to 1000 messages (that's 10x normal limit)
461
- const indexToStop = Math.max(0, index - 1000);
462
- let realMessagesLeft = 100;
439
+ // Limit to maxBatchSize messages when condensed
440
+ const indexToStop = Math.max(0, index - maxBatchSize);
441
+ let realMessagesLeft = batchSize;
463
442
  for (let i = index - 1; i >= indexToStop; i--) {
464
443
  startIndex--;
465
- // Do not count condensed messages towards the 100 messages
466
- if (irc_1.condensedTypes.has(chan.messages[i].type)) {
444
+ // Do not count condensed messages towards the batch
445
+ if (condensedTypes.has(chan.messages[i].type)) {
467
446
  continue;
468
447
  }
469
- // Count up actual 100 visible messages
448
+ // Count up actual visible messages
470
449
  if (--realMessagesLeft === 0) {
471
450
  break;
472
451
  }
473
452
  }
474
453
  }
475
454
  else {
476
- startIndex = Math.max(0, index - 100);
455
+ startIndex = Math.max(0, index - batchSize);
477
456
  }
478
457
  messages = chan.messages.slice(startIndex, index);
479
458
  }
@@ -484,8 +463,7 @@ class Client {
484
463
  };
485
464
  }
486
465
  clearHistory(data) {
487
- const client = this;
488
- const target = client.find(data.target);
466
+ const target = this.find(data.target);
489
467
  if (!target) {
490
468
  return;
491
469
  }
@@ -493,14 +471,14 @@ class Client {
493
471
  target.chan.unread = 0;
494
472
  target.chan.highlight = 0;
495
473
  target.chan.firstUnread = 0;
496
- client.emit("history:clear", {
474
+ this.emit("history:clear", {
497
475
  target: target.chan.id,
498
476
  });
499
477
  if (!target.chan.isLoggable()) {
500
478
  return;
501
479
  }
502
480
  for (const messageStorage of this.messageStorage) {
503
- messageStorage.deleteChannel(target.network, target.chan).catch((e) => log_1.default.error(e));
481
+ messageStorage.deleteChannel(target.network, target.chan).catch((e) => log.error(e));
504
482
  }
505
483
  }
506
484
  async search(query) {
@@ -512,6 +490,28 @@ class Client {
512
490
  }
513
491
  return this.messageProvider.search(query);
514
492
  }
493
+ async getMessagesAround(data) {
494
+ const target = this.find(data.target);
495
+ if (!target) {
496
+ return null;
497
+ }
498
+ const chan = target.chan;
499
+ const network = target.network;
500
+ if (!this.messageProvider?.isEnabled) {
501
+ return null;
502
+ }
503
+ try {
504
+ const messages = await this.messageProvider.getMessagesAround(network.uuid, chan.name, data.time, 200);
505
+ return {
506
+ chan: chan.id,
507
+ messages: messages,
508
+ };
509
+ }
510
+ catch (err) {
511
+ log.error("Failed to get messages around time:", String(err));
512
+ return null;
513
+ }
514
+ }
515
515
  open(socketId, target) {
516
516
  // Due to how socket.io works internally, normal events may arrive later than
517
517
  // the disconnect event, and because we can't control this timing precisely,
@@ -538,17 +538,17 @@ class Client {
538
538
  this.emit("open", targetNetChan.chan.id);
539
539
  }
540
540
  sortChannels(netid, order) {
541
- const network = lodash_1.default.find(this.networks, { uuid: netid });
541
+ const network = _.find(this.networks, { uuid: netid });
542
542
  if (!network) {
543
543
  return;
544
544
  }
545
545
  network.channels.sort((a, b) => {
546
546
  // Always sort lobby to the top regardless of what the client has sent
547
547
  // Because there's a lot of code that presumes channels[0] is the lobby
548
- if (a.type === chan_2.ChanType.LOBBY) {
548
+ if (a.type === ChanType.LOBBY) {
549
549
  return -1;
550
550
  }
551
- else if (b.type === chan_2.ChanType.LOBBY) {
551
+ else if (b.type === ChanType.LOBBY) {
552
552
  return 1;
553
553
  }
554
554
  return order.indexOf(a.id) - order.indexOf(b.id);
@@ -569,23 +569,21 @@ class Client {
569
569
  });
570
570
  }
571
571
  names(data) {
572
- const client = this;
573
- const target = client.find(data.target);
572
+ const target = this.find(data.target);
574
573
  if (!target) {
575
574
  return;
576
575
  }
577
- client.emit("names", {
576
+ this.emit("names", {
578
577
  id: target.chan.id,
579
578
  users: target.chan.getSortedUsers(target.network.irc),
580
579
  });
581
580
  }
582
581
  part(network, chan) {
583
- const client = this;
584
- network.channels = lodash_1.default.without(network.channels, chan);
585
- client.mentions = client.mentions.filter((msg) => !(msg.chanId === chan.id));
582
+ network.channels = _.without(network.channels, chan);
583
+ this.mentions = this.mentions.filter((msg) => !(msg.chanId === chan.id));
586
584
  chan.destroy();
587
- client.save();
588
- client.emit("part", {
585
+ this.save();
586
+ this.emit("part", {
589
587
  chan: chan.id,
590
588
  });
591
589
  }
@@ -608,13 +606,12 @@ class Client {
608
606
  network.destroy();
609
607
  });
610
608
  for (const messageStorage of this.messageStorage) {
611
- messageStorage.close().catch((e) => log_1.default.error(e));
609
+ messageStorage.close().catch((e) => log.error(e));
612
610
  }
613
611
  }
614
612
  clientAttach(socketId, token) {
615
- const client = this;
616
- if (client.awayMessage && lodash_1.default.size(client.attachedClients) === 0) {
617
- client.networks.forEach(function (network) {
613
+ if (this.awayMessage && _.size(this.attachedClients) === 0) {
614
+ this.networks.forEach((network) => {
618
615
  // Only remove away on client attachment if
619
616
  // there is no away message on this network
620
617
  if (network.irc && !network.awayMessage) {
@@ -622,32 +619,30 @@ class Client {
622
619
  }
623
620
  });
624
621
  }
625
- const openChannel = client.lastActiveChannel;
626
- client.attachedClients[socketId] = { token, openChannel };
622
+ const openChannel = this.lastActiveChannel;
623
+ this.attachedClients[socketId] = { token, openChannel };
627
624
  }
628
625
  clientDetach(socketId) {
629
- const client = this;
630
626
  delete this.attachedClients[socketId];
631
- if (client.awayMessage && lodash_1.default.size(client.attachedClients) === 0) {
632
- client.networks.forEach(function (network) {
627
+ if (this.awayMessage && _.size(this.attachedClients) === 0) {
628
+ this.networks.forEach((network) => {
633
629
  // Only set away on client deattachment if
634
630
  // there is no away message on this network
635
631
  if (network.irc && !network.awayMessage) {
636
- network.irc.raw("AWAY", client.awayMessage);
632
+ network.irc.raw("AWAY", this.awayMessage);
637
633
  }
638
634
  });
639
635
  }
640
636
  }
641
- // TODO: type session to this.attachedClients
642
637
  registerPushSubscription(session, subscription, noSave = false) {
643
- if (!lodash_1.default.isPlainObject(subscription) ||
638
+ if (!_.isPlainObject(subscription) ||
644
639
  typeof subscription.endpoint !== "string" ||
645
640
  !/^https?:\/\//.test(subscription.endpoint) ||
646
- !lodash_1.default.isPlainObject(subscription.keys) ||
641
+ !_.isPlainObject(subscription.keys) ||
647
642
  !subscription.keys || // TS compiler doesn't understand isPlainObject
648
643
  typeof subscription.keys.p256dh !== "string" ||
649
644
  typeof subscription.keys.auth !== "string") {
650
- session.pushSubscription = null;
645
+ session.pushSubscription = undefined;
651
646
  return;
652
647
  }
653
648
  const data = {
@@ -667,12 +662,11 @@ class Client {
667
662
  this.config.sessions[token].pushSubscription = undefined;
668
663
  this.save();
669
664
  }
670
- save = lodash_1.default.debounce(function SaveClient() {
671
- if (config_1.default.values.public) {
665
+ save = _.debounce(function SaveClient() {
666
+ if (Config.values.public) {
672
667
  return;
673
668
  }
674
- const client = this;
675
- client.manager.saveUser(client);
669
+ this.manager.saveUser(this);
676
670
  }, 5000, { maxWait: 20000 });
677
671
  }
678
- exports.default = Client;
672
+ export default Client;