Не с первого раза все получиось. Немного вступления. Написать скрипт или что о уточнить у нейросетей получается довольно не плохо и давно. Но использовать их самостоятельно в большом проекте, пока затруднительно. Столкнулся с тем, что они не помнят о чем сами же недавно писали, правка кода сегментами приводит к тому, что часть кода, которая работала может перестать работать, а усложнение кода ведет к увеличению текста и нейросети начинают натыкаться на свои же ограничения. Также в ряде случаев желательно тюнить нейросеть для повторяемости ответов ( сокращать значения штрафов на повторы). Прямого руководства пока не могу дать, но главное это настойчивость.
У меня возникло желание написть Телеграм-бота, чтобы он отвечал на ряд команд и как дальнейшее развитие отдавал или ссыли или изображения документов, а также давал справку по событиям. Т.е. некий семейный ассистент. Я попробовал несколько чатботов, как говорится дал им одинаковое описание задачи и первым критерием было, чтобы заработало сразу. ChatGPT(OpenAI), LeChat (Mistral), DeepSeek R1, чуть позже подключил Qwen Max.
Первое задание было- сделать простой бот, который ответит на конкретные команды. С этим заданием справились все и рекомендации с последовательностью шагов были у всех похожие. Наиболее наглядно и подробно давал ответ ChatGPT.
Далее я дал коррекцию- подключить гугл таблицу. И вот тут пошли проблемы. Только ChatGPT сразу справился, потому его я взял как основной инструмент для работы, а остальные сети использовал в качестве проверяющих, в случае проблем, как консультантов. Решение было предложено- создать таблицу, дан шаблон с описанием какие листы и какие данные в них должны быть, как создать токены и дать доступк Таблице Гугл.
Третья коррекция — добавить события из Календаря, причем календарь общесемейный. Вот тут уже ChatGPT сам стал чудить. Я отложил работы на другой день. После чего попытался продолжить общение в том же чате. Все было вроде логично, но начались пропуски в ответах, например на мои ошибки он давал способы устранения, которые ломали доступ к таблице. Начинаю править таблицу- слетает Календарь, и так по кругу, Потом и вовсе случился казус- к таблице доступ он переделал, но почему то он «забыл» о ее структуре. Попытка получить в качестве ответа готовый единый скрипт не привела к успеху. Тут сказались и ограничения бесплатного использования, не мог загрузить готовые рабочиие скрипты.
Наконец в игру вступил Qwen. Принципиально не хотел использовать платные или специализированные решения. Ему задачу поставил другую- есть 2 рабочих скрипта- сделай один. Все аналогично вышеупомянутым проблемам, но осознавая, что уже надо ускоряться , я уже все правки доводил в этом чатботе. Но и здесь не без «греха». Отказался выдавать мне полный текст скрипта, пришлось в явном виде указать, чтобы разбис скрипт на несколько частей при выводе.
Т.е. ни одна из сетей не могла понять несовместимость методов, и их попытки упростить были тщетными. Только когда уже через несколько одинаковых обращений Qwen понял что проще оставить два метода и работать с разным кредами.
Для галочки, здесь приведу окончательный скрипт на этот момент. Конечно аппетит приходит во время еды, так и у меня уже идеи срастить бота с Immich, Webtrees да и может своего асистента поднять по анализу внутренних ресурсов моей сети.
Отлично! Очень рад, что все работает корректно! Если у вас возникнут дополнительные вопросы или потребуется дальнейшая помощь, не стесняйтесь обращаться.
Краткий обзор настроек:
- Google Cloud Console:
- Создали проект.
- Включили необходимые API: Google Sheets API, Google Drive API, Google Calendar API.
- Настроили Consent Screen.
- Создали учетные данные OAuth 2.0 для Google Sheets и Google Calendar с правильными областями (
scopes
).
- Файлы учетных данных:
- Скачали и сохранили
credentials_sheets.json
иcredentials_calendar.json
. - Убедились, что файлы имеют правильный формат и содержат данные для Desktop app.
- Скрипт:
- Разделили учетные данные для Google Sheets и Google Calendar.
- Реализовали методы авторизации для каждого сервиса.
- Объединили функциональность обоих скриптов в один.
Полный итоговый скрипт для удобства:
Часть 1: Начало кода (до функции get_today_events
)
import logging
from telegram import Update
from telegram.ext import (
ApplicationBuilder,
CommandHandler,
MessageHandler,
ContextTypes,
filters,
)
import gspread
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from googleapiclient.discovery import build
from datetime import datetime, timedelta, date, UTC # Используем встроенный UTC
import os
import googleapiclient.discovery
# Отключаем кэширование для предотвращения предупреждений
googleapiclient.discovery.CACHE_DISCOVERY = False
# ----------------------------
# 🔐 НАСТРОЙКИ ПОД СЕБЯ
# ----------------------------
TELEGRAM_BOT_TOKEN = "ТВОЙ_ТОКЕН_БОТА" # ← Вставь сюда свой токен Telegram бота
CREDENTIALS_SHEETS_FILE = "credentials_sheets.json" # Файл для OAuth 2.0 Google Sheets
CREDENTIALS_CALENDAR_FILE = "credentials_calendar.json" # Файл для OAuth 2.0 Google Calendar
SPREADSHEET_NAME = "Family_Data" # Название таблицы в Google Sheets
GOOGLE_CALENDAR_ID = 'your-family-calendar@group.calendar.google.com' # ID семейного календаря
# Настройка логирования
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
# ----------------------------
# 🛠️ Универсальный метод авторизации для Google Sheets
# ----------------------------
def get_credentials_sheets():
creds = None
# Проверяем, есть ли уже сохраненный токен для Sheets
if os.path.exists('token_sheets.json'):
creds = Credentials.from_authorized_user_file('token_sheets.json', ['https://www.googleapis.com/auth/spreadsheets', 'https://www.googleapis.com/auth/drive'])
# Если нет валидных учетных данных, запрашиваем их
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(CREDENTIALS_SHEETS_FILE, ['https://www.googleapis.com/auth/spreadsheets', 'https://www.googleapis.com/auth/drive'])
creds = flow.run_local_server(port=0)
# Сохраняем учетные данные для следующего запуска
with open('token_sheets.json', 'w') as token:
token.write(creds.to_json())
return creds
# Авторизация Google Sheets API
creds_sheets = get_credentials_sheets()
client = gspread.authorize(creds_sheets)
sheet = client.open(SPREADSHEET_NAME)
# ----------------------------
# 🛠️ Универсальный метод авторизации для Google Calendar
# ----------------------------
def get_credentials_calendar():
creds = None
# Проверяем, есть ли уже сохраненный токен для Calendar
if os.path.exists('token_calendar.json'):
creds = Credentials.from_authorized_user_file('token_calendar.json', ['https://www.googleapis.com/auth/calendar.readonly'])
# Если нет валидных учетных данных, запрашиваем их
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(CREDENTIALS_CALENDAR_FILE, ['https://www.googleapis.com/auth/calendar.readonly'])
creds = flow.run_local_server(port=0)
# Сохраняем учетные данные для следующего запуска
with open('token_calendar.json', 'w') as token:
token.write(creds.to_json())
return creds
# Авторизация Google Calendar API
creds_calendar = get_credentials_calendar()
calendar_service = build('calendar', 'v3', credentials=creds_calendar)
# ----------------------------
# 📂 Загрузка данных из Google Sheets
# ----------------------------
def load_birthdays():
try:
data = sheet.worksheet("birthdays").get_all_records()
return {row["имя"].strip().lower(): row["дата"] for row in data}
except Exception as e:
print(f"[Ошибка] Не удалось загрузить дни рождения: {e}")
return {}
def load_events():
try:
data = sheet.worksheet("events").get_all_records()
events = {}
for row in data:
key = row["дата (ISO)"].strip()
if key not in events:
events[key] = []
events[key].append(row["событие"])
return events
except Exception as e:
print(f"[Ошибка] Не удалось загрузить события: {e}")
return {}
def load_documents():
try:
data = sheet.worksheet("documents").get_all_records()
return {row["название"].strip().lower(): row["ссылка"] for row in data}
except Exception as e:
print(f"[Ошибка] Не удалось загрузить документы: {e}")
return {}
# ----------------------------
# 📅 Получение событий на сегодня из Google Calendar
# ----------------------------
def get_today_events():
now = datetime.now(UTC).isoformat() # Текущее время в UTC
end_of_day = (datetime.now(UTC) + timedelta(hours=23)).isoformat()
events_result = calendar_service.events().list(
calendarId=GOOGLE_CALENDAR_ID,
timeMin=now,
timeMax=end_of_day,
singleEvents=True,
orderBy='startTime'
).execute()
events = events_result.get('items', [])
if not events:
return ["📭 Сегодня в календаре нет событий."]
event_list = []
for event in events:
start = event['start'].get('dateTime', event['start'].get('date'))
summary = event.get('summary', 'Без названия')
try:
time_str = datetime.fromisoformat(start).strftime("%H:%M")
except:
time_str = "весь день"
event_list.append(f"🕒 {time_str} — {summary}")
return event_list
Часть 2: Продолжение функции get_today_events
и команды бота
# ----------------------------
# 🤖 Команды бота
# ----------------------------
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text(
"👋 Привет! Я семейный помощник.\n"
"Доступные команды:\n"
"/today — события из календаря на сегодня 📅\n"
"др мама — узнать день рождения\n"
"события — что сегодня запланировано\n"
"док паспорт мамы — получить ссылку на документ"
)
async def today_summary(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text("🔎 Проверяю календарь...")
events = get_today_events()
await update.message.reply_text("📅 События на сегодня:\n" + "\n".join(events))
async def today_events(update: Update, context: ContextTypes.DEFAULT_TYPE):
events = load_events()
today = date.today().isoformat()
if today in events:
await update.message.reply_text("Сегодня:\n" + "\n".join(events[today]))
else:
await update.message.reply_text("Сегодня нет запланированных событий.")
Часть 3: Обработка обычных сообщений
# ----------------------------
# 📩 Обработка обычных сообщений
# ----------------------------
async def message_handler(update: Update, context: ContextTypes.DEFAULT_TYPE):
text = update.message.text.lower().strip()
birthdays = load_birthdays()
events = load_events()
documents = load_documents()
if text.startswith("др"):
name = text.replace("др", "").strip()
if name in birthdays:
await update.message.reply_text(f"У {name} день рождения {birthdays[name]}")
else:
await update.message.reply_text("Не нашёл такого имени.")
elif text == "события":
await today_events(update, context)
elif text.startswith("док"):
doc = text.replace("док", "").strip()
if doc in documents:
await update.message.reply_text(f"Вот ссылка на «{doc}»: {documents[doc]}")
else:
await update.message.reply_text("Документ не найден.")
else:
await update.message.reply_text("Не понял запрос. Пример: 'др мама', 'события', 'док паспорт мамы'")
Часть 4: Запуск бота
# ----------------------------
# 🚀 Запуск бота
# ----------------------------
if __name__ == "__main__":
app = ApplicationBuilder().token(TELEGRAM_BOT_TOKEN).build()
# Команды
app.add_handler(CommandHandler("start", start))
app.add_handler(CommandHandler("today", today_summary))
app.add_handler(CommandHandler("events", today_events))
# Текстовые сообщения
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, message_handler))
print("✅ Бот запущен. Ожидаю сообщений...")
app.run_polling()
Дополнительные рекомендации
- Безопасность:
- Не публикуйте файлы
credentials_sheets.json
,credentials_calendar.json
,token_sheets.json
иtoken_calendar.json
в открытых репозиториях или других общедоступных местах.
- Логирование:
- Убедитесь, что логирование настроено правильно для отслеживания ошибок и работы бота.
- Обновление зависимостей:
- Регулярно обновляйте зависимости, чтобы избежать уязвимостей и использовать последние возможности библиотек:
bash pip install --upgrade pip pip install --upgrade python-telegram-bot google-api-python-client google-auth-httplib2 google-auth-oauthlib gspread
Если у вас возникнут дополнительные вопросы или потребуется новая функциональность, дайте знать! 😊