sahel-api / whatsapp-bot /telegram.js
Mohamed-20's picture
Deploy backend to HF Spaces
1337ed3
Raw
History Blame Contribute Delete
4.95 kB
require('dotenv').config();
const { writeFileSync, existsSync, readFileSync } = require('fs');
const path = require('path');
const axios = require('axios').default;
const API_BASE = process.env.API_BASE || 'http://localhost:8000';
const DEFAULT_SLUG = process.env.DEFAULT_BUSINESS_SLUG || 'sahel';
const BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN;
const DEV = process.env.NODE_ENV !== 'production';
if (!BOT_TOKEN) {
console.error('❌ TELEGRAM_BOT_TOKEN not set in .env');
process.exit(1);
}
const API = `https://api.telegram.org/bot${BOT_TOKEN}`;
const TIMEOUT = 30;
const POLL_INTERVAL = 1500;
const userBusinessSlugs = {};
const userSessions = {};
let lastUpdateId = 0;
let running = true;
function loadUserData() {
const file = path.join(__dirname, 'users.json');
if (existsSync(file)) {
try {
const data = JSON.parse(readFileSync(file, 'utf-8'));
Object.assign(userBusinessSlugs, data);
} catch (e) { console.error('Erreur chargement utilisateurs:', e); }
}
}
function saveUserData() {
const file = path.join(__dirname, 'users.json');
try {
writeFileSync(file, JSON.stringify(userBusinessSlugs, null, 2));
} catch (e) { console.error('Erreur sauvegarde utilisateurs:', e); }
}
loadUserData();
async function apiCall(method, payload = {}) {
try {
const res = await axios.post(`${API}/${method}`, payload, { timeout: 10000 });
return res.data;
} catch (err) {
if (err.response) {
console.error(`❌ Telegram API error [${method}]:`, err.response.data.description);
}
return null;
}
}
async function sendMessage(chatId, text, opts = {}) {
return apiCall('sendMessage', { chat_id: chatId, text, ...opts });
}
const menuText = `🤖 *Sahel.ai — AI Assistant*
Commands:
/business <slug> — Choose a business
/menu or /start — Show this menu
*To get started:*
1. Send /business <slug> (e.g., /business sahel)
2. Ask your questions directly
💡 Get your business slug from the dashboard.`;
async function handleUpdate(update) {
const msg = update.message;
if (!msg || !msg.text) return;
const chatId = msg.chat.id;
const sender = String(msg.from.id);
const text = msg.text.trim();
const lower = text.toLowerCase();
if (lower === '/start' || lower === '/menu') {
return sendMessage(chatId, menuText, { parse_mode: 'Markdown' });
}
if (lower.startsWith('/business')) {
const parts = text.split(/\s+/);
if (parts.length < 2) {
return sendMessage(chatId, 'Usage: /business <slug>\n\nExample: /business sahel');
}
const slug = parts[1].toLowerCase();
try {
const res = await axios.get(`${API_BASE}/businesses/${slug}`, { timeout: 10000 });
userBusinessSlugs[sender] = slug;
saveUserData();
return sendMessage(chatId, `✅ Connected to *${res.data.name || slug}*.\n\nSend a message to chat with the AI assistant.`, { parse_mode: 'Markdown' });
} catch (e) {
console.error('Erreur vérification commerce:', e);
return sendMessage(chatId, `❌ Business "${slug}" not found. Check the slug.`);
}
}
const slug = userBusinessSlugs[sender] || DEFAULT_SLUG;
if (!userSessions[sender]) {
userSessions[sender] = { conversation_id: null };
}
await apiCall('sendChatAction', { chat_id: chatId, action: 'typing' });
try {
const res = await axios.post(`${API_BASE}/businesses/${slug}/chat`, {
question: text,
conversation_id: userSessions[sender].conversation_id
}, {
headers: { 'Content-Type': 'application/json' },
timeout: 30000
});
if (res.status === 200) {
userSessions[sender].conversation_id = res.data.conversation_id;
const answer = res.data.answer || "Sorry, I don't have an answer.";
await sendMessage(chatId, answer);
}
} catch (err) {
const detail = err.response?.data?.detail || err.message;
await sendMessage(chatId, `⚠️ Error: ${detail}`);
}
}
async function poll() {
while (running) {
try {
const res = await axios.get(`${API}/getUpdates`, {
params: { offset: lastUpdateId + 1, timeout: TIMEOUT },
timeout: (TIMEOUT + 5) * 1000
});
if (res.data.ok && res.data.result.length > 0) {
for (const update of res.data.result) {
if (update.update_id >= lastUpdateId) {
lastUpdateId = update.update_id;
await handleUpdate(update);
}
}
}
} catch (err) {
if (err.code === 'ECONNABORTED') continue;
console.error('⚠️ Poll error:', err.message);
await new Promise(r => setTimeout(r, 3000));
}
}
}
console.log('[telegram] ✅ Telegram bot started!');
console.log('[telegram] 🤖 @Sahel_ai_bot');
console.log('[telegram] 🌐 API:', API_BASE);
console.log('[telegram] 🏪 Default slug:', DEFAULT_SLUG, '\n');
poll();
process.on('SIGINT', () => {
running = false;
console.log('[telegram] 👋 Stopping bot...');
process.exit(0);
});