Google Календари
Мы сделали очень простую в настройке систему бронирования для тех, кто не использует CRM. Она подходит как для индивидуальных исполнителей, так и для бизнеса с небольшим количеством графиков занятости (то есть сотрудников или ресурсов, если речь идет об аренде). Создайте чат-бота для работы с бронированием через календарь.
Настройка
Шаг 1. Создание Google Календарей
- Перейдите по ссылке https://calendar.google.com/ и выполните вход в тот аккаунт, который будет содержать будущие расписания.
- Перейдите в настройки (значок шестеренки в правом верхнем углу)
- Разверните «Добавить календарь» и выберите «Создать календарь»
- Введите название будущего календаря (имена сотрудников, номера помещений). Если планируется только одно расписание (календарь), то укажите, например «Рабочий».
- Обновите страницу и выберите слева созданный календарь:
- В пункте «Автоматически принимать приглашения» выставите «Автоматически принимать не конфликтующие между собой приглашения»
- В «Доступе для отдельных пользователей» откройте доступ «info@bot-manager.com» с разрешениями «Внесение изменений в мероприятия»

- В «Другие уведомления» поставьте, чтобы уведомления о новых и отмененных мероприятиях приходили на Email

- Скопируйте «Идентификатор календаря» в разделе «Интеграция календаря»
- В BotManager в разделе «Онлайн запись» в пункте «Календари» нажмите «Добавить календарь», вставьте скопированный идентификатор в появившуюся строку, поставьте галку «Активен» и нажмите на иконку подтверждения слева.

Если Вы выполнили все пункты, то сразу после сохранения в поле «Название» должно появиться название Google календаря – расписание подключено!
Шаг 2. Выставление рабочего времени
Перейдите в Google Календари. Для удобства работы советуем скрыть календари, не относящиеся к работе:

Предположим, что Ваш график работы следующий: вт-вс с 10:00 до 20:00, пн – выходной.

- Переключите вид отображения на недельный (в правом верхнем углгу).
- Создайте мероприятие, начинающееся сегодня в 20:00 (для этого просто кликните в нужную точку).
- Укажите название, например «Не рабочее» или «-».
- Время окончания выставите «10:00».
- Вместо «Не повторять» поставьте «Ежедневно».
- По умолчанию данное мероприятие относится только к одному календарю. Если у их Вас несколько и Вы хотите применить это же нерабочее время, то в поле «Добавить гостей» вставьте идентификаторы остальных интересующих календарей (можете скопировать их из BotManager). После сохранения нажмите «Не отправлять» приглашение. (сохранение мероприятий может занять около минуты)
- Повторите действия выше, чтобы сделать каждое воскресенье нерабочим, а также, по желанию, измените цвет всех мероприятий на серый. В итоге должно получиться вот так:

Шаг 3. Настройка оказываемых услуг
В панели BotManager в разделе «Онлайн запись» вы можете создавать категории услуг и непосредственно услуги.
Для примера мы создали категорию «Маникюр». Чтобы добавить услуги:
- Выберите интересующую категорию услуг
- Укажите название и длительность (остальные поля необязательные)
- Нажмите иконку подтверждения слева

- После сохранения нажмите «Настроить» и выберите те календари, которые оказывают данную услугу, и нажмите сохранить:

Можете уже переходить к последнему шагу. Стоит упомянуть о дополнительных настройках:
Поле «Доступное количество» в услуге
Если Вы укажите целые числа через запятую, то пользователю будет доступен выбор продолжительности услуги. Например, если длительность услуги «Аренда катера» выставить 30 минут, то при указании «2,3,4» пользователь будет выбирать между «60 мин», «90 мин» и «120 мин».
Поле «Любой календарь» в услуге
Если отметить это поле, то после выбора услуги не будет происходить выбор специалиста (помещения и тд). Вместо этого пользователь сразу перейдет к выбору даты и времени. Удобно для сферы аренды однотипных объектов.
Поле «Текст кнопки с выбором календарей» в разделе «Прочие настройки»
Если Вы укажете какой-либо текст, например, «Специалисты», то пользователь сможет начать бронирование не только с выбора услуги, но и с выбора специалиста. Удобно для сфер, где важно, кто оказывает услуги.
Поле «Текст запроса доп. информации» в разделе «Прочие настройки»
Если хотите запрашивать информацию от клиента, то укажите любой текст, и он отобразится перед завершением бронирования с запросом ввода любого текста. Номер телефона будет запрашиваться в любом случае, так что используйте поле для других целей.
Поле «Интервал между слотами» в разделе «Прочие настройки»
По умолчанию, доступные слоты будут отображаться с интервалом в 30 минут. Вы можете изменить это значение (но учтите, что если выставить 5-10 минут, то пользователю будет не так удобно найти желаемое время).
Поле «Бронирование нескольких категорий услуг» в разделе «Прочие настройки»
Если отметить это поле, то пользователь сможет выбрать несколько категорий услуг, а затем последовательно услуги в каждой категории.
Поле «Бронирование нескольких услуг» в разделе «Прочие настройки»
Если отметить это поле, то пользователь сможет выбрать несколько услуг внутри одной категории, продолжительность которых будет складываться. Однако все выбранные пользователем услуги должны оказываться одним сотрудником, иначе программа не сможет найти свободное время.
Поле «Разрешить повтор последней брони» в разделе «Прочие настройки»
После оформления бронирования пользователю становится доступна кнопка «Отмена бронирования». Если разрешить повтор брони, то пользователь также увидит кнопки быстрого повтора, чтобы сразу перейти к выбору даты и времени.
Поле «№ филиала» в разделе «Календари»
Если у Вас несколько филиалов, но Вы хотите сделать запись в одном боте, то укажите разные числа в этом поле для календарей из разных филиалов и затем используйте эти числа в Шаге 4 . Так Вам не придется заново настраивать услуги для каждого филиала.
Шаг 4. Настройка дерева экранов
- Перейдите в раздел «Боты» и нажмите кнопку «Импортировать шаблон» для выбранного.
- Выберите категорию «Интеграции», затем «Онлайн запись в Google Календарях».
- Нажмите «Выполнить импорт».

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

Если Вы хотите переправить пользователя на какой-либо свой экран после успешного бронирования, то добавьте самостоятельно кнопку с названием «Забронировано» в экраны «Онлайн запись 1» и «Онлайн запись 2». Затем в настройках этих кнопок в поле «Выполнить действия после нажатия» укажите «[ВыходИзОнлайнЗаписи]». Соедините эти кнопки с желаемым экраном, и система переведет пользователя на него после оформления бронирования.
Все готово! Теперь пользователи могут записываться к Вам, а также будут получать уведомления за день и за два часа до бронирования.
Уведомления из гугл календаря в телеграм
Некоторое время назад я столкнулась с проблемой ведения двух календарей: личного и рабочего. Точнее само ведение (планирование встреч, переносы, редактирование и т.п.) проблемы не вызывало, но я осознала, что в ежедневной суете у меня совершенно нет возможности / желания несколько раз залезать в календарь и проверять предстоящие встречи.
Естественно, я объединила оба календаря, дала права доступа через оба аккаунта на возможность видеть встречи друг друга, с учетом того, чтобы мой личный календарь не был виден аккаунтам, которые просматривают рабочий календарь, то бишь коллегам, заказчикам и прочим.
Но все равно я периодически пропускала уведомления о начале какого-либо события.
В общем, я пришла к тому, что мне лично очень удобно все делать через телеграм и было бы неплохо иметь бота, который присылал бы мне сообщение со ссылкой на встречу за 5-10 минут. Для меня лично это стало лучшим решением, которым я пользуюсь несколько месяцев и до сих пор не вижу дроубеков вообще.
Таким образом, я продолжаю пользоваться календарем: в личный календарь я вношу конфы и митинги по личным интересам, плюс онлайн-уроки; в рабочем планы строят обычно коллеги, что позволяет мне вообще его не открывать. Телеграм бот в свою очередь присылает полный план встреч на день и каждую отдельную встречу непосредственно перед началом из обоих календарей.
Реализация
Первым делом нужно обозначить видимость для обоих календарей. Для этого просто подписываемся на каждый из календарей из обоих аккаунтов по кнопке Добавить другой календарь

Далее выбираем Подписаться на календарь и вводим почту, на кого подписываемся. Я не буду подробно здесь останавливаться, возможно нужны дополнительные пермишены и подтверждения, но я уверена, что вы справитесь.
Как только видимость из обоих календарей настроена можно переходить к написанию кода. Моменты с созданием бота и проектом в App Script уже были обговорены здесь, поэтому сразу перейду к ключевым функциям.
Получение всех событий из календаря
В App Script есть класс CalendarApp , в котором мы используем метод getCalendarById(calendar_id) , calendar_id — это ваш gmail login.
Таким образом я сохраняю оба календаря в соответствующие переменные:
const calendar_ct = CalendarApp.getCalendarById(gmail_login_1) const calendar_ns = CalendarApp.getCalendarById(gmail_login_2)
Далее отфильтрую все события по дате, я хочу получить планы только на сегодня и использую метод getEventsForDay(date) . Также записываю их в переменные
const now = new Date(); const events_ct = calendar_ct.getEventsForDay(now); const events_ns = calendar_ns.getEventsForDay(now);
Полученные массивы просто объединяем в один методом concat() . При обращении к элементу массива мы увидим в консоли CalendarEvent . Чтобы вычленить подробности из CalendarEvent , нужно обратиться к каждому элементу и забрать нужную инфу, например, название, время начала, описание.
Для данного набора запрашиваемой информации мне нужно использовать методы getDescription(), getTitle(), getStartTime().
Помимо получения деталей события я их преобразую в нужный мне вид. Например, getStartTime() возвращает мне дату целиком и возвращаемое время не соответствует моему часовому поясу. Учитывая, что я хочу получить только время вида чч:мм, я преобразую время следующим образом
start_time = new Date(start_time.getTime() + (6 * 60 * 60 * 1000)) //6 - разница во времени между NY и Berlin
С описанием из события в календаре тоже не все так просто. При выводе в консоль я получаю не текст или ссылку в виде текста, как я ожидала, а текст и ссылки заключенные в теги.

И проблема заключается в отправке в телеграм. Чтобы все обработалось гладко и сообщение выглядело примерно следующим образом

я применила замену (я просто остановилась на решении, которое работает, не вдаваясь в детали) :
const regExp = /<(?!\/?a>|\/?a href|\/?br)[^>]+>/g; descr = descr.replace(regExp,'').replace(/
/g,`\n`).replace(/ /g,' ');
Все выбранные и преобразованные данные я вставила в новый массив для дальнейшей работы уже с ним. Вся функция целиком ниже.
/** * The function gets all events for today from 2 calendars and sorts them * * @return events_details_arr all sorted events for today */ function getEvents() < const calendar_ct = CalendarApp.getCalendarById(gmail_login_1) const calendar_ns = CalendarApp.getCalendarById(gmail_login_2) const now = new Date(); const events_ct = calendar_ct.getEventsForDay(now); const events_ns = calendar_ns.getEventsForDay(now); const events = events_ct.concat(events_ns); const regExp = /<(?!\/?a>|\/?a href|\/?br)[^>]+>/g; const events_details_arr = new Array(); events.forEach((el) => < let descr = el.getDescription().toString(); const title = el.getTitle(); let start_time = el.getStartTime(); start_time = new Date(start_time.getTime() + (6 * 60 * 60 * 1000)) //6 - разница во времени между NY и Berlin if (descr === '') < events_details_arr.push([title,start_time]); >else < descr = descr.replace(regExp,'').replace(/
/g,`\n`).replace(/ /g,' '); events_details_arr.push([title,start_time,`\n$`]); > >) bubble_sort(events_details_arr) return events_details_arr >
Да, здесь же я сортирую события по возрастанию времени их старта.
Отдельно я написала функцию, которая преобразует время в удобоваримый вид
/** * The function transforms the date into a string * * @param date The date to be transformed * @return time_str the time in the string form */ function time_to_string(date) < let h = new String(date.getHours()); let m = new String(date.getMinutes()); if (m.length == 1) < m = `0$` > const time_str = ` $:$`; return time_str >
Отправка в телеграм
Наконец, функции отправки в чат с ботом. Я разделила отправку на 2 отдельные функции: 1-я отправляет одно событие в чат за 5-8 минут до начала, 2-я отправляет все события списком в одном сообщении по порядку следования.
function send_next_event() < const today_events_arr = getEvents(); const cur_time = new Date(new Date().getTime() + (6 * 60 * 60 * 1000)) //6 - разница во времени между NY и Berlin let msg = new String(); for (i=0; i0.1) < today_events_arr[i][1] = time_to_string(today_events_arr[i][1]); if (msg == "") < msg = `$` > else < msg = `$\n$` > send(msg,chat_id_root,API); > > >
Функция send_next_event() проверяет разницу между текущим временем и стартовым временем каждого из событий в массиве и отправляет сообщение в чат ( send(msg,chat_id_root,API) ) примерно за 5 минут.
Понимаю, здесь очень странные манипуляции со значением времени, но вот так это выглядит в js. Я сама еще в этом плаваю, потому не хочу объяснять все детали, т.к. сама искала ответы онлайн касаемо этой части. В целом, идея была преобразовать время из 1.65286722566E12 во время вида 13:00 .
Функция для отправки всех событий представлена ниже.
function send_all_events() < const today_events_arr = getEvents(); let msg = new String(); if (today_events_arr.length !== 0) < today_events_arr.forEach((el,ind) => < el[1] = time_to_string(el[1]) if (msg == "") < msg = `$$` > else < msg = `$\n$ $` > >) send(msg, chat_id_root, API); > else < send('В календаре нет планов', chat_id_root, API); >>
Функция активируется по триггеру каждое утро, а также я могу запустить ее по команде из бота.



Слева на скрине примеры сообщений, которые я получаю от бота в течение дня. При этом описание в календаре есть только у второго события.
Заключение
Вот такой Reminder bot уровня минимум спасает меня каждый день. Как обычно буду рада вашим комментам и вопросам. Весь код целиком есть на гите, который я вот только начала вести (https://github.com/Nadezhda95).
Всем добра и нет войне.
Epical — Telegram бот для Google календаря
В какой-то момент я понял, что настраивать уведомления о событиях в календаре через системные уведомления не очень удобно: телефоны меняются, ноутбуки ломаются, появляются новые девайсы.
5.4K открытий
Особенно неприятно, если аккаунтов с календарями и устройств много — постоянно что-то забываешь подключить или что-то отваливается.
Гораздо удобнее подключить уведомления в Телеграм и получить их сразу на всех устройствах. Существующего решения я не нашел, так что пришлось написать свое.
Что умеет бот:
1. Уведомлять о предстоящем событии за 1 минуту, 5, 10 или 15 минут
2. Работать с несколькими аккаунтами
3. Работать с несколькими календарями в каждом аккаунте
4. Гибко отслеживать состояние события. Например, если у вас установлено уведомление за 1 минуту, а событие удалено за 2, бот это поймет.
5. Присылать кнопку для подключения к онлайн-митингу и данные об организаторе события.
Как это выглядит?
Вот пример, как оно будет работать в чате:
А сколько стоит?
Бесплатно, рекламы нет, данные не продаются. Возможно, через какое-то время появятся платные фишки типа создания онлайн-митинга через бот или аналитика по времени, проведенному на встречах, но я пока об этом не задумывался, однако, запросы на новые фичи уже можно оставлять.
Как к телеграм боту подключить гугл календарь

Интеграция telegram бота на python с google calendar
- Python
- telegram
- calendar
Для того, чтобы научить telegram бота отправлять сообщения о событиях из google calendar (добавлять новые, редактировать старые, следить за обновлениями и т.д.) необходимо связать его с календарём, который мы хотим отслеживать.
В одной из прошлых статей мы создали бота на python с использованием библиотеки telepot, теперь научим его общаться с Google Calendar Api.
Подготовка:
На официальном сайте google находим раздел с описанием условий, необходимых для установки python библиотеки google-api-python-client. Кстати, там же можно найти мануал для других языков программирования. Так как документация постоянно обновляется, вполне вероятно, что у вас будет уже модифицированная версия описания, но на момент написания статьи условия такие:
- Python2.6 и выше
- Установленный pip для python
- Доступ в интернет и любой веб-браузер
- Аккаунт в google
Подключение Google API:
Если все перечисленное в наличии — можно приступать. Включаем Calendar API в консоли вашего google аккаунта по ссылке.
Выбираем «Создать проект» и нажимаем «Продолжить»:

Затем «Создать учетные данные»:

Дальше гугл в своем мануале рекомендует нажать «Отмена» на экране «Добавление учетных данных» и вместо этого перейти на вкладку «Окно запроса доступа OAuth». Кто мы такие чтобы спорить с разработчиком? И все же поспорим 🙂 Здесь наши пути с мануалом разойдутся, потому что там написано как запускать авторизацию через запрос у пользователя данных о его учетной записи Google. Это когда в интернете какое-то приложение просит разрешить использовать данные вашего аккаунта, вы наверняка не раз встречали такое.
В нашем случае такая авторизация не подходит, так как во-первых, авторизовываться будет бот, а не человек и во-вторых, код бота у нас будет работать на сервере, где и браузера то может не быть чтобы в нем всплыло окошко с просьбой авторизоваться.
Поэтому мы пойдем своим путем. Шаг с добавлением учетных данных не игнорируем. Выбираем что вызов будет происходить с веб-сервера, обращаться мы будем к данным приложения и сервис Google App Engine мы не используем. После этого нажимаем «Выбрать тип учетных данных»:

Далее создаем сервисный аккаунт. Это как раз то, что нам нужно для того чтобы к календарю подключался бот, а не человек. Задаем название аккаунта и выбираем для него роль «Администратор App Engine»:

Ставим галку напротив пункта «Создать новый закрытый ключ», выбираем тип ключа «Json» и жмем «Создать»:

Произойдет генерация и скачивание файла с закрытым ключом.
Если открыть и посмотреть что внутри скачанного json файла, то увидим авторизационные данные:
Последним шагом переименовываем файл в более удобное название client_secret.json и переносим его в папку где лежит (или будет лежать) наш бот.
С настройкой Google API все.
Подключение библиотек Python:
Для того, чтобы наш бот на Python смог работать с календарем, потребуется установить две библиотеки.
1. google-api-python-client. Это библиотека Google для взаимодействия календаря и python. Ставится через команду:
pip install --upgrade google-api-python-client
Если у вас стоит несколько разных версий python, не забудьте указать версию pip под которым будет выполняться установка. В случае успеха вы увидите что-то вроде этого:

2. shedule. Эта библиотека позволит нашему python боту через заданные промежутки проверять Google Calendar и отправлять сообщения в telegram. Описание модуля можно посмотреть здесь. Ставится командой:
pip install schedule
При успешной установке на экране будет примерно то же, что мы видели в п.1 🙂
Пишем telegram бота на Python, проверяющего Google Calendar и информирующего пользователей по событиям на ближайшие сутки.
На странице с описанием Calendar API есть код python, на котором Google предлагает выполнить проверку того, что все работает корректно. Не будем изобретать велосипед, возьмем этот код за основу и доработаем под наши нужды.
Во-первых, создадим конфиг файл config.py в который положим токен нашего бота (см.предыдущую статью) и путь к Json файлу с ключом, который мы скачали выше.
#Это конфиг файл config.py с данными для интеграционного взаимодейстия # -*- coding: utf-8-*- TOKEN = 'код вашего токена в telegram, полученный от botfather' client_secret_calendar='/root/bot/cal_py/client_secret.json' #указываем путь к скачанному Json
Дальше импортируем конфиг и библиотеки time, telepot, shedule. Из проверочного кода google так же оставляем несколько библиотек. Остальные удаляем.
from __future__ import print_function import httplib2 import datetime import time import config import telepot import schedule from apiclient import discovery from oauth2client.service_account import ServiceAccountCredentials
Полностью удаляем функцию get_credentials(). В коде google она служит для того, чтобы запрашивать авторизационные данные у пользователя, мы же будем напрямую обращаться к Json файлу. Поэтому в функции main() переменная credentials перестанет выглядеть как credentials = get_credentials(), а модернизируется до credentials = ServiceAccountCredentials.from_json_keyfile_name(config.client_secret_calendar, ‘https://www.googleapis.com/auth/calendar.readonly’)
Так как мы будем следить за событиями на ближайшие сутки, вычисляем даты «от» и «до» за которые будут подбираться события из календаря:
now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time now_1day = round(time.time())+86400 #плюс сутки now_1day = datetime.datetime.fromtimestamp(now_1day).isoformat() + 'Z'
Дальше задаем параметры запроса к календарю. Указываем id вашего google календаря, даты за которые будем смотреть события, максимальное количество выводимых результатов и так далее. Полный перечень возможных параметров можно посмотреть здесь.
eventsResult = service.events().list( calendarId='id google календаря который вы хотите отслеживать', timeMin=now, timeMax=now_1day, maxResults=100, singleEvents=True, orderBy='startTime').execute() events = eventsResult.get('items', [])
Потом указываем что именно нужно взять из ответа Google Calendar (заголовок события, текст, дату, ссылку на календарь и т.д. полный перечень возможных параметров можно посмотреть здесь). Записываем полученные данные в сообщение и отправляем его в telegram с помощью sendMessage:
if not events: print('нет событий на ближайшие сутки') bot.sendMessage(сюда нужно подставить идентификатор вашего чата telegram, 'нет событий на ближайшие сутки') else: msg = 'События на ближайшие сутки:\n' for event in events: start = event['start'].get('dateTime', event['start'].get('date')) print(start,' ', event['summary']) if not event['description']: print('нет описания') ev_desc = 'нет описания' else: print(event['description']) ev_desc = event['description'] ev_title = event['summary'] cal_link = 'Подробнее. '%event['htmlLink'] ev_start = event['start'].get('dateTime') print (cal_link) msg = msg+'%s\n%s\n%s\n%s\n\n'%(ev_title, ev_start, ev_desc, cal_link) print('===================================================================') bot.sendMessage(сюда нужно подставить идентификатор вашего чата telegram, msg, parse_mode='HTML')
Оборачиваем все что получилось в функцию job() и вызываем её по расписанию с помощью функций библиотеки shedule. Итоговый код:
from __future__ import print_function import httplib2 import datetime import time import config import telepot import schedule from apiclient import discovery from oauth2client.service_account import ServiceAccountCredentials def job(): print("I'm working. ") bot = telepot.Bot(config.TOKEN) def main(): credentials = ServiceAccountCredentials.from_json_keyfile_name(config.client_secret_calendar, 'https://www.googleapis.com/auth/calendar.readonly') http = credentials.authorize(httplib2.Http()) service = discovery.build('calendar', 'v3', http=http) now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time now_1day = round(time.time())+86400 #плюс сутки now_1day = datetime.datetime.fromtimestamp(now_1day).isoformat() + 'Z' print('Берем 100 событий') eventsResult = service.events().list( calendarId=Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в вашем браузере должен быть включен Javascript.', timeMin=now, timeMax=now_1day, maxResults=100, singleEvents=True, orderBy='startTime').execute() events = eventsResult.get('items', []) if not events: print('нет событий на ближайшие сутки') bot.sendMessage(сюда нужно подставить идентификатор вашего чата telegram, 'нет событий на ближайшие сутки') else: msg = 'События на ближайшие сутки:\n' for event in events: start = event['start'].get('dateTime', event['start'].get('date')) print(start,' ', event['summary']) if not event['description']: print('нет описания') ev_desc = 'нет описания' else: print(event['description']) ev_desc = event['description'] ev_title = event['summary'] cal_link = 'Подробнее. '%event['htmlLink'] ev_start = event['start'].get('dateTime') print (cal_link) msg = msg+'%s\n%s\n%s\n%s\n\n'%(ev_title, ev_start, ev_desc, cal_link) print('===================================================================') bot.sendMessage(сюда нужно подставить идентификатор вашего чата telegram, msg, parse_mode='HTML') if __name__ == '__main__': main() print('Listening . ') #schedule.every(1).minutes.do(job) #schedule.every().hour.do(job) schedule.every().day.at("11:15").do(job) #schedule.every().monday.do(job) #schedule.every().wednesday.at("13:15").do(job) while True: schedule.run_pending() time.sleep(1)
Запускаем и получаем процесс, по которому каждый день в 11:15 вызывается код, обращающийся к Google calendar, проверяющий какие события произойдут в ближайшие сутки, собирающий эти события в сообщение telegram и отправляющий его в нужный чат.
|
