claude-notification-plugin 1.0.24 → 1.0.27
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/notifier/notifier.js +125 -10
- package/package.json +2 -3
package/notifier/notifier.js
CHANGED
|
@@ -5,11 +5,8 @@ import os from 'os';
|
|
|
5
5
|
import path from 'path';
|
|
6
6
|
import process from 'process';
|
|
7
7
|
import notifier from 'node-notifier';
|
|
8
|
-
import player from 'play-sound';
|
|
9
8
|
import { spawn } from 'child_process';
|
|
10
9
|
|
|
11
|
-
const audio = player({});
|
|
12
|
-
|
|
13
10
|
// ----------------------
|
|
14
11
|
// CONFIG
|
|
15
12
|
// ----------------------
|
|
@@ -222,19 +219,137 @@ function playSound (config) {
|
|
|
222
219
|
return;
|
|
223
220
|
}
|
|
224
221
|
try {
|
|
225
|
-
|
|
222
|
+
const file = config.sound.file.replace(/'/g, "''");
|
|
223
|
+
const psCommand = `(New-Object Media.SoundPlayer '${file}').PlaySync()`;
|
|
224
|
+
spawn('powershell', ['-Command', psCommand], {
|
|
225
|
+
stdio: 'ignore',
|
|
226
|
+
windowsHide: true,
|
|
227
|
+
});
|
|
226
228
|
} catch {
|
|
227
229
|
// silent fail
|
|
228
230
|
}
|
|
229
231
|
}
|
|
230
232
|
|
|
233
|
+
function pluralize (n, forms) {
|
|
234
|
+
// forms: [one, few, many] e.g. ['секунда', 'секунды', 'секунд']
|
|
235
|
+
if (forms.length === 1) {
|
|
236
|
+
return forms[0];
|
|
237
|
+
}
|
|
238
|
+
if (forms.length === 2) {
|
|
239
|
+
return n === 1 ? forms[0] : forms[1];
|
|
240
|
+
}
|
|
241
|
+
// Slavic pluralization (ru, uk, pl, etc.)
|
|
242
|
+
const abs = Math.abs(n);
|
|
243
|
+
const mod10 = abs % 10;
|
|
244
|
+
const mod100 = abs % 100;
|
|
245
|
+
if (mod10 === 1 && mod100 !== 11) {
|
|
246
|
+
return forms[0];
|
|
247
|
+
}
|
|
248
|
+
if (mod10 >= 2 && mod10 <= 4 && (mod100 < 10 || mod100 >= 20)) {
|
|
249
|
+
return forms[1];
|
|
250
|
+
}
|
|
251
|
+
return forms[2];
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// ----------------------
|
|
255
|
+
// NUMBER TO WORDS
|
|
256
|
+
// ----------------------
|
|
257
|
+
|
|
258
|
+
const numWordsEn = {
|
|
259
|
+
ones: ['', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine',
|
|
260
|
+
'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen',
|
|
261
|
+
'seventeen', 'eighteen', 'nineteen'],
|
|
262
|
+
tens: ['', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety'],
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
function numberToWordsEn (n) {
|
|
266
|
+
if (n === 0) {
|
|
267
|
+
return 'zero';
|
|
268
|
+
}
|
|
269
|
+
const parts = [];
|
|
270
|
+
if (n >= 1000) {
|
|
271
|
+
parts.push(numWordsEn.ones[Math.floor(n / 1000)] + ' thousand');
|
|
272
|
+
n %= 1000;
|
|
273
|
+
}
|
|
274
|
+
if (n >= 100) {
|
|
275
|
+
parts.push(numWordsEn.ones[Math.floor(n / 100)] + ' hundred');
|
|
276
|
+
n %= 100;
|
|
277
|
+
}
|
|
278
|
+
if (n >= 20) {
|
|
279
|
+
const t = numWordsEn.tens[Math.floor(n / 10)];
|
|
280
|
+
const o = numWordsEn.ones[n % 10];
|
|
281
|
+
parts.push(o ? `${t}-${o}` : t);
|
|
282
|
+
} else if (n > 0) {
|
|
283
|
+
parts.push(numWordsEn.ones[n]);
|
|
284
|
+
}
|
|
285
|
+
return parts.join(' ');
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Russian: feminine accusative for "секунду" (одну, две)
|
|
289
|
+
const numWordsRu = {
|
|
290
|
+
ones: ['', 'одну', 'две', 'три', 'четыре', 'пять', 'шесть', 'семь', 'восемь', 'девять',
|
|
291
|
+
'десять', 'одиннадцать', 'двенадцать', 'тринадцать', 'четырнадцать', 'пятнадцать',
|
|
292
|
+
'шестнадцать', 'семнадцать', 'восемнадцать', 'девятнадцать'],
|
|
293
|
+
tens: ['', '', 'двадцать', 'тридцать', 'сорок', 'пятьдесят', 'шестьдесят',
|
|
294
|
+
'семьдесят', 'восемьдесят', 'девяносто'],
|
|
295
|
+
hundreds: ['', 'сто', 'двести', 'триста', 'четыреста', 'пятьсот', 'шестьсот',
|
|
296
|
+
'семьсот', 'восемьсот', 'девятьсот'],
|
|
297
|
+
thousands: ['тысяча', 'тысячи', 'тысяч'],
|
|
298
|
+
thousandOnes: ['', 'одна', 'две', 'три', 'четыре', 'пять', 'шесть', 'семь', 'восемь', 'девять'],
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
function numberToWordsRu (n) {
|
|
302
|
+
if (n === 0) {
|
|
303
|
+
return 'ноль';
|
|
304
|
+
}
|
|
305
|
+
const parts = [];
|
|
306
|
+
if (n >= 1000) {
|
|
307
|
+
const th = Math.floor(n / 1000);
|
|
308
|
+
if (th >= 100) {
|
|
309
|
+
parts.push(numWordsRu.hundreds[Math.floor(th / 100)]);
|
|
310
|
+
}
|
|
311
|
+
let thRem = th % 100;
|
|
312
|
+
if (thRem >= 20) {
|
|
313
|
+
parts.push(numWordsRu.tens[Math.floor(thRem / 10)]);
|
|
314
|
+
thRem %= 10;
|
|
315
|
+
}
|
|
316
|
+
if (thRem > 0 && thRem < 20) {
|
|
317
|
+
parts.push(thRem < 10 ? numWordsRu.thousandOnes[thRem] : numWordsRu.ones[thRem]);
|
|
318
|
+
}
|
|
319
|
+
parts.push(pluralize(th, numWordsRu.thousands));
|
|
320
|
+
n %= 1000;
|
|
321
|
+
}
|
|
322
|
+
if (n >= 100) {
|
|
323
|
+
parts.push(numWordsRu.hundreds[Math.floor(n / 100)]);
|
|
324
|
+
n %= 100;
|
|
325
|
+
}
|
|
326
|
+
if (n >= 20) {
|
|
327
|
+
parts.push(numWordsRu.tens[Math.floor(n / 10)]);
|
|
328
|
+
n %= 10;
|
|
329
|
+
}
|
|
330
|
+
if (n > 0) {
|
|
331
|
+
parts.push(numWordsRu.ones[n]);
|
|
332
|
+
}
|
|
333
|
+
return parts.join(' ');
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function numberToWords (n, lang) {
|
|
337
|
+
if (lang === 'ru') {
|
|
338
|
+
return numberToWordsRu(n);
|
|
339
|
+
}
|
|
340
|
+
if (lang === 'en') {
|
|
341
|
+
return numberToWordsEn(n);
|
|
342
|
+
}
|
|
343
|
+
return String(n);
|
|
344
|
+
}
|
|
345
|
+
|
|
231
346
|
const voicePhrases = {
|
|
232
|
-
en: (d) => `Claude finished coding in ${d} seconds`,
|
|
233
|
-
ru: (d) => `Клод завершил работу за ${d}
|
|
234
|
-
de: (d) => `Claude hat die Arbeit in ${d} Sekunden abgeschlossen`,
|
|
235
|
-
fr: (d) => `Claude a termine en ${d} secondes`,
|
|
236
|
-
es: (d) => `Claude termino en ${d} segundos`,
|
|
237
|
-
pt: (d) => `Claude terminou em ${d} segundos`,
|
|
347
|
+
en: (d) => `Claude finished coding in ${numberToWords(d, 'en')} ${pluralize(d, ['second', 'seconds'])}`,
|
|
348
|
+
ru: (d) => `Клод завершил работу за ${numberToWords(d, 'ru')} ${pluralize(d, ['секунду', 'секунды', 'секунд'])}`,
|
|
349
|
+
de: (d) => `Claude hat die Arbeit in ${d} ${pluralize(d, ['Sekunde', 'Sekunden'])} abgeschlossen`,
|
|
350
|
+
fr: (d) => `Claude a termine en ${d} ${pluralize(d, ['seconde', 'secondes'])}`,
|
|
351
|
+
es: (d) => `Claude termino en ${d} ${pluralize(d, ['segundo', 'segundos'])}`,
|
|
352
|
+
pt: (d) => `Claude terminou em ${d} ${pluralize(d, ['segundo', 'segundos'])}`,
|
|
238
353
|
ja: (d) => `Claudeは${d}秒でコーディングを完了しました`,
|
|
239
354
|
ko: (d) => `Claude가 ${d}초 만에 코딩을 완료했습니다`,
|
|
240
355
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-notification-plugin",
|
|
3
3
|
"productName": "claude-notification-plugin",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.27",
|
|
5
5
|
"description": "Telegram and Windows notifications for Claude Code task completion",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
|
@@ -43,8 +43,7 @@
|
|
|
43
43
|
"access": "public"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"node-notifier": "^10.0.1"
|
|
47
|
-
"play-sound": "^1.1.6"
|
|
46
|
+
"node-notifier": "^10.0.1"
|
|
48
47
|
},
|
|
49
48
|
"devDependencies": {
|
|
50
49
|
"eslint-plugin-import": "^2.31.0"
|