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 +4 -1
- package/bin/install.js +16 -4
- package/notifier/notifier.js +81 -11
- package/package.json +1 -1
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: {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
|
package/notifier/notifier.js
CHANGED
|
@@ -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: {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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,
|
|
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
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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