forked from admin/zeropost-engine
fix: VK photo upload — 2-step getWallUploadServer + saveWallPhoto
До: wall.post без attachments → картинка игнорировалась
После:
1. photos.getWallUploadServer → upload_url
2. POST upload_url с файлом (local path или download) → server/photo/hash
3. photos.saveWallPhoto → owner_id + photo_id
4. wall.post с attachments=photo{owner_id}_{id}
При ошибке загрузки фото — публикуем без картинки (graceful degradation)
Поддерживает как локальные /uploads/ файлы так и внешние URL
This commit is contained in:
@@ -127,7 +127,13 @@ async function publishToVK({ channel, text, photoUrl, article }) {
|
||||
if (!channel.vk_group_id || !channel.vk_access_token) {
|
||||
throw new Error('VK не настроен');
|
||||
}
|
||||
// VK не поддерживает кнопки в постах — добавляем ссылку в конец текста, если её там ещё нет
|
||||
|
||||
const groupId = String(channel.vk_group_id).replace(/^-/, '');
|
||||
const ownerId = '-' + groupId;
|
||||
const token = channel.vk_access_token;
|
||||
const v = '5.199';
|
||||
|
||||
// Добавляем ссылку на статью в конец текста
|
||||
let finalText = text;
|
||||
if (article) {
|
||||
const url = articleUrl(article);
|
||||
@@ -136,15 +142,69 @@ async function publishToVK({ channel, text, photoUrl, article }) {
|
||||
finalText = `${finalText}\n\n${buttonText}\n${url}`;
|
||||
}
|
||||
}
|
||||
|
||||
// 2-step upload картинки
|
||||
let attachments = '';
|
||||
if (photoUrl) {
|
||||
try {
|
||||
// Шаг 1: получаем upload URL
|
||||
const uploadServerRes = await axios.get('https://api.vk.com/method/photos.getWallUploadServer', {
|
||||
params: { group_id: groupId, access_token: token, v },
|
||||
timeout: 10_000,
|
||||
});
|
||||
if (uploadServerRes.data?.error) throw new Error(`VK getWallUploadServer: ${uploadServerRes.data.error.error_msg}`);
|
||||
const uploadUrl = uploadServerRes.data.response.upload_url;
|
||||
|
||||
// Шаг 2: загружаем файл
|
||||
const localPath = resolveLocalPhoto(photoUrl);
|
||||
let fileBuffer, fileName;
|
||||
if (localPath) {
|
||||
fileBuffer = fs.readFileSync(localPath);
|
||||
fileName = path.basename(localPath);
|
||||
} else {
|
||||
// Внешний URL — скачиваем
|
||||
const dl = await axios.get(photoUrl, { responseType: 'arraybuffer', timeout: 30_000 });
|
||||
fileBuffer = Buffer.from(dl.data);
|
||||
fileName = 'photo.jpg';
|
||||
}
|
||||
|
||||
const form = new FormData();
|
||||
form.append('photo', fileBuffer, { filename: fileName, contentType: 'image/jpeg' });
|
||||
const uploadRes = await axios.post(uploadUrl, form, {
|
||||
headers: form.getHeaders(),
|
||||
timeout: 60_000,
|
||||
});
|
||||
const { server, photo: photoData, hash } = uploadRes.data;
|
||||
if (!photoData) throw new Error('VK upload: пустой ответ');
|
||||
|
||||
// Шаг 3: сохраняем фото
|
||||
const saveRes = await axios.get('https://api.vk.com/method/photos.saveWallPhoto', {
|
||||
params: { group_id: groupId, server, photo: photoData, hash, access_token: token, v },
|
||||
timeout: 10_000,
|
||||
});
|
||||
if (saveRes.data?.error) throw new Error(`VK saveWallPhoto: ${saveRes.data.error.error_msg}`);
|
||||
const saved = saveRes.data.response?.[0];
|
||||
if (!saved) throw new Error('VK saveWallPhoto: нет данных');
|
||||
|
||||
attachments = `photo${saved.owner_id}_${saved.id}`;
|
||||
} catch (photoErr) {
|
||||
console.warn(`[VK] photo upload failed, posting without photo: ${photoErr.message}`);
|
||||
// Публикуем без картинки если загрузка упала
|
||||
}
|
||||
}
|
||||
|
||||
// Шаг 4: публикуем пост
|
||||
const params = new URLSearchParams({
|
||||
owner_id: '-' + String(channel.vk_group_id).replace(/^-/, ''),
|
||||
owner_id: ownerId,
|
||||
from_group: '1',
|
||||
message: finalText,
|
||||
access_token: channel.vk_access_token,
|
||||
v: '5.199',
|
||||
access_token: token,
|
||||
v,
|
||||
});
|
||||
const res = await axios.post('https://api.vk.com/method/wall.post', params, { timeout: 15000 });
|
||||
if (res.data?.error) throw new Error(`VK: ${res.data.error.error_msg}`);
|
||||
if (attachments) params.set('attachments', attachments);
|
||||
|
||||
const res = await axios.post('https://api.vk.com/method/wall.post', params, { timeout: 15_000 });
|
||||
if (res.data?.error) throw new Error(`VK wall.post: ${res.data.error.error_msg}`);
|
||||
return res.data?.response?.post_id;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user