Governed substrate for autonomous agents: scoped identity (passports), audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
123 lines
3.8 KiB
JavaScript
123 lines
3.8 KiB
JavaScript
// backend/routes/v3/telegram/channelOps.js
|
|
// Channel posting API: post, stats, schedule
|
|
|
|
import { apiLogger, sendChannelPost, sendChannelPhoto, getChannelMemberCount, dbRun, isPostgres } from './shared.js';
|
|
|
|
/**
|
|
* Register channel operation routes on the given router.
|
|
*/
|
|
export default function registerChannelOps(router) {
|
|
/**
|
|
* POST /api/v3/telegram/channel/post
|
|
* Post content to the @godcrm Telegram channel.
|
|
* Body: { text, photo_url?, disable_preview?, reply_markup? }
|
|
* Returns: { success, message_id }
|
|
*/
|
|
router.post('/channel/post', async (req, res) => {
|
|
try {
|
|
const { text, photo_url, disable_preview, reply_markup } = req.body;
|
|
if (!text && !photo_url) {
|
|
return res.status(400).json({ success: false, error: 'Either text or photo_url is required' });
|
|
}
|
|
|
|
const options = {};
|
|
if (disable_preview) options.disable_web_page_preview = true;
|
|
if (reply_markup) options.reply_markup = reply_markup;
|
|
|
|
let result;
|
|
if (photo_url) {
|
|
result = await sendChannelPhoto(photo_url, text || '', options);
|
|
} else {
|
|
result = await sendChannelPost(text, options);
|
|
}
|
|
|
|
if (!result.success) {
|
|
return res.status(500).json({ success: false, error: result.error });
|
|
}
|
|
|
|
res.json({ success: true, message_id: result.messageId });
|
|
} catch (err) {
|
|
apiLogger.error({ err }, '[Telegram] Channel post error');
|
|
res.status(500).json({ success: false, error: err.message });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* GET /api/v3/telegram/channel/stats
|
|
* Get channel statistics (member count, etc.)
|
|
*/
|
|
router.get('/channel/stats', async (req, res) => {
|
|
try {
|
|
const memberCount = await getChannelMemberCount();
|
|
|
|
res.json({
|
|
success: true,
|
|
channel: '@godcrm',
|
|
channel_url: 'https://t.me/godcrm',
|
|
members: memberCount.success ? memberCount.count : null,
|
|
});
|
|
} catch (err) {
|
|
res.status(500).json({ success: false, error: err.message });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* POST /api/v3/telegram/channel/schedule
|
|
* Schedule a post for later publishing (stores in content queue).
|
|
* Body: { text, photo_url?, publish_at }
|
|
* For now: stores to DB, a cron job will pick up and publish.
|
|
*/
|
|
router.post('/channel/schedule', async (req, res) => {
|
|
try {
|
|
const { text, photo_url, publish_at } = req.body;
|
|
|
|
if (!text) {
|
|
return res.status(400).json({ success: false, error: 'text is required' });
|
|
}
|
|
|
|
if (!publish_at) {
|
|
return res.status(400).json({ success: false, error: 'publish_at is required (ISO 8601)' });
|
|
}
|
|
|
|
// Store scheduled post in content pipeline table (2603) or a dedicated scheduled posts table
|
|
const rowData = JSON.stringify({
|
|
title: text.substring(0, 100),
|
|
body: text,
|
|
photo_url: photo_url || null,
|
|
status: 'scheduled',
|
|
platform: 'telegram',
|
|
publish_at: publish_at,
|
|
content_type: 'post',
|
|
created_at: new Date().toISOString(),
|
|
});
|
|
|
|
let result;
|
|
if (isPostgres()) {
|
|
result = await dbRun(
|
|
`INSERT INTO table_rows (table_id, data, created_at, updated_at)
|
|
VALUES (2603, $1::jsonb, NOW(), NOW())
|
|
RETURNING id`,
|
|
[rowData]
|
|
);
|
|
} else {
|
|
result = await dbRun(
|
|
`INSERT INTO table_rows (table_id, data, created_at, updated_at)
|
|
VALUES (2603, ?, datetime('now'), datetime('now'))`,
|
|
[rowData]
|
|
);
|
|
}
|
|
|
|
const rowId = result?.rows?.[0]?.id || result?.lastInsertRowid;
|
|
|
|
res.json({
|
|
success: true,
|
|
row_id: rowId,
|
|
publish_at,
|
|
status: 'scheduled',
|
|
});
|
|
} catch (err) {
|
|
apiLogger.error({ err }, '[Telegram] Channel schedule error');
|
|
res.status(500).json({ success: false, error: err.message });
|
|
}
|
|
});
|
|
}
|