|
|
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(`π€ <b>New User Started Bot</b>\n\nUser: ${userLink}\nID: ${from.id}`); |
|
|
} |
|
|
|
|
|
await sendMessage(chatId, |
|
|
`β¨ <b>VoltEdge Bot Help</b>\n\n` + |
|
|
`I can host your media at lightning speed using our edge infrastructure.\n\n` + |
|
|
`π <b>How to Upload:</b>\n` + |
|
|
`β’ Send a <b>Photo/Video/GIF</b> directly to me.\n` + |
|
|
`β’ Send an <b>Image/Video/GIF</b> as a <b>Document</b>.\n` + |
|
|
`β’ Or <b>Reply</b> to an existing Media with /upload or /tgm.\n\n` + |
|
|
`<b>Commands:</b>\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, |
|
|
`π <b>VoltEdge Statistics</b>\n\n` + |
|
|
`π₯ <b>Total Users:</b> ${stats.totalUsers}\n` + |
|
|
`πΌοΈ <b>Images:</b> ${stats.totalImages}\n` + |
|
|
`π¬ <b>Videos/GIFs:</b> ${stats.totalVideos}\n` + |
|
|
`π€ <b>Bot Uploads:</b> ${stats.botUploads}\n` + |
|
|
`π <b>Web Uploads:</b> ${stats.webUploads}\n` + |
|
|
`πΆ <b>Ping:</b> ${stats.ping}ms`, |
|
|
'HTML' |
|
|
); |
|
|
return new NextResponse('OK'); |
|
|
} |
|
|
|
|
|
if (command === '/upload' || command === '/tgm') { |
|
|
|
|
|
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'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
await sendMessage(chatId, |
|
|
`<b>VoltEdge Upload Mode:</b>\n\n` + |
|
|
`1. Directly send a photo to this bot.\n` + |
|
|
`2. Or send an image as a "File/Document".\n` + |
|
|
`3. Or <b>reply</b> to an image with /upload.\n\n` + |
|
|
`I will instantly return a high-speed VoltEdge link!` |
|
|
); |
|
|
return new NextResponse('OK'); |
|
|
} |
|
|
|
|
|
|
|
|
if (body.message.chat.type === 'private') { |
|
|
await sendMessage(chatId, |
|
|
`β <b>I'm not sure what you mean.</b>\n\n` + |
|
|
`Just send me any <b>Photo</b> or <b>Video/GIF</b> and I will host it for you instantly! Or type /help for commands.`, |
|
|
'HTML' |
|
|
); |
|
|
} |
|
|
return new NextResponse('OK'); |
|
|
} |
|
|
|
|
|
|
|
|
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'); |
|
|
} |
|
|
|
|
|
|
|
|
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'); |
|
|
} |
|
|
|
|
|
|
|
|
if (document) { |
|
|
|
|
|
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(`β οΈ <b>Webhook Error</b>\n\nError: ${error}`); |
|
|
return new NextResponse('OK'); |
|
|
} |
|
|
} |
|
|
|
|
|
async function processFile(chatId: number, fileId: string, fileSize: number, mimeType: string, userLink: string, userId: number | string, mediaType: 'photo' | 'animation' | 'video') { |
|
|
try { |
|
|
|
|
|
const MAX_SIZE = 2 * 1024 * 1024 * 1024; |
|
|
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/'; |
|
|
|
|
|
|
|
|
const LARGE_FILE_THRESHOLD = 50 * 1024 * 1024; |
|
|
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) { |
|
|
|
|
|
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'; |
|
|
|
|
|
|
|
|
await sendMediaToChannel(fileId, `π€ <b>Uploaded by:</b> ${userLink}\nπ¦ <b>Stored in HF Hub</b>\nπ <b>HF URL:</b> ${hfResult.file_url}`, mediaType); |
|
|
} catch (hfError: any) { |
|
|
console.error('HF upload failed for bot, using Telegram:', hfError); |
|
|
|
|
|
await sendMediaToChannel(fileId, `π€ <b>Uploaded by:</b> ${userLink}`, mediaType); |
|
|
storageResult = { file_id: fileId }; |
|
|
} |
|
|
} else { |
|
|
|
|
|
await sendMediaToChannel(fileId, `π€ <b>Uploaded by:</b> ${userLink}`, mediaType); |
|
|
storageResult = { file_id: fileId }; |
|
|
} |
|
|
|
|
|
|
|
|
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, |
|
|
`β
<b>File Uploaded Successfully!</b>\n\n` + |
|
|
`π <b>Link:</b> ${publicUrl}\n` + |
|
|
(storageType === 'huggingface' ? `π¦ <b>Stored in:</b> Hugging Face Hub\n` : '') + |
|
|
`β‘ <i>Hosted on VoltEdge</i>`, |
|
|
'HTML' |
|
|
); |
|
|
|
|
|
await sendLog(`π€ <b>New Bot Upload</b>\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(`β <b>Upload Processing Error</b>\n\nUser: ${userLink}\nError: ${error}`); |
|
|
await sendMessage(chatId, "β Failed to process your image. Please try again later."); |
|
|
} |
|
|
} |
|
|
|