Governed substrate for autonomous agents: scoped identity (passports), audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
92 lines
3.7 KiB
JavaScript
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 };
|
|
}
|
|
}
|
|
};
|