Как организовать хранилище музыкальных групп в Google Таблицах с поддержкой с разных программ?
Дано: Аккаунты в Google, VK, Telegram.
Задача: Создать хранилище музыкальных групп с возможностью удобного пополнения.
Инструменты: Google Apps Script (GAS), VK API, Tampermonkey, Telegram Bot API.
Создаём новую Google Таблицу.
Делаем закреплённый заголовок (потом все циклы перебора ячеек из-за этого будут начинаться не с 0, а с 1).
В принципе наш шаблон готов.
Единственное, я не сделал привязку к колонкам через имена (не хотел использовать циклы), так что на протяжении всего скрипта буду обращаться к жёстко зашитому номеру колонки.
Жмём «Инструменты — Редактор скриптов».
Создаём 2 скрипта: Код.gs и Бот.gs.
Подробнее разберём ниже.
Жмём "Опубликовать — Развернуть, как веб приложение".
Теперь у нас есть ссылка вида script.google.com/macros/s/AAA/exec, подставив к которой параметр «link», мы сможем занести группу ВК в Google Таблицу.
Первый и основной способ занесения данных!
Открываем браузер Google Chrome или Mozilla Firefox и ставим плагин юзерскриптов Tampermonkey.
Ссылки для Хрома, для Мозиллы.
Изначально пользовался Greasemonkey, но пришлось перейти на Tampermonkey из-за кроссбраузерности.
У кого возникнут вопросы, почему параметры юзерскрипта начинаются с префикса GM_, а не TM_ — идея не моя, идём в документацию.
Вставляем скрипт vkGroupToGS, нажав на первую вкладку с плюсиком.
«Файл — Сохранить».
Как видим, наш скрипт рисует плюсик (на что хватило фантазии) в левом блоке left_menu_nav_wrap.
По нажатию на плюсик выполняется GM_xmlhttpRequest из vkGroupToGS в наш скрипт Код.gs по ссылке Google Таблицы, приложенной выше (url_first).
Tampermonkey запросит у нас связать эти 2 скрипта правами, жмём либо "Всегда разрешать" либо "Всегда разрешать домену" для "script.google.com". Повторить при надобности для "script.googleusercontent.com".
Как только запрос успешно улетит, придёт колбэк из GM_xmlhttpRequest и окрасит нашу кнопку в зелёный цвет ? при удачном добавлении либо в красный ? если вернулась ошибка.
Плюс Tampermonkey — облачное хранение скриптов в Google (раньше не было) при наличии их плодовитости и своего воображения.
Вернёмся к Google Таблице.
Перед тем, как начать разбирать наш Код.gs я хочу поделиться документацией для разработки в ВК — vk.com/dev
Получение ключа доступа
Выполнение запросов к API ВКонтакте
Пока научился только одним способом добывать токен — через самописные приложения.
Заходим сюда.
Платформа — "Standalone-приложение".
После создания всё, что нам надо — это ID приложения (ныне семизначный), пусть будет 1234567.
Но нам нужен токен.
Очень хорошо про токен написано в Знакомстве с API ВКонтакте.
Если не хотим читать, просто открываем ссылку, вставляя в client_id свой ID приложения.
Нас редиректит на страничку с надписью:
И это верно, будьте очень аккуратны с токеном!
Токен копируем из адресной строки после access_token.
Сразу поделюсь, что с этим токеном можно запросы вида vk.com/dev/groups.search?params%5Bq%5D=%D0%BC%D1%83%D0%B7%D1%8B%D0%BA%D0%B0¶ms%5Btype%5D=page¶ms%5Bcountry_id%5D=1¶ms%5Bcity_id%5D=1¶ms%5Bfuture%5D=0¶ms%5Bmarket%5D=0¶ms%5Boffset%5D=0¶ms%5Bcount%5D=10¶ms%5Bv%5D=5.107
можно спокойно преобразовывать в ссылки api.vk.com/method/groups.search?q=%D0%BC%D1%83%D0%B7%D1%8B%D0%BA%D0%B0&type=page&country_id=1&city_id=1&future=0&market=0&offset=0&count=10&access_token=1111111111111&v=5.107 и получать информацию в любом приложении.
Я открыл маленький ящик пандоры, возможно это и не новость, но таким способом можно получить много полезного из ВК, автоматизировать множество процессов и сделать уйму удобняшек.
Конечно с определёнными ограничениями, допустим не больше 999 сущностей в ответе или 20 запросов в секунду и т.п.
Также кому-нибудь будет интересно поиграться с хранимками — в нашем приложении вкладка "Хранимые процедуры".
Там есть свои правила вытягивания информации, очень удобно для последовательных запросов, чтоб не делать множество циклов у себя в коде.
Следим за версиями!
С их изменениями может меняться функциональность методов. Ныне прикладываю примеры для версии 5.107.
Итак, токен у нас есть, переходим к…
Код.gs
doGet(e) — Принимаем входящие get запросы от бота, ВК и просто с браузера.
Входные параметры парсятся, как e.parameters['ВХОДНОЙ_ПАРАМЕТР']
Прилетает ссылка, проверяем, есть ли в таблице. Если нет, запрашиваем подробную информацию через groups.getById, парсим, добавляем.
! Внимание! Скрипт простой, так что будет проглатывать все прилетающие ссылки, так что будьте аккуратны даже введя 123 при проверке скрипта.
searchGenre — умный поиск стиля/жанра группы, из-за которого я и поставил метку «регулярные выражения» в статье.
Возможны улучшения.
Пока работает только на поиск последующей цепочки слов через слеш или дефис после слов "стиль или жанр или genre".
isRus — простая регулярка, исключающая русские буквы.
sort — сортировка. Поставил по умолчанию — 1. город, 2. название.
searchExists — простой поиск существующих групп через цикл.
addInfo — сделал скрипт для себя, оставил здесь, как бонус. Нужен если у вас есть список ссылок, но нет по ним данных.
Вставляем список в колонку «Ссылка», жмём "Дозаполнить пустые" в главном меню и остальные колонки дозаполняются из ВК.
Из-за ограничений на количество символов в UrlFetchApp.fetch и в количестве ссылок, помещаемый в адресную строку пришлось сделать цикл по 1000 символов.
getVkFave — ещё один бонус, возникший в голове во время написания статьи и которым я сам стал пользоваться.
Суть — заходим с телефона в приложение ВК, натыкаемся на группу, которую хотим сохранить, сохраняем её в закладках с заранее добавленной меткой "муз.группы" (глобальная переменная faveTag).
Как они накопятся, заходим в нашу Google Таблицу, вызываем меню, жмём "Втянуть закладки из ВК".
Скрипт с токеном лезет в api.vk.com/method/fave.getTags, парсит, добавляет в таблицу, предварительно предупредив об удалении из ВК (тут аккуратней с тестированием, всё удаляется подчистую с закладок с этим тегом).
getStatusUrl и checkActualLink — эксперимент, вылившийся в 2 скрипта.
Суть — проверять валидность и доступ до ссылок.
Потому что за годы накопления музыкальных групп я столкнулся с тем, что паблики удаляют либо переименовывают.
Кто будет пользоваться моей удобняшкой, советую некое TODO, которое будет не просто удалять невалидные ссылки, но и проверять через groups.search по названию наличие в ВК.
checkActualLink — единоразово проверяет валидность, окрашивая в красный невалидные.
getStatusUrl — вызывается в ячейке через скрипт
Основу взял отсюда.
Далее идут удобняшки для гуглоскриптов:
addInTableFromArray — Добавление строки в таблицу. Либо с цикла либо единоразово.
getNumSheet — Поиск номера листа по имени. Честно, не нашёл в интернете способа проще, чем этот. Нужен из-за смещения номеров листков при добавлении новых. Основу взял отсюда.
escapeHtml — Удалить спецсимволы
ll — для себя сделал Logger.log кратким, сокращает время, советую.
onOpen — Всем знакомая функция для отрисовки меню в Google Таблице.
Итак, наш Код.gs ловит ссылки с ВК через Tampermonkey и добавляет группы в Google Таблицу.
С компьютера мы научились добавлять группы через "плюсик" в ВК и через закладки.
Через телефон только через закладки в приложении ВК.
Разберём ещё два варианта внесения групп.
Юзерскрипты в мобильном браузере.
Кому-то он покажется старомодным и ненужным, но найдутся те люди, которые вынесут плюсы из него.
Поискав в интернете нашёл пока только такой плагин.
Работает только в Firefox (тестировал только в android).
Огромный плюс — можно использовать скрипт Tampermonkey.
Из-за отличий вёрстки между vk.com и m.vk.com, а также из-за открывания окна по умолчанию в m.vk.com я не стал рисовать плюсик, а сделал простой скрипт VK_event_to_list_mvk, срабатывающий при открытии окна (гореть мне в аду за setTimeout, знаю).
Создаём бота по инструкции.
Нам нужен токен, пусть он будет 123123.
Создаём скрипт Бот.gs, вставляем в переменную botApi наш 123123, обрамляя кавычками и ссылкой api.telegram.org.
Далее надо сцепить нашего бота с Google Таблицей. Посмотреть, как это сделать через WebHook можно в предыдущей статье.
Что будет делать наш бот?
Разложим по командам:
Видим, что у нас есть начало работы бота, туда мы вставим просто tutorial с описательной частью на ваше усмотрение.
Три команды из главного меню на всякий случай.
И очередной бонус — /getall, о нём ниже.
Команды для удобного вызова (в строке ввода сообщения написать слеш) можно добавить в том же @BotFather через /setcommands.
Формат — строки через тире:
Бот.gs
doPost — принимает входящие сообщения от бота.
Естественно, как пытливый программист, я сразу начал использовать логи. Возможно есть способы проще, но я просто создал новый лист, назвал его «Логи» и ловлю в первую ячейку входящие запросы.
Поэтому после того, как мы распарсили входящее сообщение через JSON.parse(e.postData.contents), сразу пишем в логи.
Далее определяем, какая из команд нам пришла.
Если не команда, проверяем на валидность, то есть существует ли группа в ВК.
sendText — отправляет сообщения боту (по умолчанию parse_mode = HTML), документация.
/getall — разберём отдельно.
Пусть вас не пугает моё решение из
Сделано для заполнения строчек городами, пока ограничимся 8-10 строчками по 4 города (переменная j = 4).
Если музыкальная группа без города, вставляем ---.
Просто выбираем город и ловим русские буквы в Бот.gs.
Выводим список ссылок, ничего более.
Помним, что доступ к документации и тестированию бота через адресную строку через Hotspot Shield Free VPN Proxy — Unlimited VPN (для Google Chrome).
Вот мы и изучили три способа внесения в нашу таблицу:
Префикс club в ссылках ВК
В группах ВК есть 2 типа хранения идентификатора.
Причём выкладываться могут и так и так.
Проверить просто.
Если есть группа, допустим vk.com/4soulsband, подставляем 4soulsband в group_ids в vk.com/dev/groups.getById, выводится перечень параметров, один из которых ID. И если мы перед ним поставим club, вуаля, попадаем на ту же страницу vk.com/club68130764.
Ограничение на хранение групп в ВК до 5000
Кажется смешным, но мне не хватило.
Моя боль с обновлением страницы ВК
Каюсь, пока не смог победить отлов перехода между страницами в ВК для сброса состояния плюсика в Tampermonkey. TODO
Использование Google Таблиц, как БД
Определение города группы
Адрес группы в ВК может быть тут vk.com/dev/groups.getAddresses, соответственно кто захочет улучшить поиск города для группы, может использовать его.
У нас есть:
Моя цель не кичиться очередной созданной игрушкой, моя цель показать новичкам (и не только) огромный пласт открытых API`шек, которые помогут облегчить жизнь в той или иной степени, перенестись с железной земли в «облака», так сказать.
Данный функционал готов, как и открыт для улучшений.
Удачи в разработке и постижении новых знаний.
Google Apps Script — Spreadsheet Service
Telegram Bot API
VK API
Учимся писать userscript'ы
Userscripts. Углубляемся
Мониторинг сайта с помощью Google Docs
Баловство. Пишем Telegram бота на Google script
[Примеры, Google Apps Script] Разработка дополнений/скриптов для Google Таблиц (spreadsheets)
How to use google spreadsheets to check for broken links
Дано: Аккаунты в Google, VK, Telegram.
Задача: Создать хранилище музыкальных групп с возможностью удобного пополнения.
Инструменты: Google Apps Script (GAS), VK API, Tampermonkey, Telegram Bot API.
Создание Google Таблицы
Создаём новую Google Таблицу.
Делаем закреплённый заголовок (потом все циклы перебора ячеек из-за этого будут начинаться не с 0, а с 1).
В принципе наш шаблон готов.
Единственное, я не сделал привязку к колонкам через имена (не хотел использовать циклы), так что на протяжении всего скрипта буду обращаться к жёстко зашитому номеру колонки.
Жмём «Инструменты — Редактор скриптов».
Создаём 2 скрипта: Код.gs и Бот.gs.
Код.gs
access_token = '1111111111' // VK token
ss = SpreadsheetApp.getActiveSpreadsheet() // Таблица
sheet = ss.getSheets()[getNumSheet('Группы')] // Лист
data = sheet.getDataRange().getValues() // Все ячейки
numRows = sheet.getLastRow()+1 // Последняя строка
faveTag = 'муз.группы' // Тег закладок в ВК
// Принимаем входящие get запросы
function doGet(e) {
try {
var link = e.parameters['link']
/*
A - Название
B - English
C - Ссылка
D - Город
E - Жанр
F - Описание
G - Примечание
*/
var n = searchExists(link)
if (n != false) return ContentService.createTextOutput('Группа '+n+' уже есть!')
// Запрос в API VK https://vk.com/dev/groups.getById
var html = UrlFetchApp.fetch('https://api.vk.com/method/groups.getById.json?group_id='+link+'&fields=city,description&access_token='+access_token+'&v=5.107')
// Парсим в json первый элемент response
var json = JSON.parse(html).response[0]
addInTableFromArray(json, link)
sort()
return ContentService.createTextOutput('Добавлено')
} catch (e) {
return ContentService.createTextOutput('Ошибка из гуглотаблиц doGet! '+e)
}
}
// Поиск стиля/жанра группы из описания по регулярке
function searchGenre(txt) {
var t = txt.match(/(?:(?:стил[ь|я|ю|ем|е]|genre|жанр[а|у|ом|е]?)[\s:-]+){1}([a-zа-я\/-]+)/i)
if(txt != '' && t) return t[1]
}
// Поиск русских букв в названии
function isRus(txt) {
return txt.search(/[^A-Za-z0-9\/.:_]+/i)
}
// Сортировка
function sort(){
// Диапазон сортируемых ячеек от левой верхней до правой нижней
var tableRange = "A2:G"+numRows
var editedCell = sheet.getActiveCell()
var range = sheet.getRange(tableRange)
// Порядок сортировки по номеру столбца. Начинается не с 0, а с 1
range.sort([{
column : 4,
ascending: true
},{
column: 1,
ascending: true
},{
column: 2,
ascending: true
}])
}
// Поиск существующих групп
function searchExists(t) {
for (var i = 1; i < data.length; i++) {
if (t == data[i][2] || 'club'+t == data[i][2]){
return data[i][0]+data[i][1]
}
}
return false
}
// Подтягивание данных массово если их нет
function addInfo(isBot = false) {
var arr = []
var j = 0
var part = 1000 // Кол-во символов, на которое будет делиться блок текста. Из-за ограничений в адресной строке и UrlFetchApp.fetch
arr[j] = new Array()
for (var i = 1; i < data.length; i++) {
var txt = data[i][2].replace('https://vk.com/','').replace('vk.com/','').replace('^club','')
if (txt != '' && data[i][0] == '' && data[i][1] == '' && data[i][3] == '' && data[i][4] == '' && data[i][5] == '' && data[i][6] == ''){
arr[j].push(txt)
if (arr[j].toString().length > part){
j++
arr[j] = new Array()
}
}
}
if (arr[0].length == 0){
if (isBot) return false
else{
SpreadsheetApp.getUi().alert('Нет групп, которые нужно дополнить')
return false
}
}
// Цикл по ссылкам
for (var t = 0; t < Math.ceil(arr.toString().length/part); t++) {
var html = UrlFetchApp.fetch('https://api.vk.com/method/groups.getById.json?group_ids='+arr[t].toString()+'&fields=city,description&access_token='+access_token+'&v=5.107')
var json = JSON.parse(html).response
if(json){
for (var i = 0; i < json.length; i++) {
var id = json[i].id
var link = json[i].screen_name
var name = json[i].name
var description = (json[i].description)?json[i].description:''
var city = (json[i].city)?json[i].city.title:''
var rus = (isRus(name) != -1)?"A":"B"
// Скрипт можно попробовать заменить на двумерный массив к arr
for (var j = 1; j < data.length; j++) {
var nameCell = data[j][2].replace('https://vk.com/','').replace('vk.com/','').replace('^club','')
if (nameCell == link || nameCell == id){
var num = j+1
break
}
}
sheet.getRange(rus+num).setValue(name.replace('=',''))
sheet.getRange("C"+num).setValue('=HYPERLINK("https://vk.com/'+link+'";"'+link+'")')
sheet.getRange("D"+num).setValue(city)
sheet.getRange("E"+num).setValue(searchGenre(description))
sheet.getRange("F"+num).setValue(description)
}
}
}
sort()
return true
}
// Втянуть группы из закладок ВК
function getVkFave(isBot = false) {
var idTag
var getTags = UrlFetchApp.fetch('https://api.vk.com/method/fave.getTags.json?access_token='+access_token+'&v=5.107')
var res = JSON.parse(getTags).response
var iTag = res.items
for (var i = 0; i < iTag.length; i++) {
if (iTag[i].name == faveTag) idTag = iTag[i].id
}
// Предупреждение
if (!isBot){
var ui = SpreadsheetApp.getUi()
var resp = ui.alert('После втягивания закладок с тегом "'+faveTag+'" они будут удалены из ВК. Продолжаем?', ui.ButtonSet.YES_NO)
}
var inside = function (){
var getPages = UrlFetchApp.fetch('https://api.vk.com/method/fave.getPages.json?tag_id='+idTag+'&fields=city,description&access_token='+access_token+'&v=5.107')
var iPage = JSON.parse(getPages).response.items
// Цикл по каждой закладке. Добавление в таблицу, удаление с ВК
for (var j = 0; j < iPage.length; j++) {
var gr = iPage[j].group
addInTableFromArray(gr)
numRows++
UrlFetchApp.fetch('https://api.vk.com/method/fave.removePage?group_id='+gr.id+'&access_token='+access_token+'&v=5.107')
Utilities.sleep(1000) // костыль
}
sort()
}
if (isBot) { // Костыль, потому что бот стопарится на ui
inside()
}else if(resp == ui.Button.YES) {
inside()
}
}
// Добавление строки в таблицу
function addInTableFromArray(arr, linkIn) {
if (linkIn){
var link = linkIn
}else{
var link = arr.screen_name
if (searchExists(link) != false) return false
}
var name = arr.name // Название
var description = (arr.description)?arr.description:'' //Описание
var city = (arr.city)?arr.city.title:'' // Город
// Если в тексте нет русских букв, значит пишем название в колонку "English"
var rus = (isRus(name) != -1)?"A":"B"
// Добавление строки в таблицу
sheet.getRange(rus+numRows).setValue(name.replace('=',''))
sheet.getRange("C"+numRows).setValue('=HYPERLINK("https://vk.com/'+link+'";"'+link+'")')
sheet.getRange("D"+numRows).setValue(city)
sheet.getRange("E"+numRows).setValue(searchGenre(description))
sheet.getRange("F"+numRows).setValue(description)
}
// Актуализировать ссылки. Единоразово.
function checkActualLink(){
for (var i = 1; i < data.length; i++) {
try {
var num = i+1
UrlFetchApp.fetch("https://vk.com/"+data[i][2])
sheet.getRange('C'+num).setBackgroundColor('')
}catch(err) {
var num = i+1
sheet.getRange('C'+num).setBackgroundColor('red')
}
}
}
// Статус URL в строке. Запуск добавлением кода в ячейку
function getStatusUrl(url){
var options = {
'muteHttpExceptions': true,
'followRedirects': false
}
var response = UrlFetchApp.fetch(url.trim(), options)
return response.getResponseCode()
}
//Поиск номера листа по имени
function getNumSheet(nameList){
var s = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(nameList)
if (s != null) {
return s.getIndex()-1
}else{
return false
}
}
//Удалить спецсимволы
function escapeHtml(text) {
return text
.replace(/&/g, "")
.replace(/</g, "")
.replace(/>/g, "");
}
// Меню
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('Меню специальное')
.addItem('Сортировка', 'sort')
.addItem('Втянуть закладки из ВК', 'getVkFave')
.addItem('Дозаполнить пустые', 'addInfo')
.addToUi();
}
// Краткий вызов логирования
function ll(t){
return Logger.log(t)
}
Бот.gs
var botApi = 'https://api.telegram.org/bot1123123:AAA/' // Токен Telegram бота
/* Команды:
/start - Начало работы бота
/sort - Сортировка
/getvkfave - Втянуть закладки из ВК
/addinfo - Дозаполнить пустые
/getall - Получить все группы
*/
function doPost(e){
var inp = JSON.parse(e.postData.contents)
// Логи
var sheet2 = ss.getSheets()[getNumSheet('Логи')]
sheet2.getRange('A1').setValue(inp)
var inpTxt = inp.message.text
var chatId = inp.message.chat.id
var link = inpTxt.replace('https://vk.com/','').replace('vk.com/','').replace('^club','')
// Команды бота
// start
if (inpTxt == '/start' || inpTxt == 'В начало'){
sendText(chatId, 'Бот заносит группы из ВК в Google таблицу.\n'+
'/start - Начало работы бота\n'+
'/sort - Сортировка\n'+
'/getvkfave - Втянуть закладки из ВК\n'+
'/addinfo - Дозаполнить пустые\n'+
'/getall - Получить все группы')
return true
}
// sort
if (inpTxt == '/sort' || inpTxt == 'Сортировка'){
sort()
sendText(chatId, 'Отсортировано')
return true
}
// getvkfave
if (inpTxt == '/getvkfave' || inpTxt == 'Втянуть закладки из ВК'){
getVkFave(true)
sendText(chatId, 'Втянуто')
return true
}
// addinfo
if (inpTxt == '/addinfo' || inpTxt == 'Дозаполнить пустые'){
if (addInfo(true)) sendText(chatId, 'Дозаполнено')
else sendText(chatId, 'Нет групп, которые нужно дополнить')
return true
}
// getall
if (inpTxt == '/getall' || inpTxt == 'Получить все группы'){
var arr = [[],[],[],[],[],[],[],[],[],[],['В начало']] // TODO доделать
var repeat = ''
var n = 0
var j = 0
for (var i = 1; i < data.length; i++) {
if (j == 4){ // Количество на одной строке
n++
j = 0
}
if (repeat != data[i][3]){
arr[n][j] = data[i][3]
if (data[i][3] == '') arr[n][j] = '---'
j++
}
repeat = data[i][3]
}
var key = JSON.stringify({keyboard:
arr,
resize_keyboard:true,
one_time_keyboard:true
});
sendText(chatId, 'Выберите город', key)
return true
}
// Город
if (isRus(link) != -1) {
var name = (inpTxt == '---')?'Не заполнен':inpTxt
for (var j = 1; j < data.length; j++) {
if (data[j][3] == inpTxt || inpTxt == '---') {
var str = 'Город '+name+'\n\n'
for (var i = 1; i < data.length; i++) {
if ((data[j][3] == data[i][3]) || (data[i][3] == '' && inpTxt == '---')) {
if (str.length >= 4000) { // Ограничение на кол-во символов в сообщении Telegram бота
sendText(chatId, str)
str = ''
}
str = str + '<a href="https://vk.com/' + data[i][2] + '">' + escapeHtml(data[i][0] + data[i][1]) + '</a> \n'
}
}
if (str != '') sendText(chatId, str)
return true
}
}
sendText(chatId, inpTxt + ' - это не муз.группа и не команда бота')
return true
}
// Проверка на валидность
if (getStatusUrl('https://vk.com/'+link) != 200 && getStatusUrl('https://vk.com/club'+link) != 200){
sendText(chatId, inpTxt+' - битая ссылка')
return true
}
var name = searchExists(link)
if (name != false){
sendText(chatId, 'Группа '+name+' уже есть')
return true
}else{
var html = UrlFetchApp.fetch('https://api.vk.com/method/groups.getById.json?group_id='+link+'&fields=city,description&access_token='+access_token+'&v=5.103')
var json = JSON.parse(html).response[0]
addInTableFromArray(json, link)
sort()
sendText(chatId, 'Группа '+json.name+' добавлена')
return true
}
}
//Отправить сообшение боту
function sendText(chatId, text, key = ''){
var payload = {
'method': 'sendMessage',
'chat_id': String(chatId),
'text': text,
'parse_mode': 'HTML',
'reply_markup': key,
'disable_web_page_preview': true,
'one_time_keyboard':true
}
var data = {
"method": "post",
"payload": payload
}
// для дебага комментируем UrlFetchApp.fetch и вставляем ll(text)
UrlFetchApp.fetch(botApi, data)
}
Подробнее разберём ниже.
Жмём "Опубликовать — Развернуть, как веб приложение".
Теперь у нас есть ссылка вида script.google.com/macros/s/AAA/exec, подставив к которой параметр «link», мы сможем занести группу ВК в Google Таблицу.
Юзерскрипты в браузере
Первый и основной способ занесения данных!
Открываем браузер Google Chrome или Mozilla Firefox и ставим плагин юзерскриптов Tampermonkey.
Ссылки для Хрома, для Мозиллы.
Изначально пользовался Greasemonkey, но пришлось перейти на Tampermonkey из-за кроссбраузерности.
У кого возникнут вопросы, почему параметры юзерскрипта начинаются с префикса GM_, а не TM_ — идея не моя, идём в документацию.
Вставляем скрипт vkGroupToGS, нажав на первую вкладку с плюсиком.
vkGroupToGS
// ==UserScript==
// @name vkGroupToGS
// @namespace https://vk.com/*
// @version 0.1
// @author You
// @match https://vk.com/*
// @grant GM_xmlhttpRequest
// ==/UserScript==
var url_first = 'https://script.google.com/macros/s/AAA/exec'
var url_short = document.location.href.replace("https://vk.com/", "")
var d = document.createElement('div')
var head = document.querySelector('.left_menu_nav_wrap')
d.setAttribute('id', 'send_group')
d.style.display = 'inline-block'
d.style.position = 'relative'
d.style.fontSize = '50pt'
d.style.cursor = 'pointer'
d.innerHTML = '+'
head.parentNode.appendChild(d)
d.onclick = function() {
setTimeout(function() {
GM_xmlhttpRequest({
method: 'GET',
url: url_first + '?link=' + url_short,
headers: {
'Accept': 'application/atom+xml,application/xml,text/xml'
},
onload: function(x) {
console.log(x.responseText)
if (/Ошибка/.test(x.responseText)) document.querySelector('#send_group').style.color = 'red'
else document.querySelector('#send_group').style.color = 'green'
}
})
}, 0)
};
«Файл — Сохранить».
Как видим, наш скрипт рисует плюсик (на что хватило фантазии) в левом блоке left_menu_nav_wrap.
По нажатию на плюсик выполняется GM_xmlhttpRequest из vkGroupToGS в наш скрипт Код.gs по ссылке Google Таблицы, приложенной выше (url_first).
Tampermonkey запросит у нас связать эти 2 скрипта правами, жмём либо "Всегда разрешать" либо "Всегда разрешать домену" для "script.google.com". Повторить при надобности для "script.googleusercontent.com".
Как только запрос успешно улетит, придёт колбэк из GM_xmlhttpRequest и окрасит нашу кнопку в зелёный цвет ? при удачном добавлении либо в красный ? если вернулась ошибка.
Плюс Tampermonkey — облачное хранение скриптов в Google (раньше не было) при наличии их плодовитости и своего воображения.
Вернёмся к Google Таблице.
Перед тем, как начать разбирать наш Код.gs я хочу поделиться документацией для разработки в ВК — vk.com/dev
Токен ВК
Получение ключа доступа
Выполнение запросов к API ВКонтакте
Пока научился только одним способом добывать токен — через самописные приложения.
Заходим сюда.
Платформа — "Standalone-приложение".
После создания всё, что нам надо — это ID приложения (ныне семизначный), пусть будет 1234567.
Но нам нужен токен.
Очень хорошо про токен написано в Знакомстве с API ВКонтакте.
Если не хотим читать, просто открываем ссылку, вставляя в client_id свой ID приложения.
Нас редиректит на страничку с надписью:
Пожалуйста, не копируйте данные из адресной строки для сторонних сайтов. Таким образом Вы можете потерять доступ к Вашему аккаунту.
И это верно, будьте очень аккуратны с токеном!
Токен копируем из адресной строки после access_token.
Сразу поделюсь, что с этим токеном можно запросы вида vk.com/dev/groups.search?params%5Bq%5D=%D0%BC%D1%83%D0%B7%D1%8B%D0%BA%D0%B0¶ms%5Btype%5D=page¶ms%5Bcountry_id%5D=1¶ms%5Bcity_id%5D=1¶ms%5Bfuture%5D=0¶ms%5Bmarket%5D=0¶ms%5Boffset%5D=0¶ms%5Bcount%5D=10¶ms%5Bv%5D=5.107
можно спокойно преобразовывать в ссылки api.vk.com/method/groups.search?q=%D0%BC%D1%83%D0%B7%D1%8B%D0%BA%D0%B0&type=page&country_id=1&city_id=1&future=0&market=0&offset=0&count=10&access_token=1111111111111&v=5.107 и получать информацию в любом приложении.
Я открыл маленький ящик пандоры, возможно это и не новость, но таким способом можно получить много полезного из ВК, автоматизировать множество процессов и сделать уйму удобняшек.
Конечно с определёнными ограничениями, допустим не больше 999 сущностей в ответе или 20 запросов в секунду и т.п.
Также кому-нибудь будет интересно поиграться с хранимками — в нашем приложении вкладка "Хранимые процедуры".
Там есть свои правила вытягивания информации, очень удобно для последовательных запросов, чтоб не делать множество циклов у себя в коде.
Следим за версиями!
С их изменениями может меняться функциональность методов. Ныне прикладываю примеры для версии 5.107.
Итак, токен у нас есть, переходим к…
Код.gs
doGet(e) — Принимаем входящие get запросы от бота, ВК и просто с браузера.
Входные параметры парсятся, как e.parameters['ВХОДНОЙ_ПАРАМЕТР']
Прилетает ссылка, проверяем, есть ли в таблице. Если нет, запрашиваем подробную информацию через groups.getById, парсим, добавляем.
! Внимание! Скрипт простой, так что будет проглатывать все прилетающие ссылки, так что будьте аккуратны даже введя 123 при проверке скрипта.
searchGenre — умный поиск стиля/жанра группы, из-за которого я и поставил метку «регулярные выражения» в статье.
Возможны улучшения.
Пока работает только на поиск последующей цепочки слов через слеш или дефис после слов "стиль или жанр или genre".
isRus — простая регулярка, исключающая русские буквы.
sort — сортировка. Поставил по умолчанию — 1. город, 2. название.
searchExists — простой поиск существующих групп через цикл.
addInfo — сделал скрипт для себя, оставил здесь, как бонус. Нужен если у вас есть список ссылок, но нет по ним данных.
Вставляем список в колонку «Ссылка», жмём "Дозаполнить пустые" в главном меню и остальные колонки дозаполняются из ВК.
Из-за ограничений на количество символов в UrlFetchApp.fetch и в количестве ссылок, помещаемый в адресную строку пришлось сделать цикл по 1000 символов.
getVkFave — ещё один бонус, возникший в голове во время написания статьи и которым я сам стал пользоваться.
Суть — заходим с телефона в приложение ВК, натыкаемся на группу, которую хотим сохранить, сохраняем её в закладках с заранее добавленной меткой "муз.группы" (глобальная переменная faveTag).
Как они накопятся, заходим в нашу Google Таблицу, вызываем меню, жмём "Втянуть закладки из ВК".
Скрипт с токеном лезет в api.vk.com/method/fave.getTags, парсит, добавляет в таблицу, предварительно предупредив об удалении из ВК (тут аккуратней с тестированием, всё удаляется подчистую с закладок с этим тегом).
getStatusUrl и checkActualLink — эксперимент, вылившийся в 2 скрипта.
Суть — проверять валидность и доступ до ссылок.
Потому что за годы накопления музыкальных групп я столкнулся с тем, что паблики удаляют либо переименовывают.
Кто будет пользоваться моей удобняшкой, советую некое TODO, которое будет не просто удалять невалидные ссылки, но и проверять через groups.search по названию наличие в ВК.
checkActualLink — единоразово проверяет валидность, окрашивая в красный невалидные.
- Минус — низкая скорость.
- Плюс — выполнять можно, когда захочется.
getStatusUrl — вызывается в ячейке через скрипт
=getStatusUrl(CONCAT("https://vk.com/";C2))
Основу взял отсюда.
- Минус — запускается для всех ячеек при каждом открытии страницы.
- Плюс — отрабатывает параллельно для всех ячеек, то есть скорость в разы выше, чем у checkActualLink.
Далее идут удобняшки для гуглоскриптов:
addInTableFromArray — Добавление строки в таблицу. Либо с цикла либо единоразово.
getNumSheet — Поиск номера листа по имени. Честно, не нашёл в интернете способа проще, чем этот. Нужен из-за смещения номеров листков при добавлении новых. Основу взял отсюда.
escapeHtml — Удалить спецсимволы
ll — для себя сделал Logger.log кратким, сокращает время, советую.
onOpen — Всем знакомая функция для отрисовки меню в Google Таблице.
Итак, наш Код.gs ловит ссылки с ВК через Tampermonkey и добавляет группы в Google Таблицу.
Включаем лень
- Мы сидим за компьютером.
- Мы сидим в телефоне.
С компьютера мы научились добавлять группы через "плюсик" в ВК и через закладки.
Через телефон только через закладки в приложении ВК.
Разберём ещё два варианта внесения групп.
USI
Юзерскрипты в мобильном браузере.
Кому-то он покажется старомодным и ненужным, но найдутся те люди, которые вынесут плюсы из него.
Поискав в интернете нашёл пока только такой плагин.
Работает только в Firefox (тестировал только в android).
Огромный плюс — можно использовать скрипт Tampermonkey.
Из-за отличий вёрстки между vk.com и m.vk.com, а также из-за открывания окна по умолчанию в m.vk.com я не стал рисовать плюсик, а сделал простой скрипт VK_event_to_list_mvk, срабатывающий при открытии окна (гореть мне в аду за setTimeout, знаю).
Код.gs
// ==UserScript==
// @name VK_event_to_list_mvk
// @namespace https://m.vk.com/*
// @match https://m.vk.com/*
// @grant GM_xmlhttpRequest
// ==/UserScript==
setTimeout(function() {
var url_first = 'https://script.google.com/macros/s/111/exec';
var url_short = document.location.href.replace("https://m.vk.com/", "");
GM_xmlhttpRequest({
method: 'GET',
url: url_first + '?link=' + url_short,
headers: {
'Accept': 'application/atom+xml,application/xml,text/xml'
},
onload: function(x) {
alert(x.responseText);
}
});
}, 2000);
Telegram Бот
Создаём бота по инструкции.
Нам нужен токен, пусть он будет 123123.
Создаём скрипт Бот.gs, вставляем в переменную botApi наш 123123, обрамляя кавычками и ссылкой api.telegram.org.
Далее надо сцепить нашего бота с Google Таблицей. Посмотреть, как это сделать через WebHook можно в предыдущей статье.
Что будет делать наш бот?
Разложим по командам:
- /start — Начало работы бота
- /sort — Сортировка
- /getvkfave — Втянуть закладки из ВК
- /addinfo — Дозаполнить пустые
- /getall — Получить все группы
Видим, что у нас есть начало работы бота, туда мы вставим просто tutorial с описательной частью на ваше усмотрение.
Три команды из главного меню на всякий случай.
И очередной бонус — /getall, о нём ниже.
Команды для удобного вызова (в строке ввода сообщения написать слеш) можно добавить в том же @BotFather через /setcommands.
Формат — строки через тире:
start - Вернуться в начало
sort - Сортировка
getvkfave - Втянуть закладки из ВК
addinfo - Дозаполнить пустые
getall - Получить все группы
Бот.gs
doPost — принимает входящие сообщения от бота.
Естественно, как пытливый программист, я сразу начал использовать логи. Возможно есть способы проще, но я просто создал новый лист, назвал его «Логи» и ловлю в первую ячейку входящие запросы.
Поэтому после того, как мы распарсили входящее сообщение через JSON.parse(e.postData.contents), сразу пишем в логи.
Далее определяем, какая из команд нам пришла.
Если не команда, проверяем на валидность, то есть существует ли группа в ВК.
sendText — отправляет сообщения боту (по умолчанию parse_mode = HTML), документация.
/getall — разберём отдельно.
Пусть вас не пугает моё решение из
[[],[],[],[],[],[],[],[],[],[],['В начало']]
Сделано для заполнения строчек городами, пока ограничимся 8-10 строчками по 4 города (переменная j = 4).
Если музыкальная группа без города, вставляем ---.
Просто выбираем город и ловим русские буквы в Бот.gs.
Выводим список ссылок, ничего более.
Помним, что доступ к документации и тестированию бота через адресную строку через Hotspot Shield Free VPN Proxy — Unlimited VPN (для Google Chrome).
Вот мы и изучили три способа внесения в нашу таблицу:
- Юзерскрипты в браузере
- Юзерскрипты в мобильном браузере (USI)
- Telegram Бот
Что нужно знать и помнить?
Префикс club в ссылках ВК
В группах ВК есть 2 типа хранения идентификатора.
- Название на английском с допустимыми подчёркиваниями и дефисами.
- ID
Причём выкладываться могут и так и так.
Проверить просто.
Если есть группа, допустим vk.com/4soulsband, подставляем 4soulsband в group_ids в vk.com/dev/groups.getById, выводится перечень параметров, один из которых ID. И если мы перед ним поставим club, вуаля, попадаем на ту же страницу vk.com/club68130764.
Ограничение на хранение групп в ВК до 5000
Кажется смешным, но мне не хватило.
Моя боль с обновлением страницы ВК
Каюсь, пока не смог победить отлов перехода между страницами в ВК для сброса состояния плюсика в Tampermonkey. TODO
Использование Google Таблиц, как БД
- Плюс — доступность
- Минус — когда копится больше 5000 строк, работа с "БД" замедляется. Так что для серьёзных проектов лучше использовать нормальные СУБД.
Определение города группы
Адрес группы в ВК может быть тут vk.com/dev/groups.getAddresses, соответственно кто захочет улучшить поиск города для группы, может использовать его.
Итог
У нас есть:
- Google таблица для сбора данных, с которой мы научились работать.
- Знания по VK API. То есть вы уже в курсе, что мы можем не ограничиваться музыкальными группами.
- Telegram Бот с БД, которую не нужно разворачивать и настраивать.
Моя цель не кичиться очередной созданной игрушкой, моя цель показать новичкам (и не только) огромный пласт открытых API`шек, которые помогут облегчить жизнь в той или иной степени, перенестись с железной земли в «облака», так сказать.
Данный функционал готов, как и открыт для улучшений.
Удачи в разработке и постижении новых знаний.
Документация
Google Apps Script — Spreadsheet Service
Telegram Bot API
VK API
Учимся писать userscript'ы
Userscripts. Углубляемся
Мониторинг сайта с помощью Google Docs
Баловство. Пишем Telegram бота на Google script
[Примеры, Google Apps Script] Разработка дополнений/скриптов для Google Таблиц (spreadsheets)
How to use google spreadsheets to check for broken links