Files
zCode-CLI-X/src/bot/message-sender.js

96 lines
2.7 KiB
JavaScript

// Adapted from claudegram's message-sender.ts — send with retry, chunking, fallback
import { logger } from '../utils/logger.js';
const MAX_MSG_LENGTH = 4000;
export function splitMessage(text) {
if (text.length <= MAX_MSG_LENGTH) return [text];
const chunks = [];
let remaining = text;
while (remaining.length > 0) {
chunks.push(remaining.slice(0, MAX_MSG_LENGTH));
remaining = remaining.slice(MAX_MSG_LENGTH);
}
return chunks;
}
export function escapeMarkdown(text) {
if (!text) return '';
return text
.replace(/_/g, '\\_')
.replace(/\*/g, '\\*')
.replace(/\[/g, '\\[')
.replace(/`/g, '\\`');
}
export async function sendFormatted(ctx, text) {
if (!text) return;
try {
// Try Markdown first
const chunks = splitMessage(text);
for (const chunk of chunks) {
await ctx.reply(chunk, { parse_mode: 'Markdown' });
}
} catch {
// Fallback to plain text
logger.warn('Markdown send failed, falling back to plain text');
const chunks = splitMessage(text);
for (const chunk of chunks) {
await ctx.reply(chunk, { parse_mode: undefined });
}
}
}
export async function sendStreamingMessage(ctx, text, options = {}) {
if (!text) return;
const {
delay = 100,
minDelay = 50,
maxDelay = 250,
charMode = false // Set to true for character-by-character, false for word-by-word
} = options;
try {
// Send initial "typing..." indicator
const sendTyping = () => ctx.api.sendChatAction(ctx.chat.id, 'typing').catch(() => {});
if (charMode) {
// Character-by-character streaming
let sentText = '';
const chars = text.split('');
for (const char of chars) {
sentText += char;
// Send message with the accumulated text
await ctx.reply(sentText, { parse_mode: 'Markdown' });
// Random delay between min and max
const delayMs = minDelay + Math.random() * (maxDelay - minDelay);
await new Promise(resolve => setTimeout(resolve, delayMs));
}
} else {
// Word-by-word streaming (default)
const words = text.split(' ');
let sentText = '';
for (let i = 0; i < words.length; i++) {
sentText += (i > 0 ? ' ' : '') + words[i];
// Send accumulated text
await ctx.reply(sentText, { parse_mode: 'Markdown' });
// Variable delay based on word length
const wordDelay = Math.max(minDelay, Math.min(maxDelay, delay + words[i].length * 10));
await new Promise(resolve => setTimeout(resolve, wordDelay));
}
}
} catch (error) {
logger.error('Streaming send failed:', error);
// Fallback to non-streaming
await sendFormatted(ctx, text);
}
}