• Как исправить ошибку JS undefinit object[3]?

    @GrigoryMorozov
    Проблема возникает из-за того, что первый элемент в массиве objects — пустая строка (ведь objectsCSV начинается с переноса строки, по которому и будет выполняться разбиение).

    В вашем случае можно поступить так:

    var objects = objectsCSV.split('\n').split(1);

    Плюс у вас, судя по всему, неверно формируется строка. Правильный вариант подстановки такой:

    balloonContent: `<a href="/flights/?${object[3]}origin_iata=...`


    Но это не главное. Возникновение подобных ошибок связно со стилем кода, с которым трудно работать, и со сложностями с отладкой (я, например, сразу дописал к коду несколько вызовов console.log, чтобы понять, в чём дело). Попробуйте почитать стайлгайды по JS и руководства по отладке, чтобы избегать подобных ошибок в будущем.
    Ответ написан
  • Как интегрировать оплату за звезды в WebApp?

    @GrigoryMorozov
    Всё максимально просто и прозрачно: получаете ссылку на инвойс методом createInvoiceLink (это нужно сделать через собственный серверный скрипт) и открываете её в мини-приложении методом openInvoice.
    Ответ написан
    Комментировать
  • Как шарить изображения в telegram mini apps js?

    @GrigoryMorozov
    Если мини-приложение запускается с помощью инлайн-кнопки, а вы хотите отправить изображение в чат с ботом, используйте серверный метод answerWebAppQuery. Реализация примерно такая:
    1. Отправляете сгенерированное изображение вместе с параметром initData собственному серверному скрипту
    2. Проверяете параметр initData, извлекаете из него query_id
    3. Сохраняете сгенерированное изображение
    4. Вызываете метод answerWebAppQuery с извлечённым query_id. В качестве параметра result передаёте InlineQueryResultPhoto со ссылкой на сгенерированное изображение
    5. Сгенерированное изображение спустя какое-то время можно удалить

    Альтернативно, если мини-приложение запускается по прямой ссылке, можно использовать клиентский метод switchInlineQuery. Предварительно выполните шаги 1-3 и сохраните ассоциацию пользователя со ссылкой на сгенерированное изображение.

    После вызова клиентского метода мини-приложение закроется, будет предложено выбрать чат (если передавался параметр choose_chat_types), а бот получит событие inline_query. Ответьте на него с помощью серверного метода answerInlineQuery, используя сохранённую ассоциацию (параметр results в данном случае — массив с одним элементом типа InlineQueryResultPhoto, cache_time должен быть небольшим, а is_personal — true).
    Ответ написан
    Комментировать
  • Снипет в для telegram minapp - как?

    @GrigoryMorozov
    Судя по URL из примера, вы не разделяете два разных варианта запуска мини-приложения:
    1. Direct Link Mini App. Пример — на первом скриншоте. Для настройки используйте команду /newapp. В процессе нужно будет указать URL, который в данном конкретном примере равен app (важно: это не какого-то специальное ключевое слово). Сниппет будет рендериться по прямой ссылке: https://t.me/DurgerKingBot/menu
    2. Main Mini App. Это то, что вы настроили. Главный результат — большая кнопка для открытия мини-приложения в окне информации о боте. Сниппет будет рендериться с GET-параметр startapp: https://t.me/DurgerKingBot?startapp

    Если необходимо, можно настроить оба варианта запуска одновременно.
    Ответ написан
    1 комментарий
  • Как добавить html код для каждой страницы с определенным url?

    @GrigoryMorozov
    Варианта два:
    1. Либо пишете скрипт, который редактирует все файлы в каталоге (функции scandir, file_get_contents и file_put_contents)
    2. Либо с помощью .htaccess (или другими средствами) направляете все запросы на конкретный PHP-файл, извлекаете из URL название запрашиваемого HTML-файла, выводите его и в процессе вывода добавляете необходимый контент (читайте про роутинг)
    Ответ написан
    Комментировать
  • Как реализовать ленивую загрузку TFJS и Face Landmarks Detection?

    @GrigoryMorozov
    Ваша конкретная ошибка состоит в том, что вы не подключаете все необходимые зависимости или загружаете их в неправильном порядке.

    Более вероятно второе: не забывайте, что динамически добавляемые скрипты по-умолчанию являются асинхронными (то есть первым выполнится тот скрипт, который загрузится раньше других). В данном случае это некорректное поведение, которое нужно исправить:

    script.async = false;

    Также разумно использовать промисы вместо коллбэков, чтобы не росла вложенность кода. Полный пример:

    // Functions
    
    const loadedScripts = new Set();
    
    async function loadScript(url) {
      if (loadedScripts.has(url)) return;
      
      const script = document.createElement('script');
    
      script.src = url;
      script.async = false;
    
      document.head.append(script);
    
      return new Promise((resolve, reject) => {
        script.addEventListener('load', () => {
          loadedScripts.add(url); resolve();
        });
      
        script.addEventListener('error', () => reject(
          new Error(`Error loading script: ${url}`)
        ));
      });
    }
    
    async function loadImage(url) {
      const image = new Image();
    
      image.src = url;
      image.crossOrigin = 'anonymous';
    
      return new Promise((resolve, reject) => {
        image.addEventListener('load', () => {
          resolve(image);
        });
      
        image.addEventListener('error', () => reject(
          new Error(`Error loading image: ${url}`)
        ));
      });
    }
    
    // Example
    
    const image = loadImage('https://habrastorage.org/r/w1560/getpro/habr/upload_files/799/62f/375/79962f375d90db7f7d926ff40d623456.png');
    
    await Promise.all([
      loadScript('https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-core'),
      loadScript('https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-converter'),
      loadScript('https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-webgl'),
      loadScript('https://cdn.jsdelivr.net/npm/@tensorflow-models/face-landmarks-detection@1.0.6/dist/face-landmarks-detection.min.js')
    ]);
    
    const model = faceLandmarksDetection.SupportedModels.MediaPipeFaceMesh;
    
    const detectorConfig = {
      runtime: 'tfjs', 
      solutionPath: 'https://cdn.jsdelivr.net/npm/@mediapipe/face_mesh'
    };
    
    const detector = await faceLandmarksDetection.createDetector(model, detectorConfig);
    const faces = await detector.estimateFaces(await image);
    
    console.log(faces);
    Ответ написан
    1 комментарий
  • При отправке команды боту в телеграмм автоматически добавляется @имя_бота как убрать это добавление?

    @GrigoryMorozov
    Это особенность клиента Телеграма, которую не получится отключить. Помните, что в группе может быть несколько ботов, и было бы хорошим тоном научить вашего бота реагировать только на команды, которые обращены к нему. Конечно, это может быть неактуально в конкретной ситуации, но в общем случае это правильный подход.
    Ответ написан
  • Как сделать безопасное общение фронтенда (Web App) с бекендом?

    @GrigoryMorozov
    Помните, что в рамках мини-приложения вы можете доверять только параметру initData, который вы соответствующим образом проверили. Из него можно извлечь ID пользователя в Телеграме и уже дальше определять, какие действия этому пользователю доступны.
    Ответ написан
    Комментировать
  • Как проверить подписку на телеграм канал в Unity?

    @GrigoryMorozov
    Сценарий примерно такой:
    1. Генерируете ссылку на мини-приложение с параметром startapp, по которому затем можно будет восстановить ID пользователя в игре
    2. Из мини-приложения перенаправляете пользователя в канал, предварительно сохраняя с помощью fetch-запроса соответствие параметра startapp (доступно через свойство tgWebAppStartParam) с ID пользователя в Телеграме (его нужно взять из параметра initData; не забывайте проверять данные на сервере)
    3. Периодически (или по запросу из игры) вызываете метод getChatMember, чтобы проверить, подписан ли пользователь на канал. Бот, от имени которого выполняется этот запрос, должен быть добавлен в канал
    Ответ написан
    Комментировать
  • Telegram mini app, как реализовать добавление бота в группу?

    @GrigoryMorozov
    Интересный вопрос.

    С помощью реверс-инжиниринга получен ответ: здесь неявно используется deep linking. Более конкретно: после нажатия на кнопку вызывается метод openTelegramLink с параметром https://t.me/UTasksBot?startgroup=1. Открытие такой ссылки (в любом контексте, не только в мини-приложении) позволяет выбрать группу и добавить в неё бота.

    А дальше всё плюс-минус понятно: отслеживаем событие my_chat_member и реагируем на него соответствующим образом. Если нужно узнать об этом событии из веб-приложения, можно использовать сокеты или long polling.
    Ответ написан
    Комментировать
  • Как загрузить в гугл таблицу результаты опроса из телеграм бота?

    @GrigoryMorozov
    К сожалению, просто получить результат опроса каким-то методом невозможно. Но можно настроить получение событий с ответами пользователей и самостоятельно формировать результат. В общих чертах:
    1. Настраиваете получение события poll_answer с помощью периодических запросов методом getUpdates или устанавливаете веб-хук, на который Телеграм сам будет отправлять запросы с этим событием (метод setWebhook; судя по всему, это ваш случай)
    2. Реализовываете логику с сохранением результата опроса (поле poll_answer в Update). Вы будете знать ID опроса, ID пользователя, проголосовавшего в опросе (если опрос неанонимный), и список выбранных им опций. Помните, что пользователь может переголосовать — это штатная функция Телеграма
    3. А уже затем работаете с таблицами. API у Google Таблиц нетривиальный (хотя, конечно, зависит от задачи), но разобраться с ним можно
    Ответ написан