thelounge-plugin-ntfy 1.1.0 → 1.3.0
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 +11 -0
- package/docker-compose.yml +7 -0
- package/index.js +11 -7
- package/package.json +2 -2
- package/src/config.js +111 -5
- package/src/crypto.js +111 -0
- package/src/handler.js +66 -23
package/README.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# The Lounge ntfy Plugin
|
|
2
2
|
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+
|
|
3
6
|
A plugin for [The Lounge](https://thelounge.chat/) that sends a message to an [ntfy](https://ntfy.sh/) server whenever you are mentioned in a chat.
|
|
4
7
|
|
|
5
8
|
## Installation
|
|
@@ -41,6 +44,14 @@ To start/stop sending push notifications in the desired network, enter:
|
|
|
41
44
|
/ntfy start/stop
|
|
42
45
|
```
|
|
43
46
|
|
|
47
|
+
## Private Messages
|
|
48
|
+
|
|
49
|
+
By default, you will only be notified when you are mentioned, **this includes messages sent privately to you**. If you want to be notified of all private messages, enter this command and start the notifier like usual:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
/ntfy config set config.notify_on_private_messages true
|
|
53
|
+
```
|
|
54
|
+
|
|
44
55
|
## License
|
|
45
56
|
|
|
46
57
|
This plugin is licensed under [MIT](https://opensource.org/license/mit)
|
package/docker-compose.yml
CHANGED
|
@@ -8,3 +8,10 @@ services:
|
|
|
8
8
|
restart: unless-stopped
|
|
9
9
|
volumes:
|
|
10
10
|
- $PWD/tl:/var/opt/thelounge
|
|
11
|
+
- $PWD/src:/var/opt/thelounge/packages/thelounge-plugin-ntfy/src
|
|
12
|
+
- $PWD/index.js:/var/opt/thelounge/packages/thelounge-plugin-ntfy/index.js
|
|
13
|
+
- $PWD/package.json:/var/opt/thelounge/packages/thelounge-plugin-ntfy/package.json
|
|
14
|
+
- $PWD/package-lock.json:/var/opt/thelounge/packages/thelounge-plugin-ntfy/package-lock.json
|
|
15
|
+
|
|
16
|
+
# Installation: su node -c "thelounge install file:/var/opt/thelounge/packages/thelounge-plugin-ntfy"
|
|
17
|
+
# Removal: su node -c "thelounge uninstall thelounge-plugin-ntfy"
|
package/index.js
CHANGED
|
@@ -22,17 +22,17 @@ const ntfyCommand = {
|
|
|
22
22
|
say(`/${command} start - Start the ntfy listener for this network`);
|
|
23
23
|
say(`/${command} stop - Stop the ntfy listener for this network`);
|
|
24
24
|
say(
|
|
25
|
-
`/${command} status - Show the ntfy listener status for this network
|
|
25
|
+
`/${command} status - Show the ntfy listener status for this network`,
|
|
26
26
|
);
|
|
27
27
|
say(`/${command} test - Send a test notification`);
|
|
28
28
|
say(
|
|
29
|
-
`/${command} config set <setting_key> <setting_value> - Set a configuration setting
|
|
29
|
+
`/${command} config set <setting_key> <setting_value> - Set a configuration setting`,
|
|
30
30
|
);
|
|
31
31
|
say(
|
|
32
|
-
`/${command} config remove <setting_key> - Set configuration setting to null
|
|
32
|
+
`/${command} config remove <setting_key> - Set configuration setting to null`,
|
|
33
33
|
);
|
|
34
34
|
say(
|
|
35
|
-
`/${command} config print - Print the current configuration with warnings if any
|
|
35
|
+
`/${command} config print - Print the current configuration with warnings if any`,
|
|
36
36
|
);
|
|
37
37
|
};
|
|
38
38
|
|
|
@@ -197,7 +197,7 @@ const ntfyCommand = {
|
|
|
197
197
|
const response = saveUserSetting(
|
|
198
198
|
client.client.name,
|
|
199
199
|
settingKey,
|
|
200
|
-
settingValue
|
|
200
|
+
settingValue,
|
|
201
201
|
);
|
|
202
202
|
|
|
203
203
|
say(response);
|
|
@@ -217,7 +217,7 @@ const ntfyCommand = {
|
|
|
217
217
|
const response = saveUserSetting(
|
|
218
218
|
client.client.name,
|
|
219
219
|
settingKey,
|
|
220
|
-
null
|
|
220
|
+
null,
|
|
221
221
|
);
|
|
222
222
|
|
|
223
223
|
say(response);
|
|
@@ -228,6 +228,8 @@ const ntfyCommand = {
|
|
|
228
228
|
case "print": {
|
|
229
229
|
const [userConfig, errors] = loadUserConfig(client.client.name);
|
|
230
230
|
|
|
231
|
+
const sensitiveKeys = new Set(["ntfy.password", "ntfy.token"]);
|
|
232
|
+
|
|
231
233
|
const printConfig = (obj, parentKey = "") => {
|
|
232
234
|
for (const key in obj) {
|
|
233
235
|
const value = obj[key];
|
|
@@ -235,6 +237,8 @@ const ntfyCommand = {
|
|
|
235
237
|
|
|
236
238
|
if (typeof value === "object" && value !== null) {
|
|
237
239
|
printConfig(value, fullKey);
|
|
240
|
+
} else if (sensitiveKeys.has(fullKey) && value) {
|
|
241
|
+
say(`${fullKey}=********`);
|
|
238
242
|
} else {
|
|
239
243
|
say(`${fullKey}=${value}`);
|
|
240
244
|
}
|
|
@@ -257,7 +261,7 @@ const ntfyCommand = {
|
|
|
257
261
|
userConfig.ntfy.password
|
|
258
262
|
) {
|
|
259
263
|
say(
|
|
260
|
-
"Warning: Both ntfy.token and ntfy.username/password are set, ntfy.token will be used for authentication"
|
|
264
|
+
"Warning: Both ntfy.token and ntfy.username/password are set, ntfy.token will be used for authentication",
|
|
261
265
|
);
|
|
262
266
|
}
|
|
263
267
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thelounge-plugin-ntfy",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "A plugin for The Lounge that sends push notifications via ntfy when highlighted",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"thelounge",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"ajv": "^8.17.1",
|
|
30
30
|
"ajv-errors": "^3.0.0",
|
|
31
31
|
"ajv-formats": "^3.0.1",
|
|
32
|
-
"ntfy": "
|
|
32
|
+
"ntfy": "~1.13.3",
|
|
33
33
|
"thelounge": "^4.4.3"
|
|
34
34
|
}
|
|
35
35
|
}
|
package/src/config.js
CHANGED
|
@@ -5,6 +5,12 @@ const path = require("path");
|
|
|
5
5
|
const Ajv2019 = require("ajv/dist/2019").default;
|
|
6
6
|
const addFormats = require("ajv-formats");
|
|
7
7
|
const addErrors = require("ajv-errors");
|
|
8
|
+
const {
|
|
9
|
+
initEncryptionKey,
|
|
10
|
+
encrypt,
|
|
11
|
+
decrypt,
|
|
12
|
+
isEncrypted,
|
|
13
|
+
} = require("./crypto.js");
|
|
8
14
|
|
|
9
15
|
const DEFAULT_CONFIG = {
|
|
10
16
|
ntfy: {
|
|
@@ -14,6 +20,9 @@ const DEFAULT_CONFIG = {
|
|
|
14
20
|
password: null,
|
|
15
21
|
token: null,
|
|
16
22
|
},
|
|
23
|
+
config: {
|
|
24
|
+
notify_on_private_messages: false,
|
|
25
|
+
},
|
|
17
26
|
};
|
|
18
27
|
|
|
19
28
|
const ALLOWED_KEYS = new Set([
|
|
@@ -22,12 +31,17 @@ const ALLOWED_KEYS = new Set([
|
|
|
22
31
|
"ntfy.username",
|
|
23
32
|
"ntfy.password",
|
|
24
33
|
"ntfy.token",
|
|
34
|
+
"config.notify_on_private_messages",
|
|
25
35
|
]);
|
|
26
36
|
|
|
37
|
+
const BOOLEAN_KEYS = new Set(["config.notify_on_private_messages"]);
|
|
38
|
+
|
|
39
|
+
const SENSITIVE_KEYS = new Set(["ntfy.password", "ntfy.token"]);
|
|
40
|
+
|
|
27
41
|
const userConfigSchema = {
|
|
28
42
|
type: "object",
|
|
29
43
|
additionalProperties: false,
|
|
30
|
-
required: ["ntfy"],
|
|
44
|
+
required: ["ntfy", "config"],
|
|
31
45
|
properties: {
|
|
32
46
|
ntfy: {
|
|
33
47
|
type: "object",
|
|
@@ -90,6 +104,17 @@ const userConfigSchema = {
|
|
|
90
104
|
password: ["username"],
|
|
91
105
|
},
|
|
92
106
|
},
|
|
107
|
+
config: {
|
|
108
|
+
type: "object",
|
|
109
|
+
additionalProperties: false,
|
|
110
|
+
required: ["notify_on_private_messages"],
|
|
111
|
+
properties: {
|
|
112
|
+
notify_on_private_messages: {
|
|
113
|
+
type: "boolean",
|
|
114
|
+
default: false,
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
},
|
|
93
118
|
},
|
|
94
119
|
};
|
|
95
120
|
|
|
@@ -104,6 +129,38 @@ let rootDir = null;
|
|
|
104
129
|
|
|
105
130
|
function setRootDir(dir) {
|
|
106
131
|
rootDir = dir;
|
|
132
|
+
initEncryptionKey(dir);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function decryptSensitiveFields(config) {
|
|
136
|
+
const decrypted = JSON.parse(JSON.stringify(config)); // Deep clone
|
|
137
|
+
|
|
138
|
+
for (const key of SENSITIVE_KEYS) {
|
|
139
|
+
const keys = key.split(".");
|
|
140
|
+
let curr = decrypted;
|
|
141
|
+
|
|
142
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
143
|
+
if (curr[keys[i]]) {
|
|
144
|
+
curr = curr[keys[i]];
|
|
145
|
+
} else {
|
|
146
|
+
curr = null;
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (curr) {
|
|
152
|
+
const finalKey = keys[keys.length - 1];
|
|
153
|
+
if (curr[finalKey] && isEncrypted(curr[finalKey])) {
|
|
154
|
+
try {
|
|
155
|
+
curr[finalKey] = decrypt(curr[finalKey]);
|
|
156
|
+
} catch (e) {
|
|
157
|
+
// Leave as-is
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return decrypted;
|
|
107
164
|
}
|
|
108
165
|
|
|
109
166
|
function loadUserConfig(username) {
|
|
@@ -125,10 +182,12 @@ function loadUserConfig(username) {
|
|
|
125
182
|
userConfig = JSON.parse(fs.readFileSync(userConfigPath, "utf-8"));
|
|
126
183
|
} catch (e) {
|
|
127
184
|
throw new Error(
|
|
128
|
-
`Invalid JSON in user config for ${username}: ${e.message}
|
|
185
|
+
`Invalid JSON in user config for ${username}: ${e.message}`,
|
|
129
186
|
);
|
|
130
187
|
}
|
|
131
188
|
|
|
189
|
+
userConfig = decryptSensitiveFields(userConfig);
|
|
190
|
+
|
|
132
191
|
const validate = ajv.compile(userConfigSchema);
|
|
133
192
|
const valid = validate(userConfig);
|
|
134
193
|
|
|
@@ -136,6 +195,33 @@ function loadUserConfig(username) {
|
|
|
136
195
|
}
|
|
137
196
|
}
|
|
138
197
|
|
|
198
|
+
function encryptSensitiveFields(config) {
|
|
199
|
+
const encrypted = JSON.parse(JSON.stringify(config)); // Deep clone
|
|
200
|
+
|
|
201
|
+
for (const key of SENSITIVE_KEYS) {
|
|
202
|
+
const keys = key.split(".");
|
|
203
|
+
let curr = encrypted;
|
|
204
|
+
|
|
205
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
206
|
+
if (curr[keys[i]]) {
|
|
207
|
+
curr = curr[keys[i]];
|
|
208
|
+
} else {
|
|
209
|
+
curr = null;
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (curr) {
|
|
215
|
+
const finalKey = keys[keys.length - 1];
|
|
216
|
+
if (curr[finalKey] && !isEncrypted(curr[finalKey])) {
|
|
217
|
+
curr[finalKey] = encrypt(curr[finalKey]);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return encrypted;
|
|
223
|
+
}
|
|
224
|
+
|
|
139
225
|
function saveUserSetting(username, settingKey, settingValue) {
|
|
140
226
|
if (!rootDir) {
|
|
141
227
|
throw new Error("Root directory is not set");
|
|
@@ -155,22 +241,42 @@ function saveUserSetting(username, settingKey, settingValue) {
|
|
|
155
241
|
}
|
|
156
242
|
}
|
|
157
243
|
|
|
244
|
+
if (settingValue && typeof settingValue !== "string") {
|
|
245
|
+
return `Error: expected value to be a string`;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (BOOLEAN_KEYS.has(settingKey)) {
|
|
249
|
+
try {
|
|
250
|
+
settingValue = settingValue
|
|
251
|
+
? JSON.parse(settingValue.toLowerCase())
|
|
252
|
+
: false;
|
|
253
|
+
|
|
254
|
+
if (typeof settingValue !== "boolean") {
|
|
255
|
+
return `Invalid value for ${settingKey}, expected a boolean`;
|
|
256
|
+
}
|
|
257
|
+
} catch {
|
|
258
|
+
return `Invalid value for ${settingKey}, expected a boolean`;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
158
262
|
curr[keys[keys.length - 1]] = settingValue;
|
|
159
263
|
|
|
264
|
+
const configToSave = encryptSensitiveFields(userConfig);
|
|
265
|
+
|
|
160
266
|
const userConfigPath = path.join(rootDir, "config", `${username}.json`);
|
|
161
267
|
|
|
162
268
|
fs.mkdirSync(path.dirname(userConfigPath), { recursive: true });
|
|
163
269
|
fs.writeFileSync(
|
|
164
270
|
userConfigPath,
|
|
165
|
-
JSON.stringify(
|
|
166
|
-
"utf-8"
|
|
271
|
+
JSON.stringify(configToSave, null, 2),
|
|
272
|
+
"utf-8",
|
|
167
273
|
);
|
|
168
274
|
|
|
169
275
|
return "Success";
|
|
170
276
|
}
|
|
171
277
|
|
|
172
278
|
return `Invalid setting ${settingKey}, allowed settings are: ${Array.from(
|
|
173
|
-
ALLOWED_KEYS
|
|
279
|
+
ALLOWED_KEYS,
|
|
174
280
|
).join(", ")}`;
|
|
175
281
|
}
|
|
176
282
|
|
package/src/crypto.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const crypto = require("crypto");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
|
|
7
|
+
const ALGORITHM = "aes-256-gcm";
|
|
8
|
+
const KEY_LENGTH = 32; // 256 bits
|
|
9
|
+
const IV_LENGTH = 16; // 128 bits
|
|
10
|
+
const AUTH_TAG_LENGTH = 16; // 128 bits
|
|
11
|
+
const ENCRYPTED_PREFIX = "enc:";
|
|
12
|
+
|
|
13
|
+
let encryptionKey = null;
|
|
14
|
+
|
|
15
|
+
function initEncryptionKey(configDir) {
|
|
16
|
+
const keyPath = path.join(configDir, ".ntfy_key");
|
|
17
|
+
|
|
18
|
+
if (fs.existsSync(keyPath)) {
|
|
19
|
+
const keyData = fs.readFileSync(keyPath, "utf-8").trim();
|
|
20
|
+
encryptionKey = Buffer.from(keyData, "hex");
|
|
21
|
+
|
|
22
|
+
if (encryptionKey.length !== KEY_LENGTH) {
|
|
23
|
+
throw new Error("Invalid encryption key length");
|
|
24
|
+
}
|
|
25
|
+
} else {
|
|
26
|
+
encryptionKey = crypto.randomBytes(KEY_LENGTH);
|
|
27
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
28
|
+
fs.writeFileSync(keyPath, encryptionKey.toString("hex"), {
|
|
29
|
+
encoding: "utf-8",
|
|
30
|
+
mode: 0o600, // R/W for owner only
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function isInitialized() {
|
|
36
|
+
return encryptionKey !== null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function encrypt(plaintext) {
|
|
40
|
+
if (!encryptionKey) {
|
|
41
|
+
throw new Error("Encryption key not initialized");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!plaintext || typeof plaintext !== "string") {
|
|
45
|
+
return plaintext;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (plaintext.startsWith(ENCRYPTED_PREFIX)) {
|
|
49
|
+
return plaintext;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const iv = crypto.randomBytes(IV_LENGTH);
|
|
53
|
+
const cipher = crypto.createCipheriv(ALGORITHM, encryptionKey, iv, {
|
|
54
|
+
authTagLength: AUTH_TAG_LENGTH,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
let encrypted = cipher.update(plaintext, "utf8", "hex");
|
|
58
|
+
encrypted += cipher.final("hex");
|
|
59
|
+
|
|
60
|
+
const authTag = cipher.getAuthTag();
|
|
61
|
+
|
|
62
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("hex")}:${authTag.toString("hex")}:${encrypted}`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function decrypt(encryptedText) {
|
|
66
|
+
if (!encryptionKey) {
|
|
67
|
+
throw new Error("Encryption key not initialized");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (!encryptedText || typeof encryptedText !== "string") {
|
|
71
|
+
return encryptedText;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (!encryptedText.startsWith(ENCRYPTED_PREFIX)) {
|
|
75
|
+
return encryptedText;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const data = encryptedText.slice(ENCRYPTED_PREFIX.length);
|
|
79
|
+
const parts = data.split(":");
|
|
80
|
+
|
|
81
|
+
if (parts.length !== 3) {
|
|
82
|
+
throw new Error("Invalid encrypted data format");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const [ivHex, authTagHex, encrypted] = parts;
|
|
86
|
+
const iv = Buffer.from(ivHex, "hex");
|
|
87
|
+
const authTag = Buffer.from(authTagHex, "hex");
|
|
88
|
+
|
|
89
|
+
const decipher = crypto.createDecipheriv(ALGORITHM, encryptionKey, iv, {
|
|
90
|
+
authTagLength: AUTH_TAG_LENGTH,
|
|
91
|
+
});
|
|
92
|
+
decipher.setAuthTag(authTag);
|
|
93
|
+
|
|
94
|
+
let decrypted = decipher.update(encrypted, "hex", "utf8");
|
|
95
|
+
decrypted += decipher.final("utf8");
|
|
96
|
+
|
|
97
|
+
return decrypted;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function isEncrypted(value) {
|
|
101
|
+
return typeof value === "string" && value.startsWith(ENCRYPTED_PREFIX);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
module.exports = {
|
|
105
|
+
initEncryptionKey,
|
|
106
|
+
isInitialized,
|
|
107
|
+
encrypt,
|
|
108
|
+
decrypt,
|
|
109
|
+
isEncrypted,
|
|
110
|
+
ENCRYPTED_PREFIX,
|
|
111
|
+
};
|
package/src/handler.js
CHANGED
|
@@ -1,45 +1,88 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
const { loadUserConfig } = require("./config.js");
|
|
4
|
+
const { PluginLogger } = require("./logger.js");
|
|
4
5
|
|
|
5
6
|
function createHandler(client, network) {
|
|
6
7
|
return async (data) => {
|
|
8
|
+
// Ignore own messages
|
|
9
|
+
if (network.nick === data.nick) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
7
13
|
const highlightRegex = new RegExp(network.highlightRegex, "i");
|
|
8
14
|
const message = data.message || "";
|
|
9
15
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
16
|
+
const mentioned = highlightRegex.test(message);
|
|
17
|
+
const isPM = data.target === network.nick;
|
|
18
|
+
|
|
19
|
+
let notify = false;
|
|
20
|
+
let userConfig;
|
|
21
|
+
|
|
22
|
+
if (mentioned) {
|
|
23
|
+
// Mentions always notify
|
|
24
|
+
notify = true;
|
|
25
|
+
} else if (isPM) {
|
|
26
|
+
// PMs notify only if enabled in config
|
|
27
|
+
const [uc, errors] = loadUserConfig(client.client.name);
|
|
13
28
|
|
|
14
29
|
if (errors.length > 0) {
|
|
15
30
|
return;
|
|
16
31
|
}
|
|
17
32
|
|
|
18
|
-
|
|
33
|
+
userConfig = uc;
|
|
19
34
|
|
|
20
|
-
if (userConfig.
|
|
21
|
-
|
|
22
|
-
} else if (userConfig.ntfy.username && userConfig.ntfy.password) {
|
|
23
|
-
ntfyAuth = {
|
|
24
|
-
username: userConfig.ntfy.username,
|
|
25
|
-
password: userConfig.ntfy.password,
|
|
26
|
-
};
|
|
35
|
+
if (userConfig.config.notify_on_private_messages) {
|
|
36
|
+
notify = true;
|
|
27
37
|
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (notify) {
|
|
41
|
+
try {
|
|
42
|
+
// Avoid needlessly loading user config multiple times
|
|
43
|
+
if (!userConfig) {
|
|
44
|
+
const [uc, errors] = loadUserConfig(client.client.name);
|
|
28
45
|
|
|
29
|
-
|
|
46
|
+
if (errors.length > 0) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
30
49
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
topic: userConfig.ntfy.topic,
|
|
34
|
-
priority: MessagePriority.HIGH,
|
|
35
|
-
tags: ["speech_balloon"],
|
|
36
|
-
authorization: ntfyAuth,
|
|
37
|
-
});
|
|
50
|
+
userConfig = uc;
|
|
51
|
+
}
|
|
38
52
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
53
|
+
let ntfyAuth;
|
|
54
|
+
|
|
55
|
+
if (userConfig.ntfy.token) {
|
|
56
|
+
ntfyAuth = {
|
|
57
|
+
username: "",
|
|
58
|
+
password: userConfig.ntfy.token,
|
|
59
|
+
};
|
|
60
|
+
} else if (userConfig.ntfy.username && userConfig.ntfy.password) {
|
|
61
|
+
ntfyAuth = {
|
|
62
|
+
username: userConfig.ntfy.username,
|
|
63
|
+
password: userConfig.ntfy.password,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const { NtfyClient, MessagePriority } = await import("ntfy");
|
|
68
|
+
|
|
69
|
+
const ntfyClient = new NtfyClient({
|
|
70
|
+
server: userConfig.ntfy.server,
|
|
71
|
+
topic: userConfig.ntfy.topic,
|
|
72
|
+
priority: MessagePriority.HIGH,
|
|
73
|
+
tags: ["speech_balloon"],
|
|
74
|
+
authorization: ntfyAuth,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
ntfyClient.publish({
|
|
78
|
+
title: isPM
|
|
79
|
+
? `${network.name}: ${data.nick}`
|
|
80
|
+
: `${network.name} ${data.target}: ${data.nick}`,
|
|
81
|
+
message: message,
|
|
82
|
+
});
|
|
83
|
+
} catch (e) {
|
|
84
|
+
PluginLogger.error("Failed to send ntfy notification", e);
|
|
85
|
+
}
|
|
43
86
|
}
|
|
44
87
|
};
|
|
45
88
|
}
|