container lifecycle management
This commit is contained in:
163
gateway/src/channels/telegram-handler.ts
Normal file
163
gateway/src/channels/telegram-handler.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
|
||||
import type { Authenticator } from '../auth/authenticator.js';
|
||||
import { AgentHarness } from '../harness/agent-harness.js';
|
||||
import type { InboundMessage } from '../types/messages.js';
|
||||
import { randomUUID } from 'crypto';
|
||||
|
||||
import type { ProviderConfig } from '../llm/provider.js';
|
||||
|
||||
export interface TelegramHandlerConfig {
|
||||
authenticator: Authenticator;
|
||||
providerConfig: ProviderConfig;
|
||||
telegramBotToken: string;
|
||||
}
|
||||
|
||||
interface TelegramUpdate {
|
||||
update_id: number;
|
||||
message?: {
|
||||
message_id: number;
|
||||
from: {
|
||||
id: number;
|
||||
first_name: string;
|
||||
username?: string;
|
||||
};
|
||||
chat: {
|
||||
id: number;
|
||||
type: string;
|
||||
};
|
||||
text?: string;
|
||||
photo?: Array<{
|
||||
file_id: string;
|
||||
file_size: number;
|
||||
}>;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Telegram webhook handler
|
||||
*/
|
||||
export class TelegramHandler {
|
||||
private config: TelegramHandlerConfig;
|
||||
private sessions = new Map<string, AgentHarness>();
|
||||
|
||||
constructor(config: TelegramHandlerConfig) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register Telegram webhook routes
|
||||
*/
|
||||
register(app: FastifyInstance): void {
|
||||
app.post('/webhook/telegram', async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
await this.handleWebhook(request, reply, app);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Telegram webhook
|
||||
*/
|
||||
private async handleWebhook(
|
||||
request: FastifyRequest,
|
||||
reply: FastifyReply,
|
||||
app: FastifyInstance
|
||||
): Promise<void> {
|
||||
const logger = app.log;
|
||||
|
||||
try {
|
||||
const update = request.body as TelegramUpdate;
|
||||
|
||||
if (!update.message?.text) {
|
||||
// Ignore non-text messages for now
|
||||
reply.code(200).send({ ok: true });
|
||||
return;
|
||||
}
|
||||
|
||||
const telegramUserId = update.message.from.id.toString();
|
||||
const chatId = update.message.chat.id;
|
||||
const text = update.message.text;
|
||||
|
||||
logger.info({ telegramUserId, chatId, text }, 'Received Telegram message');
|
||||
|
||||
// Authenticate
|
||||
const authContext = await this.config.authenticator.authenticateTelegram(telegramUserId);
|
||||
if (!authContext) {
|
||||
logger.warn({ telegramUserId }, 'Telegram user not authenticated');
|
||||
await this.sendTelegramMessage(
|
||||
chatId,
|
||||
'Please link your Telegram account to Dexorder first.'
|
||||
);
|
||||
reply.code(200).send({ ok: true });
|
||||
return;
|
||||
}
|
||||
|
||||
// Get or create harness
|
||||
let harness = this.sessions.get(authContext.sessionId);
|
||||
if (!harness) {
|
||||
harness = new AgentHarness({
|
||||
userId: authContext.userId,
|
||||
sessionId: authContext.sessionId,
|
||||
license: authContext.license,
|
||||
providerConfig: this.config.providerConfig,
|
||||
logger,
|
||||
});
|
||||
await harness.initialize();
|
||||
this.sessions.set(authContext.sessionId, harness);
|
||||
}
|
||||
|
||||
// Process message
|
||||
const inboundMessage: InboundMessage = {
|
||||
messageId: randomUUID(),
|
||||
userId: authContext.userId,
|
||||
sessionId: authContext.sessionId,
|
||||
content: text,
|
||||
timestamp: new Date(),
|
||||
};
|
||||
|
||||
const response = await harness.handleMessage(inboundMessage);
|
||||
|
||||
// Send response back to Telegram
|
||||
await this.sendTelegramMessage(chatId, response.content);
|
||||
|
||||
reply.code(200).send({ ok: true });
|
||||
} catch (error) {
|
||||
logger.error({ error }, 'Error handling Telegram webhook');
|
||||
reply.code(500).send({ ok: false, error: 'Internal server error' });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send message to Telegram chat
|
||||
*/
|
||||
private async sendTelegramMessage(chatId: number, text: string): Promise<void> {
|
||||
const url = `https://api.telegram.org/bot${this.config.telegramBotToken}/sendMessage`;
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
chat_id: chatId,
|
||||
text,
|
||||
parse_mode: 'Markdown',
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Telegram API error: ${response.statusText}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to send Telegram message:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup old sessions (call periodically)
|
||||
*/
|
||||
async cleanupSessions(maxAgeMs = 30 * 60 * 1000): Promise<void> {
|
||||
// TODO: Track session last activity and cleanup
|
||||
// For now, sessions persist until server restart
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user