claude-notification-plugin 1.0.44 → 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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-notification-plugin",
3
- "version": "1.0.44",
3
+ "version": "1.0.47",
4
4
  "description": "Claude Code notifications щт task completion: Telegram, Windows toast, sound, and voice",
5
5
  "author": {
6
6
  "name": "Viacheslav Makarov",
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
@@ -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, '&lt;')
146
+ .replace(/>/g, '&gt;');
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 = 'Markdown';
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: *${project}*\nDuration: ${duration}s\nTrigger: ${eventType}`;
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 debugBlock = '\n\n*Debug:*\n'
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
- message += debugBlock;
479
- telegramMessage += debugBlock;
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.44",
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": {