Однажды мне захотелось узнать сколько активных пользователей у моего проекта. По сути это библиотека из Spotify API со множеством надстроек. Каждый пользователь копирует код к себе на Google аккаунт, чтобы запускать разные действия по расписанию через Apps Script. Например, удалить из плейлиста недавно игравшие треки.

Другими словами, мне нужно собрать статистику из множества копий библиотеки. Предположим, что существуют готовые решения. Например, некий сервис, который готов получать запросы и рисовать красивую графику. Почти наверняка он прибегнет к пробному периоду или ограниченному плану. Будет зарубежным, что вызовет трудности с оплатой. С другой стороны, я бы не хотел тратиться да и локального сервера у меня нет. Поэтому, даже не пытаясь искать готовых решений, подумал про Google Формы - бесплатно, без заметных ограничений, есть выход к Google Таблицам для графики.

У Google Форм есть API как REST, так и внутри Apps Script. Но только для принимающей стороны (создать формы, читать ответы). То есть нельзя отправить ответ от лица пользователя. Что же делает сам Google когда мы нажимаем отправить?

Где пригодится

В следующий раз мне понадобилось собрать небольшую статистику для другого проекта - андроид приложения для Яндекс.Музыки. Просто отправляю единичный запрос с помощью Retrofit при первой установке, чтобы знать сколько пользователей у приложения. Помогает, когда нет возможности опубликовать в Google Play или другом сторе.

Как видите, пригодилось при разных сценариях и платформах. Надеюсь, будет полезно и в ваших хобби-проектах.

Перепись пользователей прочитавших статью

Разберем пошаговый пример, а заодно узнаем кто "прочитал" статью.

  1. Создаем свою форму с нужными полями. Для примера сделал два: имя и дата.

  2. Открываем форму для ответа и заполняем поля их же названием. Не отправляем.

  3. Открываем консоль браузера на странице формы (F12) и выполняем код:

var form = document.querySelector('form')
var formId = form.action.match(/e\/(.+)\/formResponse/)[1]
var entries = Array.from(form.firstChild.querySelectorAll('input'))
  .filter(i => i.name.includes('entry'))
  .map(i => ({ name: i.name, value: i.value }))

console.log('action =', form.action)
console.log('formId =', formId)
console.log('entries =', entries)

В результате получаем id формы и всех полей

[
  {
    "name": "entry.2096275148",
    "value": "имя"
  },
  {
    "name": "entry.1516955237",
    "value": "дата"
  }
]
Если скрипт не работает

Возможно со временем Google изменит разметку и скрипт сбора id сломается. На такой случай вам нужно открыть разметку страницы и найти элемент form

Элемент form с нужными значениями
Элемент form с нужными значениями

  1. Теперь остается отправить POST-запрос по адресу action с телом name1=foo&name2=bar.

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

sendForm(
  'https://docs.google.com/forms/u/0/d/e/1FAIpQLSdZ7RMKGc3nes4s8FL0hwLpiel52gJT5_4tf0EjVIrL0jmICA/formResponse',
  {
    'entry.2096275148': await getMyHabrAlias(),
    'entry.1516955237': new Date().toISOString(),
  }
)

function sendForm(action, body) {
  // corsUrl - только для отправки из консоли браузера, 
  // в остальных случаях напрямую action
  let corsUrl = `https://cors-anywhere.herokuapp.com/${action}`
  fetch(corsUrl, {
    method: 'post',
    body: new URLSearchParams(body),
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      'Origin': window.origin // для corsUrl
    }
  })
}

function getMyHabrAlias(defaultAlias = "guest") {
  return new Promise((resolve) => {
    fetch('https://habr.com/ru/top/daily/')
      .then(response => response.text())
      .then(html => {
        try {
          let raw = html.split('window.__INITIAL_STATE__=')[1].split(';(function()')[0]
          let state = JSON.parse(raw)
          resolve(state.me.user.alias)
        } catch (error) {
          console.error(`Не удалось получить alias. Дефолт = ${defaultAlias}`)
          resolve(defaultAlias)
        }
      })
  })
}

В результате мы заполнили и отправили форму с устройства пользователя без его непосредственного участия.

Да, вы, возможно, только что явно запустили скрипт, который отправляет логин с хабра. Но это только пример формирования запроса. В рамках приложения, библиотеки, расширения и других программ - действий со стороны пользователя нет.

Комментарии (4)


  1. vassabi
    02.01.2023 17:59

    а потом меня спрашивают - "почему ты запрещашь выполнение скриптов" ?


    1. Viewed Автор
      02.01.2023 18:16
      -2

      Это лишь инструмент. И здесь скрипт вообще нужен только для удобства получения id полей.
      Сразу все устройства выключайте. Там тоже скрипты гоняют и похлеще.


  1. BasiC2k
    02.01.2023 21:14
    +1

    Не совсем понятно, почему для сбора данных не была использована опция Deploy для Apps Script.

    Эта опция позволяет по сути развернуть свой web или api сервер, который будет принимать POST/GET запросы и складывать полученные данные. Причём это можно делать в фоновом режиме, без участия пользователя. Да, там есть ряд ограничений и особенностей реализации, но насколько я понял первоначальную задачу - решить вполне может.


    1. Viewed Автор
      02.01.2023 22:32

      В варианте с Google Формами тоже нет участия пользователя библиотеки. Она сама сделает запрос когда надо.
      Да, ваш вариант тоже возможен. Просто это чуть сложнее или скорее дольше, чем просто один запрос, который позволяет сохранить и в форму, и в таблицу без дополнительного кода и своего сервера.