claude-notification-plugin 1.0.6 → 1.0.10

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
@@ -32,7 +32,8 @@ Config file: `~/.claude/notifier.config.json`
32
32
  "telegram": {
33
33
  "enabled": true,
34
34
  "token": "YOUR_BOT_TOKEN",
35
- "chatId": "YOUR_CHAT_ID"
35
+ "chatId": "YOUR_CHAT_ID",
36
+ "deleteAfterHours": 24
36
37
  },
37
38
  "windowsNotification": {
38
39
  "enabled": true
@@ -50,6 +51,8 @@ Config file: `~/.claude/notifier.config.json`
50
51
 
51
52
  Each channel has an `enabled` flag (`true`/`false`) for global control.
52
53
 
54
+ `deleteAfterHours` — auto-delete old Telegram messages after the specified number of hours (default: `24`, set `0` to disable).
55
+
53
56
  Environment variables `TELEGRAM_TOKEN` and `TELEGRAM_CHAT_ID` override config file values.
54
57
 
55
58
  ### Per-channel environment variables
package/bin/install.js CHANGED
@@ -112,10 +112,22 @@ async function main () {
112
112
  fs.mkdirSync(claudeDir, { recursive: true });
113
113
 
114
114
  const config = {
115
- telegram: { enabled: true, token, chatId },
116
- windowsNotification: { enabled: true },
117
- sound: { enabled: true, file: 'C:/Windows/Media/notify.wav' },
118
- voice: { enabled: true },
115
+ telegram: {
116
+ enabled: true,
117
+ token,
118
+ chatId,
119
+ deleteAfterHours: 24,
120
+ },
121
+ windowsNotification: {
122
+ enabled: true,
123
+ },
124
+ sound: {
125
+ enabled: true,
126
+ file: 'C:/Windows/Media/notify.wav',
127
+ },
128
+ voice: {
129
+ enabled: true,
130
+ },
119
131
  minSeconds: 15,
120
132
  };
121
133
 
@@ -18,10 +18,22 @@ function loadConfig () {
18
18
  const configPath = path.join(os.homedir(), '.claude', 'notifier.config.json');
19
19
 
20
20
  const config = {
21
- telegram: { enabled: true, token: '', chatId: '' },
22
- windowsNotification: { enabled: true },
23
- sound: { enabled: true, file: 'C:/Windows/Media/notify.wav' },
24
- voice: { enabled: true },
21
+ telegram: {
22
+ enabled: true,
23
+ token: '',
24
+ chatId: '',
25
+ deleteAfterHours: 24,
26
+ },
27
+ windowsNotification: {
28
+ enabled: true,
29
+ },
30
+ sound: {
31
+ enabled: true,
32
+ file: 'C:/Windows/Media/notify.wav',
33
+ },
34
+ voice: {
35
+ enabled: true,
36
+ },
25
37
  minSeconds: 15,
26
38
  };
27
39
 
@@ -113,26 +125,62 @@ function saveState (state) {
113
125
  // TELEGRAM
114
126
  // ----------------------
115
127
 
116
- async function sendTelegram (config, text) {
128
+ async function sendTelegram (config, state) {
117
129
  if (!config.telegram.enabled || !config.telegram.token || !config.telegram.chatId) {
118
130
  return;
119
131
  }
120
132
 
121
- const url =
122
- `https://api.telegram.org/bot${config.telegram.token}/sendMessage`;
133
+ const baseUrl = `https://api.telegram.org/bot${config.telegram.token}`;
123
134
 
135
+ // Send new message and store its id
124
136
  try {
125
- await fetch(url, {
137
+ const res = await fetch(`${baseUrl}/sendMessage`, {
126
138
  method: 'POST',
127
139
  headers: { 'Content-Type': 'application/json' },
128
140
  body: JSON.stringify({
129
141
  chat_id: config.telegram.chatId,
130
- text,
142
+ text: state._telegramText,
131
143
  }),
132
144
  });
145
+ const data = await res.json();
146
+ if (data.ok && data.result?.message_id) {
147
+ if (!state.sentMessages) {
148
+ state.sentMessages = [];
149
+ }
150
+ state.sentMessages.push({
151
+ id: data.result.message_id,
152
+ ts: Date.now(),
153
+ });
154
+ }
133
155
  } catch {
134
156
  // silent fail
135
157
  }
158
+
159
+ // Delete old messages
160
+ const maxAge = (config.telegram.deleteAfterHours || 24) * 3600_000;
161
+ if (state.sentMessages?.length) {
162
+ const now = Date.now();
163
+ const keep = [];
164
+ for (const msg of state.sentMessages) {
165
+ if (now - msg.ts > maxAge) {
166
+ try {
167
+ await fetch(`${baseUrl}/deleteMessage`, {
168
+ method: 'POST',
169
+ headers: { 'Content-Type': 'application/json' },
170
+ body: JSON.stringify({
171
+ chat_id: config.telegram.chatId,
172
+ message_id: msg.id,
173
+ }),
174
+ });
175
+ } catch {
176
+ // silent fail
177
+ }
178
+ } else {
179
+ keep.push(msg);
180
+ }
181
+ }
182
+ state.sentMessages = keep;
183
+ }
136
184
  }
137
185
 
138
186
  // ----------------------
@@ -166,12 +214,30 @@ function playSound (config) {
166
214
  }
167
215
  }
168
216
 
217
+ const voicePhrases = {
218
+ en: (d) => `Claude finished coding in ${d} seconds`,
219
+ ru: (d) => `Клод завершил работу за ${d} секунд`,
220
+ de: (d) => `Claude hat die Arbeit in ${d} Sekunden abgeschlossen`,
221
+ fr: (d) => `Claude a termine en ${d} secondes`,
222
+ es: (d) => `Claude termino en ${d} segundos`,
223
+ pt: (d) => `Claude terminou em ${d} segundos`,
224
+ ja: (d) => `Claudeは${d}秒でコーディングを完了しました`,
225
+ ko: (d) => `Claude가 ${d}초 만에 코딩을 완료했습니다`,
226
+ };
227
+
228
+ function getVoicePhrase (duration) {
229
+ const locale = Intl.DateTimeFormat().resolvedOptions().locale || 'en';
230
+ const lang = locale.split('-')[0].toLowerCase();
231
+ const fn = voicePhrases[lang] || voicePhrases.en;
232
+ return fn(duration);
233
+ }
234
+
169
235
  function speakResult (config, duration) {
170
236
  if (!config.voice.enabled) {
171
237
  return;
172
238
  }
173
239
  try {
174
- say.speak(`Claude finished coding in ${duration} seconds`);
240
+ say.speak(getVoicePhrase(duration));
175
241
  } catch {
176
242
  // silent fail
177
243
  }
@@ -245,7 +311,11 @@ process.stdin.on('end', async () => {
245
311
  const message =
246
312
  `Claude finished coding\n\nProject: ${project}\nDuration: ${duration}s\nStatus: ${status}`;
247
313
 
248
- await sendTelegram(config, `\u{1F916} ${message}`);
314
+ state._telegramText = `\u{1F916} ${message}`;
315
+ await sendTelegram(config, state);
316
+ delete state._telegramText;
317
+ saveState(state);
318
+
249
319
  sendWindowsNotification(config, message);
250
320
  playSound(config);
251
321
  speakResult(config, duration);
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.6",
4
+ "version": "1.0.10",
5
5
  "description": "Telegram and Windows notifications for Claude Code task completion",
6
6
  "type": "module",
7
7
  "engines": {