import { NextRequest, NextResponse } from 'next/server';
import { TelegramUpdate, sendMessage, sendMediaToChannel, sendLog, downloadTelegramFile, getTelegramFileUrl } from '@/lib/telegram';
import { uploadToHuggingFace, isHuggingFaceConfigured } from '@/lib/huggingface';
import { saveImage, generateId, getStats, registerUser } from '@/lib/db';
export async function POST(req: NextRequest) {
try {
const body: TelegramUpdate = await req.json();
if (!body.message) {
return new NextResponse('OK');
}
const chatId = body.message.chat.id;
const text = body.message.text;
const photo = body.message.photo;
const animation = body.message.animation;
const document = body.message.document;
const replyTo = body.message.reply_to_message;
const from = body.message.from;
const userLink = from.username
? `@${from.username}`
: `${from.first_name} [${from.id}]`;
if (text) {
const command = text.split(' ')[0].split('@')[0].toLowerCase();
if (command === '/start' || command === '/help') {
if (command === '/start') {
await registerUser(from.id);
await sendLog(`š¤ New User Started Bot\n\nUser: ${userLink}\nID: ${from.id}`);
}
await sendMessage(chatId,
`⨠VoltEdge Bot Help\n\n` +
`I can host your media at lightning speed using our edge infrastructure.\n\n` +
`š How to Upload:\n` +
`⢠Send a Photo/Video/GIF directly to me.\n` +
`⢠Send an Image/Video/GIF as a Document.\n` +
`⢠Or Reply to an existing Media with /upload or /tgm.\n\n` +
`Commands:\n` +
`/stats - Show bot statistics\n` +
`/upload or /tgm - Upload a replied Media\n` +
`/help - Show this message`,
'HTML',
{
inline_keyboard: [[
{ text: "š Visit Website", url: "https://hunters.indevs.in/" }
]]
}
);
return new NextResponse('OK');
}
if (command === '/stats') {
const stats = await getStats();
await sendMessage(chatId,
`š VoltEdge Statistics\n\n` +
`š„ Total Users: ${stats.totalUsers}\n` +
`š¼ļø Images: ${stats.totalImages}\n` +
`š¬ Videos/GIFs: ${stats.totalVideos}\n` +
`š¤ Bot Uploads: ${stats.botUploads}\n` +
`š Web Uploads: ${stats.webUploads}\n` +
`š¶ Ping: ${stats.ping}ms`,
'HTML'
);
return new NextResponse('OK');
}
if (command === '/upload' || command === '/tgm') {
// Check if it's a reply to an image
if (replyTo) {
if (replyTo.photo && replyTo.photo.length > 0) {
const largestPhoto = replyTo.photo[replyTo.photo.length - 1];
await processFile(chatId, largestPhoto.file_id, largestPhoto.file_size, 'image/jpeg', userLink, from.id, 'photo');
return new NextResponse('OK');
}
if (replyTo.animation) {
await processFile(chatId, replyTo.animation.file_id, replyTo.animation.file_size, replyTo.animation.mime_type || 'image/gif', userLink, from.id, 'animation');
return new NextResponse('OK');
}
if (replyTo.document && (replyTo.document.mime_type?.startsWith('image/') || replyTo.document.mime_type?.startsWith('video/'))) {
const type = replyTo.document.mime_type?.startsWith('video/') ? 'animation' : 'photo';
await processFile(chatId, replyTo.document.file_id, replyTo.document.file_size, replyTo.document.mime_type, userLink, from.id, type);
return new NextResponse('OK');
}
}
// If not a reply, show instructions
await sendMessage(chatId,
`VoltEdge Upload Mode:\n\n` +
`1. Directly send a photo to this bot.\n` +
`2. Or send an image as a "File/Document".\n` +
`3. Or reply to an image with /upload.\n\n` +
`I will instantly return a high-speed VoltEdge link!`
);
return new NextResponse('OK');
}
// Fallback for unknown text
if (body.message.chat.type === 'private') {
await sendMessage(chatId,
`ā I'm not sure what you mean.\n\n` +
`Just send me any Photo or Video/GIF and I will host it for you instantly! Or type /help for commands.`,
'HTML'
);
}
return new NextResponse('OK');
}
// Handle Photo
if (photo && photo.length > 0) {
if (body.message.chat.type === 'private') {
const largestPhoto = photo[photo.length - 1];
await processFile(chatId, largestPhoto.file_id, largestPhoto.file_size, 'image/jpeg', userLink, from.id, 'photo');
}
return new NextResponse('OK');
}
// Handle Animation (GIF)
if (animation) {
if (body.message.chat.type === 'private') {
await processFile(chatId, animation.file_id, animation.file_size, animation.mime_type || 'image/gif', userLink, from.id, 'animation');
}
return new NextResponse('OK');
}
// Handle Document (image or video/gif)
if (document) {
// Only process direct documents in PRIVATE chats
if (body.message.chat.type === 'private') {
const mimeType = document.mime_type || '';
if (mimeType.startsWith('image/')) {
await processFile(chatId, document.file_id, document.file_size, mimeType, userLink, from.id, 'photo');
} else if (mimeType.startsWith('video/')) {
await processFile(chatId, document.file_id, document.file_size, mimeType, userLink, from.id, 'animation');
} else {
await sendMessage(chatId, "ā Please send only image or GIF files.");
}
}
return new NextResponse('OK');
}
return new NextResponse('OK');
} catch (error) {
console.error('Webhook error:', error);
await sendLog(`ā ļø Webhook Error\n\nError: ${error}`);
return new NextResponse('OK'); // Always return OK to Telegram
}
}
async function processFile(chatId: number, fileId: string, fileSize: number, mimeType: string, userLink: string, userId: number | string, mediaType: 'photo' | 'animation' | 'video') {
try {
// Enforce 2GB limit
const MAX_SIZE = 2 * 1024 * 1024 * 1024; // 2GB
if (fileSize > MAX_SIZE) {
await sendMessage(chatId, "ā File too large. Max size is 2GB.");
return;
}
const id = generateId();
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || 'https://hunters.indevs.in/';
// Determine storage based on file size
const LARGE_FILE_THRESHOLD = 50 * 1024 * 1024; // 50MB
const isLargeFile = fileSize > LARGE_FILE_THRESHOLD;
const useHFForLargeFiles = isHuggingFaceConfigured() && process.env.USE_HF_FOR_LARGE_FILES === 'true';
let storageResult: { file_id: string; file_url?: string };
let storageType: 'telegram' | 'huggingface' = 'telegram';
if (useHFForLargeFiles && isLargeFile) {
// For large files, download from Telegram and upload to HF Hub
try {
const fileBlob = await downloadTelegramFile(fileId);
const fileName = `bot-upload-${id}`;
const hfResult = await uploadToHuggingFace(fileBlob, fileName, id);
storageResult = {
file_id: hfResult.file_id,
file_url: hfResult.file_url
};
storageType = 'huggingface';
// Still forward to Telegram chat for backup/notification
await sendMediaToChannel(fileId, `š¤ Uploaded by: ${userLink}\nš¦ Stored in HF Hub\nš HF URL: ${hfResult.file_url}`, mediaType);
} catch (hfError: any) {
console.error('HF upload failed for bot, using Telegram:', hfError);
// Fallback to Telegram
await sendMediaToChannel(fileId, `š¤ Uploaded by: ${userLink}`, mediaType);
storageResult = { file_id: fileId };
}
} else {
// Use Telegram for small files
await sendMediaToChannel(fileId, `š¤ Uploaded by: ${userLink}`, mediaType);
storageResult = { file_id: fileId };
}
// Save to DB
const record: any = {
id,
telegram_file_id: storageResult.file_id,
storage_type: storageType,
storage_url: storageResult.file_url,
created_at: Date.now(),
metadata: {
size: fileSize,
type: mimeType
}
};
await saveImage(record, 'bot', userId);
const publicUrl = `${baseUrl}/i/${id}`;
await sendMessage(chatId,
`ā
File Uploaded Successfully!\n\n` +
`š Link: ${publicUrl}\n` +
(storageType === 'huggingface' ? `š¦ Stored in: Hugging Face Hub\n` : '') +
`ā” Hosted on VoltEdge`,
'HTML'
);
await sendLog(`š¤ New Bot Upload\n\nUser: ${userLink}\nType: ${mimeType}\nSize: ${(fileSize / 1024 / 1024).toFixed(2)} MB\nStorage: ${storageType === 'huggingface' ? 'HF Hub' : 'Telegram'}\nLink: ${publicUrl}`);
} catch (error) {
console.error('Processing error:', error);
await sendLog(`ā Upload Processing Error\n\nUser: ${userLink}\nError: ${error}`);
await sendMessage(chatId, "ā Failed to process your image. Please try again later.");
}
}