@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.
- 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
package/dist/server/client.js
CHANGED
|
@@ -1,50 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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 = (
|
|
94
|
-
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
98
|
+
this.messageStorage.push(this.messageProvider);
|
|
122
99
|
}
|
|
123
|
-
if (
|
|
124
|
-
|
|
100
|
+
if (Config.values.messageStorage.includes("text")) {
|
|
101
|
+
this.messageStorage.push(new TextFileMessageStorage(this.name));
|
|
125
102
|
}
|
|
126
|
-
for (const messageStorage of
|
|
127
|
-
messageStorage.enable().catch((e) =>
|
|
103
|
+
for (const messageStorage of this.messageStorage) {
|
|
104
|
+
messageStorage.enable().catch((e) => log.error(e));
|
|
128
105
|
}
|
|
129
106
|
}
|
|
130
|
-
if (!
|
|
131
|
-
|
|
107
|
+
if (!_.isPlainObject(this.config.sessions)) {
|
|
108
|
+
this.config.sessions = {};
|
|
132
109
|
}
|
|
133
|
-
if (!
|
|
134
|
-
|
|
110
|
+
if (!_.isPlainObject(this.config.clientSettings)) {
|
|
111
|
+
this.config.clientSettings = {};
|
|
135
112
|
}
|
|
136
|
-
if (!
|
|
137
|
-
|
|
113
|
+
if (!_.isPlainObject(this.config.browser)) {
|
|
114
|
+
this.config.browser = {};
|
|
138
115
|
}
|
|
139
|
-
if (
|
|
140
|
-
|
|
116
|
+
if (this.config.clientSettings.awayMessage) {
|
|
117
|
+
this.awayMessage = this.config.clientSettings.awayMessage;
|
|
141
118
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
152
|
-
|
|
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
|
-
(
|
|
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
|
|
159
|
-
if (
|
|
160
|
-
|
|
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 =
|
|
164
|
-
|
|
139
|
+
let delay = this.manager.clients.length * 500;
|
|
140
|
+
this.networks.forEach((network) => {
|
|
165
141
|
setTimeout(() => {
|
|
166
|
-
network.channels.forEach((channel) => channel.loadMessages(
|
|
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
|
-
|
|
149
|
+
this.fileHash = this.manager.getDataToSave(this).newHash;
|
|
174
150
|
}
|
|
175
151
|
}
|
|
176
152
|
createChannel(attr) {
|
|
177
|
-
const chan = new
|
|
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 =
|
|
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 =
|
|
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(
|
|
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 &&
|
|
220
|
-
|
|
221
|
-
|
|
195
|
+
if (badChanConf && this.name) {
|
|
196
|
+
log.warn("User '" +
|
|
197
|
+
this.name +
|
|
222
198
|
"' on network '" +
|
|
223
|
-
|
|
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
|
|
213
|
+
return this.createChannel({
|
|
238
214
|
name: chan,
|
|
239
215
|
});
|
|
240
216
|
});
|
|
241
217
|
}
|
|
242
|
-
//
|
|
243
|
-
|
|
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:
|
|
246
|
-
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:
|
|
252
|
-
nick:
|
|
253
|
-
username:
|
|
254
|
-
realname:
|
|
255
|
-
leaveMessage:
|
|
256
|
-
sasl:
|
|
257
|
-
saslAccount:
|
|
258
|
-
saslPassword:
|
|
259
|
-
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
|
|
238
|
+
ignoreList: args.ignoreList || [],
|
|
262
239
|
proxyEnabled: !!args.proxyEnabled,
|
|
263
|
-
proxyHost:
|
|
264
|
-
proxyPort: parseInt(args.proxyPort, 10),
|
|
265
|
-
proxyUsername:
|
|
266
|
-
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 =
|
|
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
|
-
|
|
279
|
-
|
|
254
|
+
this.networks.push(network);
|
|
255
|
+
this.emit("network", {
|
|
280
256
|
network: network.getFilteredClone(this.lastActiveChannel, -1),
|
|
281
257
|
});
|
|
282
|
-
if (!network.validate(
|
|
258
|
+
if (!network.validate(this)) {
|
|
283
259
|
return;
|
|
284
260
|
}
|
|
285
|
-
network.createIrcFramework(
|
|
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
|
|
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(
|
|
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
|
-
|
|
306
|
-
network.channels.forEach((channel) => channel.loadMessages(
|
|
281
|
+
this.save();
|
|
282
|
+
network.channels.forEach((channel) => channel.loadMessages(this, network));
|
|
307
283
|
}
|
|
308
284
|
}
|
|
309
285
|
generateToken(callback) {
|
|
310
|
-
|
|
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
|
|
294
|
+
return crypto.createHash("sha512").update(token).digest("hex");
|
|
319
295
|
}
|
|
320
296
|
updateSession(token, ip, request) {
|
|
321
|
-
const
|
|
322
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
318
|
+
this.save();
|
|
342
319
|
}
|
|
343
320
|
setPassword(hash, callback) {
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
|
|
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
|
-
|
|
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
|
-
|
|
335
|
+
this.inputLine(data);
|
|
361
336
|
});
|
|
362
337
|
}
|
|
363
338
|
inputLine(data) {
|
|
364
|
-
const
|
|
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 ===
|
|
376
|
-
target.chan.pushMessage(this, new
|
|
377
|
-
type:
|
|
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
|
|
393
|
-
type:
|
|
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 =
|
|
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(
|
|
377
|
+
plugin.input.apply(this, [target.network, target.chan, cmd, args]);
|
|
404
378
|
return;
|
|
405
379
|
}
|
|
406
|
-
const extPlugin =
|
|
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
|
|
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) => (
|
|
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
|
|
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
|
-
|
|
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
|
|
461
|
-
const indexToStop = Math.max(0, index -
|
|
462
|
-
let realMessagesLeft =
|
|
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
|
|
466
|
-
if (
|
|
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
|
|
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 -
|
|
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
|
|
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
|
-
|
|
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) =>
|
|
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 =
|
|
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 ===
|
|
548
|
+
if (a.type === ChanType.LOBBY) {
|
|
549
549
|
return -1;
|
|
550
550
|
}
|
|
551
|
-
else if (b.type ===
|
|
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
|
|
573
|
-
const target = client.find(data.target);
|
|
572
|
+
const target = this.find(data.target);
|
|
574
573
|
if (!target) {
|
|
575
574
|
return;
|
|
576
575
|
}
|
|
577
|
-
|
|
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
|
-
|
|
584
|
-
|
|
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
|
-
|
|
588
|
-
|
|
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) =>
|
|
609
|
+
messageStorage.close().catch((e) => log.error(e));
|
|
612
610
|
}
|
|
613
611
|
}
|
|
614
612
|
clientAttach(socketId, token) {
|
|
615
|
-
|
|
616
|
-
|
|
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 =
|
|
626
|
-
|
|
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 (
|
|
632
|
-
|
|
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",
|
|
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 (!
|
|
638
|
+
if (!_.isPlainObject(subscription) ||
|
|
644
639
|
typeof subscription.endpoint !== "string" ||
|
|
645
640
|
!/^https?:\/\//.test(subscription.endpoint) ||
|
|
646
|
-
!
|
|
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 =
|
|
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 =
|
|
671
|
-
if (
|
|
665
|
+
save = _.debounce(function SaveClient() {
|
|
666
|
+
if (Config.values.public) {
|
|
672
667
|
return;
|
|
673
668
|
}
|
|
674
|
-
|
|
675
|
-
client.manager.saveUser(client);
|
|
669
|
+
this.manager.saveUser(this);
|
|
676
670
|
}, 5000, { maxWait: 20000 });
|
|
677
671
|
}
|
|
678
|
-
|
|
672
|
+
export default Client;
|