GoldScrape_Api / src /telegramBot.js
Aurum
Initialize clean codebase without binary files
dcc4f27
Raw
History Blame Contribute Delete
13.4 kB
/**
* GoldRate Engine โ€” Telegram Bot for API Key Management
*
* Private admin bot to create, list, revoke, and check API keys.
* Only authorized admin Telegram IDs can use this bot.
*
* Commands:
* /start โ€” Welcome message
* /create_sln โ€” Create SLN (Chennai) API key
* /create_ibja โ€” Create IBJA (India) API key
* /list โ€” List all active keys
* /revoke โ€” Revoke a key
* /check โ€” Check key status
* /help โ€” Show all commands
*
* Setup:
* 1. Create a bot via @BotFather on Telegram
* 2. Add TELEGRAM_BOT_TOKEN to .env
* 3. Add your Telegram user ID to TELEGRAM_ADMIN_IDS in .env
* 4. Start: node src/telegramBot.js (or auto-starts with server)
*/
require('dotenv').config();
const TelegramBotModule = require('node-telegram-bot-api');
const TelegramBot = TelegramBotModule.default || TelegramBotModule;
const crypto = require('crypto');
const { createClient } = require('@supabase/supabase-js');
// โ”€โ”€ Config โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
const BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN;
const ADMIN_IDS = (process.env.TELEGRAM_ADMIN_IDS || '')
.split(',')
.map(id => id.trim())
.filter(Boolean);
let supabase = null;
let bot = null;
// โ”€โ”€ Initialize โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
function initTelegramBot() {
if (!BOT_TOKEN) {
console.log('โ„น๏ธ TELEGRAM_BOT_TOKEN not set โ€” Telegram bot disabled');
return null;
}
const supabaseUrl = process.env.SUPABASE_URL;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
if (!supabaseUrl || !supabaseKey) {
console.log('โš ๏ธ Supabase not configured โ€” Telegram bot disabled');
return null;
}
supabase = createClient(supabaseUrl, supabaseKey);
bot = new TelegramBot(BOT_TOKEN, { polling: true });
// Global error handler โ€” prevent crashes
bot.on('polling_error', (err) => {
console.warn('โš ๏ธ Telegram polling error:', err.message);
});
bot.on('error', (err) => {
console.warn('โš ๏ธ Telegram error:', err.message);
});
// Register handlers
bot.onText(/\/start/, safeHandler(handleStart));
bot.onText(/\/help/, safeHandler(handleHelp));
bot.onText(/\/create_sln(.*)/, safeHandler(handleCreateSLN));
bot.onText(/\/create_ibja(.*)/, safeHandler(handleCreateIBJA));
bot.onText(/\/list/, safeHandler(handleList));
bot.onText(/\/revoke(.*)/, safeHandler(handleRevoke));
bot.onText(/\/check(.*)/, safeHandler(handleCheck));
console.log('๐Ÿค– Telegram Bot started! Waiting for commands...');
if (ADMIN_IDS.length > 0) {
console.log(`๐Ÿ”’ Authorized admins: ${ADMIN_IDS.join(', ')}`);
} else {
console.log('โš ๏ธ No TELEGRAM_ADMIN_IDS set โ€” bot will tell you your ID on /start');
}
return bot;
}
// โ”€โ”€ Safe handler wrapper (prevents crashes) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
function safeHandler(fn) {
return async (msg, match) => {
try {
await fn(msg, match);
} catch (err) {
console.error('โš ๏ธ Telegram handler error:', err.message);
try {
await bot.sendMessage(msg.chat.id, `โŒ Error: ${err.message}`);
} catch (e) {
console.error('โš ๏ธ Could not send error message:', e.message);
}
}
};
}
// โ”€โ”€ Auth Check โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
function isAdmin(chatId) {
if (ADMIN_IDS.length === 0) return true;
return ADMIN_IDS.includes(chatId.toString());
}
function generateApiKey(source) {
const prefix = source.toLowerCase();
const random = crypto.randomBytes(16).toString('hex');
return `${prefix}_${random}`;
}
// โ”€โ”€ HTML helper โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
function esc(text) {
return String(text).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
// โ”€โ”€ /start โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
async function handleStart(msg) {
const chatId = msg.chat.id;
const name = esc(msg.from.first_name || 'Admin');
const adminStatus = isAdmin(chatId)
? 'โœ… You are authorized as admin'
: 'โŒ Not authorized. Add your ID to TELEGRAM_ADMIN_IDS in .env';
const text =
`๐Ÿช™ <b>GoldRate Engine โ€” API Key Bot</b>
Welcome ${name}! ๐Ÿ‘‹
Your Telegram ID: <code>${chatId}</code>
${adminStatus}
<b>Available Commands:</b>
/create_sln <code>Owner Name</code> โ€” Chennai (SLN) key
/create_ibja <code>Owner Name</code> โ€” India (IBJA) key
/list โ€” List all API keys
/check <code>api_key</code> โ€” Check key details
/revoke <code>api_key</code> โ€” Deactivate a key
/help โ€” Show this message`;
await bot.sendMessage(chatId, text, { parse_mode: 'HTML' });
}
// โ”€โ”€ /help โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
async function handleHelp(msg) {
return handleStart(msg);
}
// โ”€โ”€ /create_sln โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
async function handleCreateSLN(msg, match) {
const chatId = msg.chat.id;
if (!isAdmin(chatId)) {
return bot.sendMessage(chatId, 'โŒ Unauthorized. Contact admin.');
}
const args = (match[1] || '').trim();
if (!args) {
return bot.sendMessage(chatId,
'โš ๏ธ Usage: /create_sln <code>Owner Name [Days]</code>\n\nExample: /create_sln Gold Shop ABC 90',
{ parse_mode: 'HTML' });
}
// Parse name and optional days (e.g. "Shop Name 90" -> "Shop Name" and 90 days)
const parts = args.split(/\s+/);
let days = 30;
let ownerName = args;
if (parts.length > 1) {
const lastPart = parts[parts.length - 1];
if (/^\d+$/.test(lastPart)) {
days = parseInt(lastPart);
ownerName = parts.slice(0, -1).join(' ');
}
}
await createKey(chatId, ownerName, 'SLN', days);
}
// โ”€โ”€ /create_ibja โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
async function handleCreateIBJA(msg, match) {
const chatId = msg.chat.id;
if (!isAdmin(chatId)) {
return bot.sendMessage(chatId, 'โŒ Unauthorized. Contact admin.');
}
const args = (match[1] || '').trim();
if (!args) {
return bot.sendMessage(chatId,
'โš ๏ธ Usage: /create_ibja <code>Owner Name [Days]</code>\n\nExample: /create_ibja Jeweller XYZ 120',
{ parse_mode: 'HTML' });
}
// Parse name and optional days
const parts = args.split(/\s+/);
let days = 30;
let ownerName = args;
if (parts.length > 1) {
const lastPart = parts[parts.length - 1];
if (/^\d+$/.test(lastPart)) {
days = parseInt(lastPart);
ownerName = parts.slice(0, -1).join(' ');
}
}
await createKey(chatId, ownerName, 'IBJA', days);
}
// โ”€โ”€ Create Key (shared) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
async function createKey(chatId, ownerName, source, days = 30) {
const apiKey = generateApiKey(source);
const expiresAt = new Date();
expiresAt.setDate(expiresAt.getDate() + days);
const { data, error } = await supabase
.from('api_keys')
.insert({
api_key: apiKey,
owner_name: ownerName,
source: source,
expires_at: expiresAt.toISOString(),
is_active: true,
notes: `Created via Telegram by ${chatId}`
})
.select()
.single();
if (error) {
return bot.sendMessage(chatId,
`โŒ Failed to create key: ${esc(error.message)}\n\n๐Ÿ’ก Did you create the <code>api_keys</code> table in Supabase?`,
{ parse_mode: 'HTML' });
}
const expiryStr = expiresAt.toLocaleDateString('en-IN', {
day: '2-digit', month: 'short', year: 'numeric'
});
const sourceName = source === 'SLN' ? 'SLN Bullion Chennai' : 'IBJA India';
const text =
`โœ… <b>API Key Created!</b>
๐Ÿ”‘ <b>Key:</b>
<code>${apiKey}</code>
๐Ÿ‘ค <b>Owner:</b> ${esc(ownerName)}
๐Ÿ“ <b>Source:</b> ${sourceName}
๐Ÿ“… <b>Valid Until:</b> ${expiryStr}
โฑ <b>Duration:</b> ${days} days
Copy the key above and share with the user.`;
await bot.sendMessage(chatId, text, { parse_mode: 'HTML' });
}
// โ”€โ”€ /list โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
async function handleList(msg) {
const chatId = msg.chat.id;
if (!isAdmin(chatId)) {
return bot.sendMessage(chatId, 'โŒ Unauthorized.');
}
const { data, error } = await supabase
.from('api_keys')
.select('*')
.order('created_at', { ascending: false })
.limit(20);
if (error) {
return bot.sendMessage(chatId, `โŒ Error: ${esc(error.message)}`);
}
if (!data || data.length === 0) {
return bot.sendMessage(chatId,
'๐Ÿ“‹ No API keys found.\n\nCreate one with /create_sln or /create_ibja');
}
const now = new Date();
let text = `๐Ÿ“‹ <b>API Keys (${data.length})</b>\n\n`;
for (const key of data) {
const expired = new Date(key.expires_at) < now;
const status = !key.is_active ? 'โŒ Revoked' : expired ? 'โฐ Expired' : 'โœ… Active';
const daysLeft = Math.max(0, Math.ceil((new Date(key.expires_at) - now) / (1000 * 60 * 60 * 24)));
const expiryStr = new Date(key.expires_at).toLocaleDateString('en-IN', { day: '2-digit', month: 'short' });
text += `${status} <b>${esc(key.owner_name)}</b> (${key.source})\n`;
text += `<code>${key.api_key.substring(0, 20)}...</code>\n`;
text += `Expires: ${expiryStr}${!expired && key.is_active ? ` (${daysLeft}d left)` : ''}\n\n`;
}
text += 'Use /check <code>KEY</code> to see full details';
await bot.sendMessage(chatId, text, { parse_mode: 'HTML' });
}
// โ”€โ”€ /revoke โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
async function handleRevoke(msg, match) {
const chatId = msg.chat.id;
if (!isAdmin(chatId)) {
return bot.sendMessage(chatId, 'โŒ Unauthorized.');
}
const apiKey = (match[1] || '').trim();
if (!apiKey) {
return bot.sendMessage(chatId, 'โš ๏ธ Usage: /revoke <code>api_key_here</code>', { parse_mode: 'HTML' });
}
const { data, error } = await supabase
.from('api_keys')
.update({ is_active: false })
.eq('api_key', apiKey)
.select()
.single();
if (error || !data) {
return bot.sendMessage(chatId, `โŒ Key not found: <code>${esc(apiKey)}</code>`, { parse_mode: 'HTML' });
}
await bot.sendMessage(chatId,
`โœ… <b>Key Revoked</b>\n\n๐Ÿ‘ค Owner: ${esc(data.owner_name)}\n๐Ÿ“ Source: ${data.source}\n๐Ÿ”‘ Key: <code>${esc(apiKey)}</code>`,
{ parse_mode: 'HTML' });
}
// โ”€โ”€ /check โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
async function handleCheck(msg, match) {
const chatId = msg.chat.id;
if (!isAdmin(chatId)) {
return bot.sendMessage(chatId, 'โŒ Unauthorized.');
}
const apiKey = (match[1] || '').trim();
if (!apiKey) {
return bot.sendMessage(chatId, 'โš ๏ธ Usage: /check <code>api_key_here</code>', { parse_mode: 'HTML' });
}
const { data, error } = await supabase
.from('api_keys')
.select('*')
.eq('api_key', apiKey)
.single();
if (error || !data) {
return bot.sendMessage(chatId, `โŒ Key not found: <code>${esc(apiKey)}</code>`, { parse_mode: 'HTML' });
}
const now = new Date();
const expired = new Date(data.expires_at) < now;
const daysLeft = Math.max(0, Math.ceil((new Date(data.expires_at) - now) / (1000 * 60 * 60 * 24)));
const status = !data.is_active ? 'โŒ Revoked' : expired ? 'โฐ Expired' : 'โœ… Active';
const createdStr = new Date(data.created_at).toLocaleDateString('en-IN', { day: '2-digit', month: 'short', year: 'numeric' });
const expiryStr = new Date(data.expires_at).toLocaleDateString('en-IN', { day: '2-digit', month: 'short', year: 'numeric' });
const sourceName = data.source === 'SLN' ? '๐Ÿ“ SLN Bullion Chennai' : '๐Ÿ‡ฎ๐Ÿ‡ณ IBJA India';
const daysInfo = (!expired && data.is_active) ? `\n<b>Days Left:</b> ${daysLeft}` : '';
const text =
`๐Ÿ”‘ <b>API Key Details</b>
<b>Key:</b> <code>${data.api_key}</code>
<b>Owner:</b> ${esc(data.owner_name)}
<b>Phone:</b> ${data.owner_phone || 'N/A'}
<b>Email:</b> ${data.owner_email || 'N/A'}
<b>Source:</b> ${sourceName}
<b>Status:</b> ${status}
<b>Created:</b> ${createdStr}
<b>Expires:</b> ${expiryStr}${daysInfo}`;
await bot.sendMessage(chatId, text, { parse_mode: 'HTML' });
}
// โ”€โ”€ Export โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
module.exports = { initTelegramBot };
// โ”€โ”€ Run standalone โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
if (require.main === module) {
console.log('๐Ÿค– Starting Telegram Bot standalone...');
const botInstance = initTelegramBot();
if (!botInstance) {
console.error('โŒ Bot could not start. Check TELEGRAM_BOT_TOKEN in .env');
process.exit(1);
}
}