Имя: Пароль:
1C
 
Отправка файла отчёта в MAX
0 yurikmellon2
 
03.04.26
10:43
Всем добрый день.
Коллеги, если есть, поделитесь рабочим кодом отправки файла в MAX, прости господи.
1 shuhard
 
03.04.26
10:46
2 yurikmellon2
 
03.04.26
10:49
(1) я так понимаю, что оба решения через GREEN-API. Так я умею. Напрямую через MAX API надо
3 mmg
 
03.04.26
10:54
4 yurikmellon2
 
03.04.26
11:00
(3) вот как раз по этой документации и пытаюсь сделать. Ссылку на загрузку получаю.
Дальше надо загрузить файл.
curl -X POST \
  -H "Content-Type: multipart/form-data" \
  -F "data=@movie.mp4" \
  "https://vu.mycdn.me/upload.do?sig={signature}&expires={timestamp}"

Что такое signature, что туда передавать?
5 Garykom
 
гуру
03.04.26
11:22
(4) случаем это не ЭЦП (точнее отпечаток-сигнатура файла) по ГОСТ через КриптоПРО?
https://xn----7sbmxacaqnu.xn--p1ai/news/kak_sozdat_sig_fajl_s%20pomoshchyu_kriptopro/
6 reg0303
 
03.04.26
11:24
(4) Эти параметры уже должны быть в ссылке на загрузку
7 Garykom
 
гуру
03.04.26
11:26
(6) дык это же загрузка файла видео в CDN от ВК
требуют указать криптохэш и время жизни файла видео
8 Garykom
 
гуру
03.04.26
11:27
(7)+ в ответ на загрузку вернется ссылка на видео, которую можно вставить в сообщение
9 picom
 
07.04.26
12:08
(4) Удалось направить файл и получить Токен вложения?
(8) Непонятно как его туда нормально загнать, пример бы

Это не работает
ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ПутьКФайлу);
Файлы = Новый Массив;
Файлы.Добавить(Новый Структура("Имя, Данные, ИмяФайла", "data", ДвоичныеДанныеФайла, "1.txt"));
Результат = Коннектор.Post(ПутьДляЗагрузкиФайла, , Новый Структура("Файлы", Файлы));
10 picom
 
07.04.26
13:34
Есть подозрение что нужно использовать Сжатие GZip
Т.к. ответ приходит уже сжатый, а первые значения 1F 8B
11 yurikmellon2
 
13.04.26
09:50
Забыл отписаться. Исправляю упущение.
Рабочая процедура отправки файла в MAX.
ПутьКФайлу - путь к файлу во временном хранилище
IDЧата - это user_id пользователя
ТокенБота - токен бота

Чтобы отправлять не конкретному пользователю, а в группу, надо в этой стоке заменить user_id на chat_id
HTTPЗапросСообщения = Новый HTTPЗапрос("/messages?user_id=" + IDЧата, ЗаголовкиАвторизации);

Процедура ОтправитьФайлВMax(ПутьКФайлу, IDЧата, ТокенБота, ТипФайла = "file", ТекстСообщения = "") Экспорт
      ИмяФайла     = "ФайлОтчета_" + СтрЗаменить(ТекущаяДата(),":","") + ".xls";
      // Заголовки авторизации
      ЗаголовкиАвторизации = Новый Соответствие;
      ЗаголовкиАвторизации.Вставить("Authorization", ТокенБота);

      //Получаем URL для загрузки файла
      HTTPСоединение = Новый HTTPСоединение("platform-api.max.ru", 443, , , , , Новый ЗащищенноеСоединениеOpenSSL());
      HTTPЗапрос = Новый HTTPЗапрос("/uploads?type=file", ЗаголовкиАвторизации);
      
      // Отправляем POST запрос (пустое тело)
      HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
      Если HTTPОтвет.КодСостояния <> 200 Тогда
          ВызватьИсключение "Ошибка при получении URL для загрузки: " + HTTPОтвет.КодСостояния + " - " + HTTPОтвет.ПолучитьТелоКакСтроку();
      КонецЕсли;
      
      // Парсим JSON ответ
      ЧтениеJSON = Новый ЧтениеJSON;
      ЧтениеJSON.УстановитьСтроку(HTTPОтвет.ПолучитьТелоКакСтроку());
      ОтветМетаЗагрузки = ПрочитатьJSON(ЧтениеJSON);
      ЧтениеJSON.Закрыть();
      
      URLДляЗагрузки = ОтветМетаЗагрузки.url; // URL куда слать файл

      //Загружаем файл на полученный URL (Multipart/Form-Data)

      // Разбираем URL чтобы понять куда коннектиться
      РазборURL = СтрРазделить(URLДляЗагрузки, "/", Ложь);
      СерверЗагрузки = РазборURL[1]; // Например, storage.max.ru
      ПутьЗагрузки   = СтрЗаменить("/" + СтрСоединить(РазборURL, "/"),"/https:/fu.oneme.ru",""); // Остальная часть пути
      
      
      // Формируем Multipart тело запроса
      Boundary      = "----WebKitFormBoundary" + СтрЗаменить(Новый UUID, "-", "");
      ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ПутьКФайлу);
      
      // Строим тело запроса в памяти
      ПотокВПамяти = Новый ПотокВПамяти;
      ЗаписьДанных = Новый ЗаписьДанных(ПотокВПамяти);
      
      //Начало части (headers)
      ЗаписьДанных.ЗаписатьСтроку("--" + Boundary);
      ЗаписьДанных.ЗаписатьСтроку("Content-Disposition: form-data; name=""data""; filename=""" + ИмяФайла + """");
      ЗаписьДанных.ЗаписатьСтроку("Content-Type: application/octet-stream");
      ЗаписьДанных.ЗаписатьСтроку(""); // Пустая строка между заголовками и данными
      
      //Сам файл (бинарные данные)
      ЗаписьДанных.Записать(ДвоичныеДанныеФайла);
      ЗаписьДанных.ЗаписатьСтроку(""); // Перенос строки после файла
      
      //Конец запроса
      ЗаписьДанных.ЗаписатьСтроку("--" + Boundary + "--");
      
      ЗаписьДанных.Закрыть();
      ТелоЗапросаЗагрузки = ПотокВПамяти.ЗакрытьИПолучитьДвоичныеДанные();
      
      // Формируем HTTP запрос
      HTTPСоединениеЗагрузки = Новый HTTPСоединение(СерверЗагрузки, 443, , , , , Новый ЗащищенноеСоединениеOpenSSL());
      HTTPЗапросЗагрузки = Новый HTTPЗапрос(ПутьЗагрузки);
      HTTPЗапросЗагрузки.Заголовки.Вставить("Content-Type", "multipart/form-data; boundary=" + Boundary);
      HTTPЗапросЗагрузки.УстановитьТелоИзДвоичныхДанных(ТелоЗапросаЗагрузки);
      
      HTTPОтветЗагрузки = HTTPСоединениеЗагрузки.ОтправитьДляОбработки(HTTPЗапросЗагрузки);
      
      Если HTTPОтветЗагрузки.КодСостояния <> 200 Тогда
          ВызватьИсключение "Ошибка при загрузке файла: " + HTTPОтветЗагрузки.КодСостояния;
      КонецЕсли;
      
      // Парсим ответ загрузки (информация о файле)
      ЧтениеJSON.УстановитьСтроку(HTTPОтветЗагрузки.ПолучитьТелоКакСтроку());
      ДанныеФайла = ПрочитатьJSON(ЧтениеJSON);
      ЧтениеJSON.Закрыть();

         //тут нужна пауза, видимо файл не успевает записаться. Без паузы валит ошибку
     //Ошибка отправки сообщения: {"code":"attachment.not.ready","message":"Key: errors.process.attachment.file.not.processed"}      
     //с паузой всё норм
      ДатаКон = ТекущаяДата() + 1;//1 секундой больше
      Пока ТекущаяДата() < ДатаКон Цикл
      КонецЦикла;      
      
      //ВызватьПаузу(1000);

      //Отправляем сообщение с вложением
      HTTPЗапросСообщения = Новый HTTPЗапрос("/messages?user_id=" + IDЧата, ЗаголовкиАвторизации);
      HTTPЗапросСообщения.Заголовки.Вставить("Content-Type", "application/json");
      
      // Формируем JSON тело
      ЗаписьJSON = Новый ЗаписьJSON;
      ЗаписьJSON.УстановитьСтроку();
      ЗаписьJSON.ЗаписатьНачалоОбъекта();
      ЗаписьJSON.ЗаписатьИмяСвойства("text");
      ЗаписьJSON.ЗаписатьЗначение("Отправляю файл");
      
      ЗаписьJSON.ЗаписатьИмяСвойства("attachments");
      ЗаписьJSON.ЗаписатьНачалоМассива();
      ЗаписьJSON.ЗаписатьНачалоОбъекта();
      ЗаписьJSON.ЗаписатьИмяСвойства("type");
      ЗаписьJSON.ЗаписатьЗначение("file");
      
      // В payload записываем весь объект, полученный на шаге 2
      ЗаписьJSON.ЗаписатьИмяСвойства("payload");
      ЗаписатьJSON(ЗаписьJSON, ДанныеФайла);
      ЗаписьJSON.ЗаписатьКонецОбъекта();
      ЗаписьJSON.ЗаписатьКонецМассива();
      ЗаписьJSON.ЗаписатьКонецОбъекта();
      
      СтрокаТелаЗапроса = ЗаписьJSON.Закрыть();
      HTTPЗапросСообщения.УстановитьТелоИзСтроки(СтрокаТелаЗапроса, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
      
      HTTPОтветСообщения = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапросСообщения);
      
      Если HTTPОтветСообщения.КодСостояния = 200 Тогда
          Сообщить("Файл успешно отправлен!");
      Иначе
          Сообщить("Ошибка отправки сообщения: " + HTTPОтветСообщения.ПолучитьТелоКакСтроку());
      КонецЕсли;
  
КонецПроцедуры
12 Stepashkin
 
13.04.26
10:16
Пригодится.
13 skafandr
 
13.04.26
11:53
(13) Спасибки. А вдруг тоже придется прикоснуться к прекрасному :-(
14 VKS
 
13.04.26
17:20
Пользуясь случаем спрошу кто работает с макс. Как в групповом чате через api упомянуть пользователя?
В телеграмм было через ИмяПользователя, а тут не выходит
15 yurikmellon2
 
14.04.26
10:12
(14) вот так
{
  "text": "Юрий <a href=\"max://user/123456789\">Юрий</a>",
  "format": "html"
}

бот, который отправляет сообщение, должен быть админом чата. Иначе ссылка не обработается
16 VKS
 
14.04.26
10:13
(15) в html никак не выходит.
вот в markdown нормально уходит: [Юрий](max://user/123456789)


"text": "[Имя Фамилия](max://user/user_id)", "format": "markdown"
Вместо User mention указывайте полное имя пользователя из профиля в MAX, в том числе фамилию. Если фамилия отсутствует — только имя
17 yurikmellon2
 
14.04.26
10:18
(16) да, так тоже отработал
{
  "text": "[Юрий](max://user/12345678)",
  "format": "markdown"
}

но и в html работает
18 areaho0ray
 
14.04.26
12:17
(15) Забавно. Если бот не админ чата - он сможет писать в чат, но не не сможет его читать. Нигде в API об этом не написано и ты битый час сидишь и не понимаешь, почему /updates отваливается по таймауту и никаких сообщений из группы ты там не увидишь.
19 yurikmellon2
 
14.04.26
14:23
(18) там вся документация левой ногой написана. Но, надо отдать должное, на вопросы отвечают довольно оперативно.
Чтобы обнаруживать ошибки, программист должен иметь ум, которому доставляет удовольствие находить изъяны там, где, казалось, царят красота и совершенство. Фредерик Брукс-младший