Куда только уже этот ChatGPT не прикрутили с момента его появления. Возможно я изобрету велосипед, но мне показалось удобным сделать бота в Телеграм, который бы поддерживал голосовой ввод и управлял моим календарем в Google.
Итак, что мы будем делать?
Создадим нейро-сотрудника на моей платформе
Зарегистрируем Телеграм-бот и подключим его к нашему сотруднику
Напишем API для интеграция с Google календарем
Дадим функцию нашему сотруднику для работы с API
Шаг №1: Создаем нейро-сотрудника
Дабы не нарушать правила хабра я не буду писать название платформы по созданию нейро-сотрудников и дам только сам системный промпт на базе которого будет работать наш бот:
Цель:
Помогать пользователю вести его Google календарь.
Роль:йр
Ты - женщина.
Тебя зовут - Жанна
Ты работаешь в должности - Личный секретарь
Твоя задача помогать управлять календарем Google используя функцию "google_calendar".
Поведение:
Для твоей работы необходим ID календаря, который ты будешь передавать в функцию "google_calendar". ID календаря может быть предоставлен и в виде Email адреса. Без него ты не можешь отвечать ни на какие вопросы пользователя.
При показе запланированных событий после названия события указывай его "event_id".
Перед удалением событий запроси согласие пользователя на удаление событий и после подтверждения запусти "google_calendar" на удаление событий по интервалу дат или по списку "event_id".
Не используй в ответах никакие эмоджи, кроме: "✅" и "?️".
Возможно пока не очень ясны некоторые инструкции в промпте, но позже вы все поймете, а пока важно отметить что промпт очень небольшой и по сути всем полностью будет управлять интеллект ChatGPT.
Шаг №2: Регистрируем Telegram-bot и прикручиваем его к нашему нейро-сотруднику
Тут все очень просто:
Зайдите в вашем Телеграме в бот @BotFather
Выберите в меню бота /newbot и следуйте указаниям
После регистрации бота скопируйте токен бота, он нужен для интеграции с нашим нейро-сотрудником
Шаг №3: Напишем API для интеграция с Google календарем
Для получения необходимого файла с ключами 'credentials.json' доступа прочитайте статью: «Настройка синхронизации google calendar с web приложением».
Вот класс, описывающий интеграцию с Google Calendar API:
from __future__ import print_function
import datetime
import googleapiclient
from google.oauth2 import service_account
from googleapiclient.discovery import build
import uuid
import json
import codecs
class GoogleCalendar(object):
SCOPES = ['<https://www.googleapis.com/auth/calendar>']
CREDS_FILE = 'credentials.json'
def __init__(self, calendarId):
credentials = service_account.Credentials.from_service_account_file(CREDS_FILE, scopes=GoogleCalendar.SCOPES)
self.service = googleapiclient.discovery.build('calendar', 'v3', credentials=credentials)
self.calendarId = calendarId
def create_event(self, event):
e = self.service.events().insert(calendarId=self.calendarId,
body=event).execute()
print('Event created: %s' % (e.get('id')))
return e
def get_events_list(self, start_date=None, end_date=None):
now = datetime.datetime.utcnow().isoformat() + 'Z'
if start_date is None and end_date is None:
events_result = self.service.events().list(calendarId=self.calendarId,
timeMin=now,
maxResults=100, singleEvents=True,
orderBy='startTime').execute()
else:
events_result = self.service.events().list(calendarId=self.calendarId,
timeMin=start_date,
timeMax=end_date,
singleEvents=True).execute()
events = events_result.get('items', [])
if not events:
print('No upcoming events found.')
return json.dumps('No upcoming events found.')
events_list = self.encode_events_list(events)
return events_list
def encode_events_list(self, events):
events_list = []
for event in events:
start = event['start'].get('dateTime', event['start'].get('date'))
end = event['end'].get('endTime', event['end'].get('date'))
events_list.append({
'event_id' : event['id'],
'event_summary' : event['summary'],
'event_start' : start,
'event_end' : end,
})
return events_list
def update_event(self, event_id, updated_event):
current_event = self.service.events().get(calendarId=self.calendarId,
eventId=event_id).execute()
updated_title = updated_event.get('summary', current_event['summary'])
updated_start = updated_event.get('start', current_event['start'])
updated_end = updated_event.get('end', current_event['end'])
updated_event = {
'summary': updated_title,
'start': updated_start,
'end': updated_end
}
event = self.service.events().update(calendarId=self.calendarId,
eventId=event_id,
body=updated_event).execute()
print('Event updated: %s' % event.get('id'))
return json.dumps('Event updated.')
def delete_event(self, event_id):
res = self.service.events().delete(calendarId=self.calendarId,
eventId=event_id).execute()
print('Event deleted: %s' % event_id)
return json.dumps('Event deleted.')
def delete_events_in_range(self, start_date, end_date):
events_result = self.service.events().list(calendarId=self.calendarId,
timeMin=start_date,
timeMax=end_date,
singleEvents=True).execute()
events = events_result.get('items', [])
for event in events:
self.delete_event(event['id'])
events_list = self.encode_events_list(events)
return json.dumps('Events deleted.')
А вот сам код API на базе Flask:
from flask import Flask, Response, render_template, redirect, url_for, request, session, jsonify
from flask_cors import CORS
from flask import send_from_directory
import google.calendar # !!! это наш класс, описанный в блоке выше
app = Flask(__name__)
app.static_folder = 'static'
CORS(app)
def GetJsonFromRequest(request):
data = request.get_json(force=True)
if type(data) is not dict:
data = request.get_json()
data = json.loads(data)
return data
@app.route('/api/v1.0/google_calendar', methods=['POST'])
def google_calendar():
data = GetJsonFromRequest(request)
try:
calendar = google.calendar.GoogleCalendar(data['calendarId'])
if data['action'] == '+':
res = calendar.create_event(data['event'])
if data['action'] == '?':
res = calendar.get_events_list(data['start_date'], data['end_date'])
if data['action'] == '-':
if data['start_date'] is None:
res = calendar.delete_event(data['event_id'])
else:
res = calendar.delete_events_in_range(data['start_date'], data['end_date'])
if data['action'] == '.':
res = calendar.update_event(data['event_id'], data['event'])
print('[END - google_calendar res]', res)
return res
except Exception as e:
print('[END - google_calendar ERROR]', str(e))
return 'Error'
if __name__ == "__main__":
app.run(debug=False, host='0.0.0.0', port=5000)
Шаг №4: Напишем функцию для связи ChatGPT и нашего Google Calendar API
Для вызова функций в ChatGPT нам нужно дать описание всех переменных функции и сам код функции.
Вот описание параметров нашей функции:
{
"name": "google_calendar",
"description": "Управление календарем Google",
"parameters": {
"type": "object",
"properties": {
"calendarId": {
"type": "string",
"description": "ID календаря",
},
"action": {
"type": "string",
"enum": ["+", "?", '-', '.'],
"description": "Тип действия. `+` - означает добавление события. `?` - означает получение всех событий календаря.. `-` - удалить событие в календаре. `.` - редактировать событие в календаре.",
},
"event_ids": {
"type": "string",
"description": "Список ID событый в календаре в формате json: ```[{\\"event_id\\": \\"3lsvlor3hjlgpgmv7er7e5juov\\"}]```. Передается при типах действия `-` или '.'",
},
"events": {
"type": "string",
"description": "Список событий в формате json: ```[{\\"summary\\": \\"test event\\", \\"description\\": \\"some info\\", \\"start\\": {\\"dateTime\\": \\"2024-09-04T03:00:00+03:00\\"}, \\"end\\": {\\"dateTime\\": \\"2024-09-04T05:30:00+03:00\\"}}]```. Передается при типах действия `+` или '.'",
},
"start_date": {
"type": "string",
"description": "Дата начала периода в формате `2024-09-04T03:00:00+03:00`. Передается при типах действия `?` или '-'",
},
"end_date": {
"type": "string",
"description": "Дата окончания периода в формате `2024-09-04T03:00:00+03:00`. Передается при типах действия `?` или '-'",
},
},
"required": ["calendarId", "action"],
},
},
А вот сам код функции:
def google_calendar(arguments):
import requests
import json
data = {
"sa_data" : None,
"calendarId" : arguments['calendarId'],
"action" : arguments['action'],
'start_date' : None,
'end_date' : None,
}
if 'start_date' in arguments.keys():
data['start_date'] = arguments['start_date']
if 'end_date' in arguments.keys():
data['end_date'] = arguments['end_date']
if arguments['action'] == '?':
response = requests.post('https://___АДРЕС_АПИ_СЕРВИСА___/api/v1.0/google_calendar', data=json.dumps(data))
return response.text
else:
responses = []
if arguments['action'] == '+' or arguments['action'] == '.':
if 'events' in arguments.keys():
events = json.loads(arguments['events'])
for i in range(len(events)):
e = events[i]
data['event'] = e
if 'event_ids' in arguments.keys():
event_ids = json.loads(arguments['event_ids'])
data['event_id'] = event_ids[i]
response = requests.post('https://___АДРЕС_АПИ_СЕРВИСА___/api/v1.0/google_calendar', data=json.dumps(data))
responses.append(response.text)
if arguments['action'] == '-':
if 'event_ids' in arguments.keys():
event_ids = json.loads(arguments['event_ids'])
for i in range(len(event_ids)):
data['event_id'] = event_ids[i]['event_id']
response = requests.post('https://___АДРЕС_АПИ_СЕРВИСА___/api/v1.0/google_calendar', data=json.dumps(data))
responses.append(response.text)
if 'start_date' in arguments.keys():
response = requests.post('https://___АДРЕС_АПИ_СЕРВИСА___/api/v1.0/google_calendar', data=json.dumps(data))
responses.append(response.text)
return json.dumps(str(responses))
Тестирование бота
Поскольку в промпте у нашего нейро-сотрудника есть такая инструкция:
Поведение:
1. Для твоей работы необходим ID календаря, который ты будешь передавать в функцию "google_calendar". ID календаря может быть предоставлен и в виде Email адреса. Без него ты не можешь отвечать ни на какие вопросы пользователя.
При старте общения с нашим ботом мы видим следующий диалог:
Если мы укажем наш ID календаря, то бот ответит примерно так:
Давайте для примера запланируем отпуск в Рио в нашем боте. Для начала спросим:
Поскольку у нас ничего пока нет в календаре, то попросим спланировать посещение достопримечательностей в Рио на этот период:
Посмотрим теперь в наш календарь Google:
Отлично! Получилось! Давайте немного посложнее попросим бота:
Как мы видим бот спросил подтверждение при удалении события из календаря, это было в его инструкции:
Поведение:
...
3. Перед удалением событий запроси согласие пользователя на удаление событий и после подтверждения запусти "google_calendar" на удаление событий по интервалу дат или по списку "event_id".
...
Проверяем календарь и видим, что событие было удалено:
Думаю идею вы уже уловили. Бота можно просить корректировать название событий, переносить события на нужную дату и время и т.д.
Да, и самое важное - боту можно отправлять голосовые и он так же будет помогать вам вести Google календарь.
Итоги
В целом мозгов у ChatGPT 4 вполне хватает на управление календарем.
Что можно еще попробовать?
Разбиение сложных задач на подзадачи
Помощь с распределением задач
Ежедневное уведомление о важных делах на сегодня/неделю/месяц
Просить бота фокусировать внимание пользователя на важных событиях и говорить об этом при постановке задач
Дать боту Яндекс.Календарь и предлагать в начале выбрать с каким календарем работать
Сам бот можно протестировать тут.
Идеи и вопросы пишите мне в телеграм.