claude-notification-plugin 1.0.43 → 1.0.47
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/.claude-plugin/plugin.json +1 -1
- package/README.md +4 -0
- package/notifier/notifier.js +80 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,8 +22,12 @@ Add the marketplace and install:
|
|
|
22
22
|
```shell
|
|
23
23
|
/plugin marketplace add Bazilio-san/claude-plugins
|
|
24
24
|
/plugin install claude-notification-plugin@bazilio-plugins
|
|
25
|
+
/reload-plugins
|
|
26
|
+
/claude-notification-plugin:setup
|
|
25
27
|
```
|
|
26
28
|
|
|
29
|
+
For a detailed visual walkthrough, see [step-by-step installation guide with screenshots](INSTALL_MARKETPLACE_AND_PLUGIN.md).
|
|
30
|
+
|
|
27
31
|
Or load directly for testing:
|
|
28
32
|
|
|
29
33
|
```bash
|
package/notifier/notifier.js
CHANGED
|
@@ -139,6 +139,57 @@ function saveState (state) {
|
|
|
139
139
|
// TELEGRAM
|
|
140
140
|
// ----------------------
|
|
141
141
|
|
|
142
|
+
function escapeHtml (text) {
|
|
143
|
+
return text
|
|
144
|
+
.replace(/&/g, '&')
|
|
145
|
+
.replace(/</g, '<')
|
|
146
|
+
.replace(/>/g, '>');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function markdownToTelegramHtml (md) {
|
|
150
|
+
const codeBlocks = [];
|
|
151
|
+
const inlineCodes = [];
|
|
152
|
+
|
|
153
|
+
// Extract fenced code blocks
|
|
154
|
+
let result = md.replace(/```[\s\S]*?```/g, (m) => {
|
|
155
|
+
const idx = codeBlocks.length;
|
|
156
|
+
const inner = m.replace(/^```\w*\n?/, '').replace(/\n?```$/, '');
|
|
157
|
+
codeBlocks.push(`<pre>${escapeHtml(inner)}</pre>`);
|
|
158
|
+
return `\x00CB${idx}\x00`;
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Extract inline code
|
|
162
|
+
result = result.replace(/`([^`]+)`/g, (_, code) => {
|
|
163
|
+
const idx = inlineCodes.length;
|
|
164
|
+
inlineCodes.push(`<code>${escapeHtml(code)}</code>`);
|
|
165
|
+
return `\x00IC${idx}\x00`;
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Escape HTML in remaining text
|
|
169
|
+
result = escapeHtml(result);
|
|
170
|
+
|
|
171
|
+
// Headers → bold
|
|
172
|
+
result = result.replace(/^#{1,6}\s+(.+)$/gm, '<b>$1</b>');
|
|
173
|
+
|
|
174
|
+
// Bold: **text**
|
|
175
|
+
result = result.replace(/\*\*(.+?)\*\*/g, '<b>$1</b>');
|
|
176
|
+
|
|
177
|
+
// Italic: *text* (but not inside words with asterisks)
|
|
178
|
+
result = result.replace(/(?<!\w)\*([^*]+?)\*(?!\w)/g, '<i>$1</i>');
|
|
179
|
+
|
|
180
|
+
// Strikethrough: ~~text~~
|
|
181
|
+
result = result.replace(/~~(.+?)~~/g, '<s>$1</s>');
|
|
182
|
+
|
|
183
|
+
// Links: [text](url)
|
|
184
|
+
result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>');
|
|
185
|
+
|
|
186
|
+
// Restore code blocks and inline codes
|
|
187
|
+
result = result.replace(/\x00CB(\d+)\x00/g, (_, i) => codeBlocks[i]);
|
|
188
|
+
result = result.replace(/\x00IC(\d+)\x00/g, (_, i) => inlineCodes[i]);
|
|
189
|
+
|
|
190
|
+
return result;
|
|
191
|
+
}
|
|
192
|
+
|
|
142
193
|
async function sendTelegram (config, state) {
|
|
143
194
|
if (!config.telegram.enabled || !config.telegram.token || !config.telegram.chatId) {
|
|
144
195
|
return;
|
|
@@ -152,7 +203,7 @@ async function sendTelegram (config, state) {
|
|
|
152
203
|
chat_id: config.telegram.chatId,
|
|
153
204
|
text: state._telegramText,
|
|
154
205
|
};
|
|
155
|
-
body.parse_mode = '
|
|
206
|
+
body.parse_mode = 'HTML';
|
|
156
207
|
const res = await fetch(`${baseUrl}/sendMessage`, {
|
|
157
208
|
method: 'POST',
|
|
158
209
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -167,6 +218,26 @@ async function sendTelegram (config, state) {
|
|
|
167
218
|
id: data.result.message_id,
|
|
168
219
|
ts: Date.now(),
|
|
169
220
|
});
|
|
221
|
+
} else if (!data.ok && body.parse_mode) {
|
|
222
|
+
// Retry without formatting if HTML parsing failed
|
|
223
|
+
const retryRes = await fetch(`${baseUrl}/sendMessage`, {
|
|
224
|
+
method: 'POST',
|
|
225
|
+
headers: { 'Content-Type': 'application/json' },
|
|
226
|
+
body: JSON.stringify({
|
|
227
|
+
chat_id: body.chat_id,
|
|
228
|
+
text: body.text,
|
|
229
|
+
}),
|
|
230
|
+
});
|
|
231
|
+
const retryData = await retryRes.json();
|
|
232
|
+
if (retryData.ok && retryData.result?.message_id) {
|
|
233
|
+
if (!state.sentMessages) {
|
|
234
|
+
state.sentMessages = [];
|
|
235
|
+
}
|
|
236
|
+
state.sentMessages.push({
|
|
237
|
+
id: retryData.result.message_id,
|
|
238
|
+
ts: Date.now(),
|
|
239
|
+
});
|
|
240
|
+
}
|
|
170
241
|
}
|
|
171
242
|
} catch {
|
|
172
243
|
// silent fail
|
|
@@ -460,7 +531,7 @@ process.stdin.on('end', async () => {
|
|
|
460
531
|
`${title}\n\nProject: ${project}\nDuration: ${duration}s\nTrigger: ${eventType}`;
|
|
461
532
|
|
|
462
533
|
let telegramMessage =
|
|
463
|
-
`${title}\n\nProject:
|
|
534
|
+
`${escapeHtml(title)}\n\nProject: <b>${escapeHtml(project)}</b>\nDuration: ${duration}s\nTrigger: ${eventType}`;
|
|
464
535
|
|
|
465
536
|
if (config.telegram.includeLastCcMessageInTelegram && event.last_assistant_message) {
|
|
466
537
|
const maxLen = 3500;
|
|
@@ -468,15 +539,18 @@ process.stdin.on('end', async () => {
|
|
|
468
539
|
if (lastMsg.length > maxLen) {
|
|
469
540
|
lastMsg = lastMsg.slice(0, maxLen) + '…';
|
|
470
541
|
}
|
|
471
|
-
telegramMessage += `\n\n${lastMsg}`;
|
|
542
|
+
telegramMessage += `\n\n${markdownToTelegramHtml(lastMsg)}`;
|
|
472
543
|
}
|
|
473
544
|
|
|
474
545
|
if (config.debug) {
|
|
475
|
-
const
|
|
546
|
+
const debugBlockMd = '\n\n*Debug:*\n'
|
|
476
547
|
+ (config.voice.enabled ? `\nVoice: ${getVoicePhrase(duration)}` : '')
|
|
477
548
|
+ `\n\nHook input:\n\`\`\`json\n${JSON.stringify(event, null, 2)}\n\`\`\``;
|
|
478
|
-
|
|
479
|
-
|
|
549
|
+
const debugBlockHtml = '\n\n<b>Debug:</b>\n'
|
|
550
|
+
+ (config.voice.enabled ? `\nVoice: ${escapeHtml(getVoicePhrase(duration))}` : '')
|
|
551
|
+
+ `\n\nHook input:\n<pre>${escapeHtml(JSON.stringify(event, null, 2))}</pre>`;
|
|
552
|
+
message += debugBlockMd;
|
|
553
|
+
telegramMessage += debugBlockHtml;
|
|
480
554
|
}
|
|
481
555
|
|
|
482
556
|
state._telegramText = `\u{1F916} ${telegramMessage}`;
|
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.47",
|
|
5
5
|
"description": "Claude Code notifications щт task completion: Telegram, Windows toast, sound, and voice",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|