fixed telegram bug on not displaying things

This commit is contained in:
lsamc 2025-09-04 21:24:03 +08:00
parent f608aed3c2
commit 28d280d37a
3 changed files with 353 additions and 45 deletions

View File

@ -78,6 +78,105 @@ class TelegramChannel extends NotificationChannel {
} }
} }
/**
* Escape special characters for Telegram Markdown
* @param {string} text - Text to escape
* @returns {string} - Escaped text
*/
_escapeMarkdown(text) {
if (!text) return '';
// Minimal escaping to avoid message rejection
// Over-escaping causes Telegram to reject the message
return text
.replace(/\*/g, '\\*') // Escape asterisks
.replace(/_/g, '\\_') // Escape underscores
.replace(/\[/g, '\\[') // Escape square brackets
.replace(/\]/g, '\\]') // Escape square brackets
.replace(/`/g, '\\`'); // Escape backticks
}
/**
* Create a safe plain text version without markdown formatting
* @param {string} text - Text to make safe
* @returns {string} - Safe text
*/
_createSafeText(text) {
if (!text) return '';
// Remove problematic characters entirely to ensure message sends
return text
.replace(/[_*\[\]()~`>#+=|{}.!\\-]/g, '') // Remove special chars
.replace(/\s+/g, ' ') // Collapse multiple spaces
.trim();
}
/**
* Calculate message length including formatting
* @param {string} message - Message to calculate
* @returns {number} - Message length
*/
_calculateMessageLength(message) {
return message.length;
}
/**
* Split long text into chunks that fit Telegram limits
* @param {string} text - Text to split
* @param {number} maxLength - Maximum length per chunk
* @returns {string[]} - Array of text chunks
*/
_splitTextIntoChunks(text, maxLength = 3000) {
if (text.length <= maxLength) {
return [text];
}
const chunks = [];
let currentChunk = '';
const lines = text.split('\n');
for (const line of lines) {
// If adding this line would exceed the limit
if (currentChunk.length + line.length + 1 > maxLength) {
if (currentChunk) {
chunks.push(currentChunk.trim());
currentChunk = '';
}
// If single line is too long, split it by words
if (line.length > maxLength) {
const words = line.split(' ');
let wordChunk = '';
for (const word of words) {
if (wordChunk.length + word.length + 1 > maxLength) {
if (wordChunk) {
chunks.push(wordChunk.trim());
wordChunk = word;
} else {
// Single word is too long, truncate it
chunks.push(word.substring(0, maxLength - 3) + '...');
}
} else {
wordChunk += (wordChunk ? ' ' : '') + word;
}
}
if (wordChunk) {
currentChunk = wordChunk;
}
} else {
currentChunk = line;
}
} else {
currentChunk += (currentChunk ? '\n' : '') + line;
}
}
if (currentChunk) {
chunks.push(currentChunk.trim());
}
return chunks;
}
async _getBotUsername() { async _getBotUsername() {
if (this.botUsername) { if (this.botUsername) {
return this.botUsername; return this.botUsername;
@ -156,16 +255,100 @@ class TelegramChannel extends NotificationChannel {
}; };
try { try {
// Log the message details for debugging
console.log(`[DEBUG] =====================================================`);
console.log(`[DEBUG] Sending Telegram message, length: ${messageText.length}`);
console.log(`[DEBUG] Chat ID: ${chatId}`);
console.log(`[DEBUG] Session ID: ${sessionId}`);
console.log(`[DEBUG] Message preview:`, messageText.substring(0, 200) + '...');
console.log(`[DEBUG] =====================================================`);
const response = await axios.post( const response = await axios.post(
`${this.apiBaseUrl}/bot${this.config.botToken}/sendMessage`, `${this.apiBaseUrl}/bot${this.config.botToken}/sendMessage`,
requestData, requestData,
this._getNetworkOptions() {
...this._getNetworkOptions(),
timeout: 15000 // 15 second timeout
}
); );
console.log(`[DEBUG] ✅ Telegram message sent successfully!`);
this.logger.info(`Telegram message sent successfully, Session: ${sessionId}`); this.logger.info(`Telegram message sent successfully, Session: ${sessionId}`);
return true; return true;
} catch (error) { } catch (error) {
this.logger.error('Failed to send Telegram message:', error.response?.data || error.message); // Enhanced error logging
const errorData = error.response?.data;
const errorMessage = errorData?.description || error.message;
const errorCode = errorData?.error_code;
console.log(`[DEBUG] ❌ Telegram send error occurred:`);
console.log(`[DEBUG] Error Code: ${errorCode}`);
console.log(`[DEBUG] Error Message: ${errorMessage}`);
console.log(`[DEBUG] Full error response:`, JSON.stringify(errorData, null, 2));
console.log(`[DEBUG] Original message length: ${messageText.length}`);
this.logger.error(`Failed to send Telegram message (${errorCode}): ${errorMessage}`);
// Try multiple fallback strategies
console.log(`[DEBUG] Attempting fallback strategies...`);
// Strategy 1: Try without parse_mode (plain text)
try {
console.log(`[DEBUG] Trying Strategy 1: Plain text without markdown`);
const plainTextMessage = this._generateMinimalMessage(notification, token,
notification.type === 'completed' ? '✅' : '⏳',
notification.type === 'completed' ? 'Completed' : 'Waiting');
await axios.post(
`${this.apiBaseUrl}/bot${this.config.botToken}/sendMessage`,
{
chat_id: chatId,
text: plainTextMessage,
reply_markup: {
inline_keyboard: buttons
}
},
{
...this._getNetworkOptions(),
timeout: 15000
}
);
console.log(`[DEBUG] ✅ Strategy 1 succeeded: Plain text message sent`);
this.logger.info(`Telegram plain text fallback message sent successfully, Session: ${sessionId}`);
return true;
} catch (fallbackError1) {
console.log(`[DEBUG] ❌ Strategy 1 failed:`, fallbackError1.response?.data?.description || fallbackError1.message);
// Strategy 2: Try absolute minimal message without buttons
try {
console.log(`[DEBUG] Trying Strategy 2: Minimal message without buttons`);
const minimalMessage = `Claude Task Ready\\nToken: ${token}`;
await axios.post(
`${this.apiBaseUrl}/bot${this.config.botToken}/sendMessage`,
{
chat_id: chatId,
text: minimalMessage
},
{
...this._getNetworkOptions(),
timeout: 15000
}
);
console.log(`[DEBUG] ✅ Strategy 2 succeeded: Minimal message sent`);
this.logger.info(`Telegram minimal fallback message sent successfully, Session: ${sessionId}`);
return true;
} catch (fallbackError2) {
console.log(`[DEBUG] ❌ Strategy 2 failed:`, fallbackError2.response?.data?.description || fallbackError2.message);
console.log(`[DEBUG] ❌ All fallback strategies failed`);
this.logger.error('All Telegram fallback strategies failed');
}
}
// Clean up failed session // Clean up failed session
await this._removeSession(sessionId); await this._removeSession(sessionId);
return false; return false;
@ -177,35 +360,92 @@ class TelegramChannel extends NotificationChannel {
const emoji = type === 'completed' ? '✅' : '⏳'; const emoji = type === 'completed' ? '✅' : '⏳';
const status = type === 'completed' ? 'Completed' : 'Waiting for Input'; const status = type === 'completed' ? 'Completed' : 'Waiting for Input';
try {
// Method 1: Try with minimal markdown formatting
let messageText = this._generateFormattedMessage(notification, token, emoji, status);
if (messageText.length <= 4000) {
console.log(`[DEBUG] Generated formatted message length: ${messageText.length}`);
return messageText;
}
// Method 2: If too long, try plain text version
console.log(`[DEBUG] Formatted message too long (${messageText.length}), trying plain text`);
messageText = this._generatePlainTextMessage(notification, token, emoji, status);
if (messageText.length <= 4000) {
console.log(`[DEBUG] Generated plain text message length: ${messageText.length}`);
return messageText;
}
// Method 3: If still too long, use minimal fallback
console.log(`[DEBUG] Plain text still too long (${messageText.length}), using minimal fallback`);
return this._generateMinimalMessage(notification, token, emoji, status);
} catch (error) {
console.log(`[DEBUG] Error generating message: ${error.message}, using safe fallback`);
return this._generateMinimalMessage(notification, token, emoji, status);
}
}
_generateFormattedMessage(notification, token, emoji, status) {
let messageText = `${emoji} *Claude Task ${status}*\n`; let messageText = `${emoji} *Claude Task ${status}*\n`;
messageText += `*Project:* ${notification.project}\n`; messageText += `*Project:* ${notification.project || 'Unknown'}\n`;
messageText += `*Session Token:* \`${token}\`\n\n`; messageText += `*Token:* \`${token}\`\n\n`;
if (notification.metadata) { // Add user question if available (limited length)
if (notification.metadata.userQuestion) { if (notification.metadata?.userQuestion) {
messageText += `📝 *Your Question:*\n${notification.metadata.userQuestion.substring(0, 200)}`; const question = notification.metadata.userQuestion.substring(0, 150);
if (notification.metadata.userQuestion.length > 200) { messageText += `📝 *Question:* ${this._escapeMarkdown(question)}${question.length < notification.metadata.userQuestion.length ? '...' : ''}\n\n`;
messageText += '...';
}
messageText += '\n\n';
}
if (notification.metadata.claudeResponse) {
messageText += `🤖 *Claude Response:*\n${notification.metadata.claudeResponse.substring(0, 300)}`;
if (notification.metadata.claudeResponse.length > 300) {
messageText += '...';
}
messageText += '\n\n';
}
} }
messageText += `💬 *To send a new command:*\n`; // Add Claude response if available (limited length)
messageText += `Reply with: \`/cmd ${token} <your command>\`\n`; if (notification.metadata?.claudeResponse) {
messageText += `Example: \`/cmd ${token} Please analyze this code\``; const maxResponseLength = 3000 - messageText.length - 200; // Reserve space for instructions
let response = notification.metadata.claudeResponse;
if (response.length > maxResponseLength) {
response = response.substring(0, maxResponseLength - 10) + '...';
}
messageText += `🤖 *Response:*\n${this._escapeMarkdown(response)}\n\n`;
}
messageText += `💬 Use: \`/cmd ${token} <command>\``;
return messageText; return messageText;
} }
_generatePlainTextMessage(notification, token, emoji, status) {
let messageText = `${emoji} Claude Task ${status}\n`;
messageText += `Project: ${notification.project || 'Unknown'}\n`;
messageText += `Token: ${token}\n\n`;
// Add user question (plain text, limited)
if (notification.metadata?.userQuestion) {
const question = this._createSafeText(notification.metadata.userQuestion.substring(0, 150));
messageText += `Question: ${question}${question.length < notification.metadata.userQuestion.length ? '...' : ''}\n\n`;
}
// Add Claude response (plain text, limited)
if (notification.metadata?.claudeResponse) {
const maxResponseLength = 3000 - messageText.length - 100;
let response = this._createSafeText(notification.metadata.claudeResponse);
if (response.length > maxResponseLength) {
response = response.substring(0, maxResponseLength - 10) + '...';
}
messageText += `Response: ${response}\n\n`;
}
messageText += `Use: /cmd ${token} <command>`;
return messageText;
}
_generateMinimalMessage(notification, token, emoji, status) {
return `${emoji} Claude Task ${status}\nToken: ${token}\nUse: /cmd ${token} <command>`;
}
async _createSession(sessionId, notification, token) { async _createSession(sessionId, notification, token) {
const session = { const session = {
id: sessionId, id: sessionId,

View File

@ -817,5 +817,95 @@
"sessionId": "dbd1f477-0550-463b-b147-fcdf78720177", "sessionId": "dbd1f477-0550-463b-b147-fcdf78720177",
"tmuxSession": "claude-session", "tmuxSession": "claude-session",
"description": "completed - ecllipse" "description": "completed - ecllipse"
},
"IS1F9FN9": {
"type": "pty",
"createdAt": 1756991169,
"expiresAt": 1757077569,
"cwd": "/home/lsamc/.local/src/Claude-Code-Remote",
"sessionId": "2fa9255d-eb9a-4b37-8852-8204d5730d42",
"tmuxSession": "claude-session",
"description": "completed - Claude-Code-Remote"
},
"CJRIHOH9": {
"type": "pty",
"createdAt": 1756991395,
"expiresAt": 1757077795,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "c5d96613-00de-459c-a8f0-52f020d135fc",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"19318YCF": {
"type": "pty",
"createdAt": 1756991584,
"expiresAt": 1757077984,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "ffba11b1-0aaf-4639-ba4f-6db27249ae22",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"KTP2NLKA": {
"type": "pty",
"createdAt": 1756991689,
"expiresAt": 1757078089,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "c4b49e6a-c4ca-446a-b28c-09863478c9fc",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"3HB36PXA": {
"type": "pty",
"createdAt": 1756991822,
"expiresAt": 1757078222,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "9a14ee0e-cf2d-44f5-bec8-548853fc97e1",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"WLR6CVMN": {
"type": "pty",
"createdAt": 1756991860,
"expiresAt": 1757078260,
"cwd": "/home/lsamc/.local/src/Claude-Code-Remote",
"sessionId": "22cd07f1-34c2-4f58-b470-d7e7d235a92b",
"tmuxSession": "claude-session",
"description": "completed - Claude-Code-Remote"
},
"634ZIJ55": {
"type": "pty",
"createdAt": 1756991894,
"expiresAt": 1757078294,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "7016be36-709a-48c5-8f64-2589aae4e5e9",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"HG4Z82VG": {
"type": "pty",
"createdAt": 1756991984,
"expiresAt": 1757078384,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "c42a911c-2b47-4b41-8280-c62889ab2384",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"OIO9DPTN": {
"type": "pty",
"createdAt": 1756992035,
"expiresAt": 1757078435,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "e29d64f2-19f5-4e5b-92c8-6bd1b766bc0d",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"PWB2DEYR": {
"type": "pty",
"createdAt": 1756992167,
"expiresAt": 1757078567,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "197cd53a-4a64-42a9-8262-8e3b429f08b8",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
} }
} }

View File

@ -1,22 +0,0 @@
#!/usr/bin/env node
/**
* Test script for extractConversation function fix
* Tests the improved response detection logic
*/
const TmuxMonitor = require('./src/utils/tmux-monitor');
// Create test tmux buffer content that mimics actual Claude Code output
const testBuffer1 = `
Welcome to Claude Code
? for shortcuts
> what does this project do?
I'll help you understand what this project does. Let me analyze the codebase structure first.
<function_calls>
<invoke name="Read">
<parameter name="file_path">/path/to/file.js