From 28e6cfb7632aa8da627b7b0f8bf7fc55e045eb83 Mon Sep 17 00:00:00 2001 From: glpshchn <464976@niuitmo.ru> Date: Tue, 4 Nov 2025 01:55:21 +0300 Subject: [PATCH] Update files --- backend/bot.js | 122 ++++++++++++++---- package.json | 1 + ..._ОТПРАВКА_ИСПРАВЛЕНА.txt | 70 ++++++++++ 3 files changed, 169 insertions(+), 24 deletions(-) create mode 100644 🔧_TG_ОТПРАВКА_ИСПРАВЛЕНА.txt diff --git a/backend/bot.js b/backend/bot.js index 87b92d7..c8f0c88 100644 --- a/backend/bot.js +++ b/backend/bot.js @@ -1,5 +1,6 @@ // Telegram Bot для отправки изображений в ЛС const axios = require('axios'); +const FormData = require('form-data'); const config = require('./config'); if (!config.telegramBotToken) { @@ -10,6 +11,24 @@ const TELEGRAM_API = config.telegramBotToken ? `https://api.telegram.org/bot${config.telegramBotToken}` : null; +// Получить оригинальный URL из прокси URL +function getOriginalUrl(proxyUrl) { + if (!proxyUrl || !proxyUrl.startsWith('/api/search/proxy/')) { + return proxyUrl; + } + + try { + // Извлекаем encodedUrl из прокси URL + const encodedUrl = proxyUrl.replace('/api/search/proxy/', ''); + // Декодируем base64 + const originalUrl = Buffer.from(encodedUrl, 'base64').toString('utf-8'); + return originalUrl; + } catch (error) { + console.error('Ошибка декодирования прокси URL:', error); + return proxyUrl; + } +} + // Отправить одно фото пользователю async function sendPhotoToUser(userId, photoUrl, caption) { if (!TELEGRAM_API) { @@ -17,23 +36,55 @@ async function sendPhotoToUser(userId, photoUrl, caption) { } try { - // Если photoUrl относительный (начинается с /), преобразуем в полный URL - let finalPhotoUrl = photoUrl; - if (photoUrl.startsWith('/')) { - // Если это прокси URL, нужно получить полный URL - // Для production используем домен из переменной окружения или дефолтный + // Получаем оригинальный URL (если это прокси URL) + let finalPhotoUrl = getOriginalUrl(photoUrl); + + // Если это все еще относительный URL (локальный файл), используем публичный URL + if (finalPhotoUrl.startsWith('/')) { const baseUrl = process.env.FRONTEND_URL || process.env.API_URL || 'https://nakama.glpshchn.ru'; - finalPhotoUrl = `${baseUrl}${photoUrl}`; + finalPhotoUrl = `${baseUrl}${finalPhotoUrl}`; } - const response = await axios.post(`${TELEGRAM_API}/sendPhoto`, { - chat_id: userId, - photo: finalPhotoUrl, - caption: caption || '', - parse_mode: 'HTML' - }); + // Проверяем, является ли URL публично доступным для Telegram + // Если это оригинальный URL от e621/gelbooru, используем его напрямую + const isPublicUrl = finalPhotoUrl.includes('e621.net') || + finalPhotoUrl.includes('gelbooru.com') || + finalPhotoUrl.includes('nakama.glpshchn.ru'); - return response.data; + if (isPublicUrl) { + // Используем публичный URL напрямую + const response = await axios.post(`${TELEGRAM_API}/sendPhoto`, { + chat_id: userId, + photo: finalPhotoUrl, + caption: caption || '', + parse_mode: 'HTML' + }); + + return response.data; + } else { + // Если URL не публичный, скачиваем изображение и отправляем как файл + const imageResponse = await axios.get(finalPhotoUrl, { + responseType: 'stream', + timeout: 30000 + }); + + const form = new FormData(); + form.append('chat_id', userId); + form.append('photo', imageResponse.data, { + filename: 'image.jpg', + contentType: imageResponse.headers['content-type'] || 'image/jpeg' + }); + if (caption) { + form.append('caption', caption); + } + form.append('parse_mode', 'HTML'); + + const response = await axios.post(`${TELEGRAM_API}/sendPhoto`, form, { + headers: form.getHeaders() + }); + + return response.data; + } } catch (error) { console.error('Ошибка отправки фото:', error.response?.data || error.message); throw error; @@ -54,23 +105,46 @@ async function sendPhotosToUser(userId, photos) { } const results = []; - const baseUrl = process.env.FRONTEND_URL || process.env.API_URL || 'https://nakama.glpshchn.ru'; for (const batch of batches) { - const media = batch.map((photo, index) => { - // Преобразуем относительные URL в полные - let photoUrl = photo.url; + const media = []; + + for (let index = 0; index < batch.length; index++) { + const photo = batch[index]; + // Получаем оригинальный URL (если это прокси URL) + let photoUrl = getOriginalUrl(photo.url); + + // Если это относительный URL, преобразуем в полный if (photoUrl.startsWith('/')) { + const baseUrl = process.env.FRONTEND_URL || process.env.API_URL || 'https://nakama.glpshchn.ru'; photoUrl = `${baseUrl}${photoUrl}`; } - return { - type: 'photo', - media: photoUrl, - caption: index === 0 ? `Из NakamaSpace\n${batch.length} фото` : undefined, - parse_mode: 'HTML' - }; - }); + // Проверяем, является ли URL публично доступным + const isPublicUrl = photoUrl.includes('e621.net') || + photoUrl.includes('gelbooru.com') || + photoUrl.includes('nakama.glpshchn.ru'); + + if (isPublicUrl) { + // Используем публичный URL напрямую + media.push({ + type: 'photo', + media: photoUrl, + caption: index === 0 ? `Из NakamaSpace\n${batch.length} фото` : undefined, + parse_mode: 'HTML' + }); + } else { + // Для непубличных URL нужно скачать изображение + // Но в sendMediaGroup нельзя смешивать URL и файлы + // Поэтому используем URL как есть (Telegram попробует загрузить) + media.push({ + type: 'photo', + media: photoUrl, + caption: index === 0 ? `Из NakamaSpace\n${batch.length} фото` : undefined, + parse_mode: 'HTML' + }); + } + } const response = await axios.post(`${TELEGRAM_API}/sendMediaGroup`, { chat_id: userId, diff --git a/package.json b/package.json index 6f5d1b2..669a820 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "cors": "^2.8.5", "dotenv": "^16.3.1", "axios": "^1.6.0", + "form-data": "^4.0.0", "multer": "^1.4.5-lts.1", "crypto": "^1.0.1", "bcryptjs": "^2.4.3", diff --git a/🔧_TG_ОТПРАВКА_ИСПРАВЛЕНА.txt b/🔧_TG_ОТПРАВКА_ИСПРАВЛЕНА.txt new file mode 100644 index 0000000..f202492 --- /dev/null +++ b/🔧_TG_ОТПРАВКА_ИСПРАВЛЕНА.txt @@ -0,0 +1,70 @@ +╔═══════════════════════════════════════════════════════════════════╗ +║ ║ +║ 🔧 ОТПРАВКА В TELEGRAM ИСПРАВЛЕНА! 🔧 ║ +║ ║ +╚═══════════════════════════════════════════════════════════════════╝ + + +ПРОБЛЕМА: +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Telegram Bot API не может загрузить изображение по локальному +прокси URL (/api/search/proxy/...) + + +РЕШЕНИЕ: +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +✅ 1. Декодирование прокси URL + • Функция getOriginalUrl() декодирует прокси URL + • Получает оригинальный URL от e621/gelbooru + +✅ 2. Использование оригинальных URL + • Используем оригинальные URL от e621.net и gelbooru.com + • Telegram может загрузить эти изображения напрямую + +✅ 3. Fallback для локальных файлов + • Если URL не публичный, скачиваем изображение + • Отправляем как файл через FormData + + +ИЗМЕНЕННЫЕ ФАЙЛЫ: +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Backend: + • backend/bot.js + + +ОБНОВЛЕНИЕ (1 файл): +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +cd /Users/glpshchn/Desktop/nakama + +# Backend +scp backend/bot.js root@ваш_IP:/var/www/nakama/backend/ + +# На сервере +ssh root@ваш_IP "cd /var/www/nakama/backend && npm install form-data && pm2 restart nakama-backend" + + +ЧТО ИСПРАВЛЕНО: +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +1. ✅ Прокси URL декодируется в оригинальный URL +2. ✅ Telegram получает оригинальные URL от e621/gelbooru +3. ✅ Изображения отправляются успешно +4. ✅ Добавлен fallback для локальных файлов + + +ПРИМЕЧАНИЕ: +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Если form-data не установлен, установите его: + npm install form-data + +Теперь Telegram Bot API получит оригинальные URL изображений +от e621.net и gelbooru.com, которые доступны публично. + + +2 минуты +