thelounge-plugin-ntfy 1.4.0 → 1.6.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 CHANGED
@@ -19,9 +19,9 @@ Restart The Lounge after installation
19
19
 
20
20
  This plugin introduces the `/ntfy` command, subcommands are:
21
21
 
22
- - `/ntfy start`: Start the ntfy listener for the network
23
- - `/ntfy stop`: Stop the ntfy listener for the network
24
- - `/ntfy status`: Show the ntfy listener status for this network
22
+ - `/ntfy start [all]`: Start the ntfy listener for the network or all networks if 'all' is specified
23
+ - `/ntfy stop [all]`: Stop the ntfy listener for the network or all networks if 'all' is specified
24
+ - `/ntfy status [all]`: Show the ntfy listener status for this network or all networks if 'all' is specified
25
25
  - `/ntfy test`: Send a test notification
26
26
  - `/ntfy config`: Config commands
27
27
  - `/ntfy config set <setting_key> <setting_value>`: Set a global configuration setting
package/index.js CHANGED
@@ -21,10 +21,14 @@ const ntfyCommand = {
21
21
 
22
22
  const helpMessage = () => {
23
23
  say(`${command} command help:`);
24
- say(`/${command} start - Start the ntfy listener for this network`);
25
- say(`/${command} stop - Stop the ntfy listener for this network`);
26
24
  say(
27
- `/${command} status - Show the ntfy listener status for this network`,
25
+ `/${command} start [all] - Start the ntfy listener for this network or all networks if 'all' is specified`,
26
+ );
27
+ say(
28
+ `/${command} stop [all] - Stop the ntfy listener for this network or all networks if 'all' is specified`,
29
+ );
30
+ say(
31
+ `/${command} status [all] - Show the ntfy listener status for this network or all networks if 'all' is specified`,
28
32
  );
29
33
  say(`/${command} test - Send a test notification`);
30
34
  say(
@@ -59,82 +63,221 @@ const ntfyCommand = {
59
63
 
60
64
  switch (subcommand[0].toLowerCase()) {
61
65
  case "start": {
66
+ let all = false;
62
67
  const [_, errors] = loadUserConfig(client.client.name);
68
+ const subsubcommand = subcommand.slice(1);
69
+
70
+ if (
71
+ typeof subsubcommand[0] === "string" &&
72
+ subsubcommand.length > 0 &&
73
+ subsubcommand[0].toLowerCase() === "all"
74
+ ) {
75
+ all = true;
76
+ }
63
77
 
64
78
  if (errors.length > 0) {
65
- say("Cannot start ntfy listener due to invalid configuration:");
79
+ say(
80
+ `Cannot start ntfy listener${all ? "s" : ""} due to invalid configuration:`,
81
+ );
82
+
66
83
  for (const error of errors) {
67
84
  say(`- ${error.instancePath} ${error.message}`);
68
85
  }
86
+
69
87
  return;
70
88
  }
71
89
 
72
90
  const userListeners = globalActiveListeners.get(client.client.name);
73
91
 
74
- if (
75
- userListeners &&
76
- typeof userListeners.has === "function" &&
77
- userListeners.has(network.uuid)
78
- ) {
79
- say("ntfy listener is already running for this network");
80
- return;
81
- }
92
+ if (all) {
93
+ const networks = client.client.networks;
94
+ let started = 0;
95
+ let skipped = 0;
96
+
97
+ for (const net of networks) {
98
+ if (
99
+ userListeners &&
100
+ typeof userListeners.has === "function" &&
101
+ userListeners.has(net.uuid)
102
+ ) {
103
+ skipped++;
104
+ continue;
105
+ }
106
+
107
+ const handler = createHandler(client, net);
108
+ net.irc.on("privmsg", handler);
109
+
110
+ if (!userListeners) {
111
+ const map = new Map();
112
+ map.set(net.uuid, { handler: handler, client: client });
113
+ globalActiveListeners.set(client.client.name, map);
114
+ } else {
115
+ userListeners.set(net.uuid, {
116
+ handler: handler,
117
+ client: client,
118
+ });
119
+ }
82
120
 
83
- const handler = createHandler(client, network);
84
- network.irc.on("privmsg", handler);
121
+ started++;
122
+ }
85
123
 
86
- if (!userListeners) {
87
- const map = new Map();
88
- map.set(network.uuid, { handler: handler, client: client });
89
- globalActiveListeners.set(client.client.name, map);
124
+ if (started === 0 && skipped === 0) {
125
+ say("No networks available to start ntfy listeners");
126
+ return;
127
+ }
128
+ if (started > 0) {
129
+ say(
130
+ `Started ntfy listener${started !== 1 ? "s" : ""} for ${started} network${started !== 1 ? "s" : ""}`,
131
+ );
132
+ }
133
+ if (skipped > 0) {
134
+ say(
135
+ `Skipped ${skipped} network${skipped !== 1 ? "s" : ""} because ntfy listener is already running`,
136
+ );
137
+ }
90
138
  } else {
91
- userListeners.set(network.uuid, { handler: handler, client: client });
92
- }
139
+ if (
140
+ userListeners &&
141
+ typeof userListeners.has === "function" &&
142
+ userListeners.has(network.uuid)
143
+ ) {
144
+ say("ntfy listener is already running for this network");
145
+ return;
146
+ }
147
+
148
+ const handler = createHandler(client, network);
149
+ network.irc.on("privmsg", handler);
150
+
151
+ if (!userListeners) {
152
+ const map = new Map();
153
+ map.set(network.uuid, { handler: handler, client: client });
154
+ globalActiveListeners.set(client.client.name, map);
155
+ } else {
156
+ userListeners.set(network.uuid, {
157
+ handler: handler,
158
+ client: client,
159
+ });
160
+ }
93
161
 
94
- say("ntfy listener started for this network");
162
+ say("ntfy listener started for this network");
163
+ }
95
164
 
96
165
  break;
97
166
  }
98
167
 
99
168
  case "stop": {
100
- const userListeners = globalActiveListeners.get(client.client.name);
169
+ let all = false;
170
+ const subsubcommand = subcommand.slice(1);
101
171
 
102
172
  if (
103
- !userListeners ||
104
- typeof userListeners.has !== "function" ||
105
- !userListeners.has(network.uuid)
173
+ typeof subsubcommand[0] === "string" &&
174
+ subsubcommand.length > 0 &&
175
+ subsubcommand[0].toLowerCase() === "all"
106
176
  ) {
107
- say("ntfy listener is not running for this network");
108
- return;
177
+ all = true;
109
178
  }
110
179
 
111
- const { handler } = userListeners.get(network.uuid);
112
- network.irc.removeListener("privmsg", handler);
113
- userListeners.delete(network.uuid);
180
+ const userListeners = globalActiveListeners.get(client.client.name);
181
+
182
+ if (all) {
183
+ const networks = client.client.networks;
184
+ let stopped = 0;
185
+ let skipped = 0;
186
+
187
+ for (const net of networks) {
188
+ if (
189
+ !userListeners ||
190
+ typeof userListeners.has !== "function" ||
191
+ !userListeners.has(net.uuid)
192
+ ) {
193
+ skipped++;
194
+ continue;
195
+ }
196
+
197
+ const { handler } = userListeners.get(net.uuid);
198
+ net.irc.removeListener("privmsg", handler);
199
+ userListeners.delete(net.uuid);
200
+ stopped++;
201
+ }
202
+
203
+ if (stopped === 0 && skipped === 0) {
204
+ say("No networks available to stop ntfy listeners");
205
+ return;
206
+ }
207
+ if (stopped > 0) {
208
+ say(
209
+ `Stopped ntfy listener${stopped !== 1 ? "s" : ""} for ${stopped} network${stopped !== 1 ? "s" : ""}`,
210
+ );
211
+ }
212
+ if (skipped > 0) {
213
+ say(
214
+ `Skipped ${skipped} network${skipped !== 1 ? "s" : ""} because ntfy listener was not running`,
215
+ );
216
+ }
217
+ } else {
218
+ if (
219
+ !userListeners ||
220
+ typeof userListeners.has !== "function" ||
221
+ !userListeners.has(network.uuid)
222
+ ) {
223
+ say("ntfy listener is not running for this network");
224
+ return;
225
+ }
226
+
227
+ const { handler } = userListeners.get(network.uuid);
228
+ network.irc.removeListener("privmsg", handler);
229
+ userListeners.delete(network.uuid);
114
230
 
115
- say("ntfy listener stopped for this network");
231
+ say("ntfy listener stopped for this network");
232
+ }
116
233
 
117
234
  break;
118
235
  }
119
236
 
120
237
  case "status": {
121
- const userListeners = globalActiveListeners.get(client.client.name);
238
+ let all = false;
239
+ const subsubcommand = subcommand.slice(1);
122
240
 
123
241
  if (
124
- userListeners &&
125
- typeof userListeners.has === "function" &&
126
- userListeners.has(network.uuid)
242
+ typeof subsubcommand[0] === "string" &&
243
+ subsubcommand.length > 0 &&
244
+ subsubcommand[0].toLowerCase() === "all"
127
245
  ) {
128
- say("ntfy listener is running for this network");
246
+ all = true;
247
+ }
248
+
249
+ const userListeners = globalActiveListeners.get(client.client.name);
250
+
251
+ if (all) {
252
+ const networks = client.client.networks;
253
+ for (const net of networks) {
254
+ if (
255
+ userListeners &&
256
+ typeof userListeners.has === "function" &&
257
+ userListeners.has(net.uuid)
258
+ ) {
259
+ say(`${net.name}: running`);
260
+ } else {
261
+ say(`${net.name}: not running`);
262
+ }
263
+ }
129
264
  } else {
130
- say("ntfy listener is not running for this network");
265
+ if (
266
+ userListeners &&
267
+ typeof userListeners.has === "function" &&
268
+ userListeners.has(network.uuid)
269
+ ) {
270
+ say("ntfy listener is running for this network");
271
+ } else {
272
+ say("ntfy listener is not running for this network");
273
+ }
131
274
  }
132
275
 
133
276
  break;
134
277
  }
135
278
 
136
279
  case "test": {
137
- const { NtfyClient, MessagePriority } = await import("ntfy");
280
+ const { NtfyClient } = await import("ntfy");
138
281
 
139
282
  const [userConfig, errors] = loadUserConfig(client.client.name);
140
283
 
@@ -160,7 +303,7 @@ const ntfyCommand = {
160
303
  const ntfyClient = new NtfyClient({
161
304
  server: userConfig.ntfy.server,
162
305
  topic: userConfig.ntfy.topic,
163
- priority: MessagePriority.HIGH,
306
+ priority: userConfig.ntfy.priority,
164
307
  tags: ["speech_balloon"],
165
308
  authorization: ntfyAuth,
166
309
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thelounge-plugin-ntfy",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "description": "A plugin for The Lounge that sends push notifications via ntfy when highlighted",
5
5
  "keywords": [
6
6
  "thelounge",
package/src/config.js CHANGED
@@ -19,6 +19,7 @@ const DEFAULT_CONFIG = {
19
19
  username: null,
20
20
  password: null,
21
21
  token: null,
22
+ priority: 3,
22
23
  },
23
24
  config: {
24
25
  notify_on_private_messages: {}, // Per-network: { "network-uuid": true/false }
@@ -31,8 +32,10 @@ const GLOBAL_KEYS = new Set([
31
32
  "ntfy.username",
32
33
  "ntfy.password",
33
34
  "ntfy.token",
35
+ "ntfy.priority",
34
36
  ]);
35
37
  const GLOBAL_BOOLEAN_KEYS = new Set([]);
38
+ const GLOBAL_NUMERIC_KEYS = new Set(["ntfy.priority"]);
36
39
 
37
40
  const PER_NETWORK_KEYS = new Set(["config.notify_on_private_messages"]);
38
41
  const PER_NETWORK_BOOLEAN_KEYS = new Set(["config.notify_on_private_messages"]);
@@ -67,6 +70,13 @@ const userConfigSchema = {
67
70
  nullable: true,
68
71
  errorMessage: "Invalid ntfy token, must start with 'tk_'",
69
72
  },
73
+ priority: {
74
+ type: "integer",
75
+ minimum: 1,
76
+ maximum: 5,
77
+ default: 3,
78
+ errorMessage: "Priority must be an integer between 1 and 5",
79
+ },
70
80
  },
71
81
  allOf: [
72
82
  {
@@ -112,7 +122,7 @@ const userConfigSchema = {
112
122
  properties: {
113
123
  notify_on_private_messages: {
114
124
  type: "object",
115
- additionalProperties: { type: ["boolean", "string"] },
125
+ additionalProperties: { type: "boolean" },
116
126
  default: {},
117
127
  },
118
128
  },
@@ -261,6 +271,18 @@ function saveUserSetting(username, settingKey, settingValue) {
261
271
  }
262
272
  }
263
273
 
274
+ if (GLOBAL_NUMERIC_KEYS.has(settingKey)) {
275
+ try {
276
+ settingValue = settingValue ? Number(settingValue) : NaN;
277
+
278
+ if (isNaN(settingValue) || typeof settingValue !== "number") {
279
+ return `Invalid value for ${settingKey}, expected a number`;
280
+ }
281
+ } catch {
282
+ return `Invalid value for ${settingKey}, expected a number`;
283
+ }
284
+ }
285
+
264
286
  curr[keys[keys.length - 1]] = settingValue;
265
287
 
266
288
  const configToSave = encryptSensitiveFields(userConfig);
package/src/handler.js CHANGED
@@ -10,17 +10,27 @@ function createHandler(client, network) {
10
10
  return;
11
11
  }
12
12
 
13
+ const isPM = data.target === network.nick;
14
+
15
+ const channel = isPM
16
+ ? network.channels.find((chan) => chan.name === data.nick)
17
+ : network.channels.find((chan) => chan.name === data.target);
18
+
19
+ if (channel.muted) {
20
+ // Ignore messages in muted channels
21
+ return;
22
+ }
23
+
13
24
  const highlightRegex = new RegExp(network.highlightRegex, "i");
14
25
  const message = data.message || "";
15
26
 
16
27
  const mentioned = highlightRegex.test(message);
17
- const isPM = data.target === network.nick;
18
28
 
19
29
  let notify = false;
20
30
  let userConfig;
21
31
 
22
- if (mentioned) {
23
- // Mentions always notify
32
+ if (mentioned && !isPM) {
33
+ // Mentions always notify in channels
24
34
  notify = true;
25
35
  } else if (isPM) {
26
36
  // PMs notify only if enabled in config for this network
@@ -71,12 +81,12 @@ function createHandler(client, network) {
71
81
  };
72
82
  }
73
83
 
74
- const { NtfyClient, MessagePriority } = await import("ntfy");
84
+ const { NtfyClient } = await import("ntfy");
75
85
 
76
86
  const ntfyClient = new NtfyClient({
77
87
  server: userConfig.ntfy.server,
78
88
  topic: userConfig.ntfy.topic,
79
- priority: MessagePriority.HIGH,
89
+ priority: userConfig.ntfy.priority,
80
90
  tags: ["speech_balloon"],
81
91
  authorization: ntfyAuth,
82
92
  });