Для того, чтобы научить 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 файла, то увидим авторизационные данные:

{
  "type": "service_account",
  "project_id": "glвп-seавпвап1217",
  "private_key_id": "8выаывп7e58ebefавпавп9842470ff2828d8",
  "private_key": "-----BEGIN PRIVATE KEY-----тут длинный ключ=\n-----END PRIVATE KEY-----\n",
  "client_email": "rer@glassываываenceвыавыа1217.iam.gserviceaccount.com",
  "client_id": "105ывавыар0222224603",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://accounts.google.com/o/oauth2/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/rer%40glываывапр217.iam.gserviceaccount.com"
}

Последним шагом переименовываем файл в более удобное название 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 = '<b>События на ближайшие сутки:</b>\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 = '<a href="/%s">Подробнее...</a>'%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 = '<b>События на ближайшие сутки:</b>\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 = '<a href="/%s">Подробнее...</a>'%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 и отправляющий его в нужный чат.