godcrm/backend/services/agent-tools/telegram-tools.js
GOD CRM Release f89e074dd1
Some checks failed
CI / Lint / Typecheck / Test / Build (push) Has been cancelled
CI / PostgreSQL Integration Tests (push) Has been cancelled
GOD CRM — public scrubbed snapshot
Governed substrate for autonomous agents: scoped identity (passports),
audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
2026-08-10 04:01:45 +03:00

92 lines
3.7 KiB
JavaScript

/**
* Telegram Tool Handlers
*
* Handles: send_telegram_message
* Wraps TelegramService functions for agent/MCP use.
*/
import { sendChannelPost, sendChannelPostEN, sendToTopic, sendGroupMessage, getTopicMap, sendMessage } from '../TelegramService.js';
import { CLUB_CHAT_ID, getClubTopicThread } from '../../routes/v3/telegram/clubRouter.js';
import { aiLogger } from '../../utils/logger.js';
/**
* Telegram tool handlers
*/
export const telegramToolHandlers = {
/**
* Send a message to a Telegram destination (channel, topic, or group).
*/
async send_telegram_message({ destination, text, parse_mode = 'Markdown' }) {
if (!destination) return { error: 'destination is required (e.g. "channel", "group", or a topic key like "news", "notifications")' };
if (!text) return { error: 'text is required' };
const options = {};
if (parse_mode) options.parse_mode = parse_mode;
try {
// ── Anti-Marketing Club forum topic (e.g. "club_bluesky") ──────────
// Drips into a calibrated topic in the club staging group. The thread_id
// is learned by clubRouter calibration, not hardcoded. Sent as PLAIN text
// (no parse_mode) — these dead-pan posts carry stray _ * ` that would
// crash Telegram's Markdown parser, matching the kitchen/voice precedent.
if (destination.startsWith('club_')) {
const key = destination.slice('club_'.length);
const threadId = await getClubTopicThread(key);
if (threadId == null) {
return { error: `Club topic "${key}" is not calibrated yet. Post the marker word "${key}" once into that topic so the router learns its thread_id, then retry.` };
}
const sent = await sendMessage(CLUB_CHAT_ID, text, { message_thread_id: threadId });
if (!sent.success) return { error: sent.error || 'Failed to send to club topic' };
return { success: true, message_id: sent.messageId, destination, text_length: text.length };
}
let result;
if (destination === 'channel') {
// Post to @godcrm channel (RU)
result = await sendChannelPost(text, options);
} else if (destination === 'channel_en') {
// Post to @god_crm channel (EN)
result = await sendChannelPostEN(text, options);
} else if (destination === 'group') {
// Post to group general topic
result = await sendGroupMessage(text, options);
} else {
// Treat as a topic key
const topics = getTopicMap();
if (!topics[destination]) {
const available = Object.keys(topics).join(', ');
return { error: `Unknown topic "${destination}". Available: ${available}` };
}
result = await sendToTopic(destination, text, options);
}
if (!result.success) {
// Retry without parse_mode if Markdown fails
if (result.error && parse_mode) {
aiLogger.warn({ destination, error: result.error }, 'Telegram: Markdown failed, retrying plain');
result = destination === 'channel'
? await sendChannelPost(text, {})
: destination === 'channel_en'
? await sendChannelPostEN(text, {})
: destination === 'group'
? await sendGroupMessage(text, {})
: await sendToTopic(destination, text, {});
}
if (!result.success) {
return { error: result.error || 'Failed to send message' };
}
}
return {
success: true,
message_id: result.messageId,
destination,
text_length: text.length
};
} catch (err) {
aiLogger.error({ err, destination }, 'Telegram send_telegram_message error');
return { error: err.message };
}
}
};