Plank help · updated 2026-09-17
Подключение Google (Gmail, Calendar, Drive)
Подключите аккаунт Google, чтобы ассистент мог читать и отправлять почту, а также работать с Calendar, Drive, Docs и Sheets — с точной одноразовой настройкой, которая срабатывает с первого раза.
Agents: fetch the raw markdown of this page at /ru/help/connecting-google.md
Подключение Google
Ваш ассистент может работать с вашим аккаунтом Google — читать и отправлять письма в Gmail, вести Calendar и работать с Drive, Docs и Sheets. После подключения вы просто просите («есть новые письма от этого клиента?», «отправь им такой ответ», «добавь заказ в таблицу учёта») — и ассистент это делает.
Вход в Google использует OAuth — вы один раз разрешаете доступ в собственном браузере, а ассистент хранит токен, чтобы не спрашивать снова. На этой странице описана одноразовая настройка. Она написана и для вас, и для ассистента: ассистент загружает её по адресу https://plank.md/help/connecting-google.md перед подключением Google, чтобы соединение прошло чисто с первого раза, а не прервалось на полпути.
Зачем такая аккуратная настройка? Во входе в Google есть несколько движущихся частей, которые должны точно совпасть — правильный тип клиента OAuth, нужные разрешения и токен, включающий офлайн-«обновление», чтобы он продолжал работать. Ошибётесь в одном — и соединение сработает один раз, а потом сломается, или не завершится вовсе. Шаги ниже — это та самая комбинация, которая работает.
Шаг 0 — Сначала попробуйте общее подключение Plank (ассистент)
Прежде чем отправлять кого-либо в Google Cloud Console, проверьте, может ли Plank выдать готовый клиент OAuth. Это быстрый путь — большинство подключений должны заканчиваться именно на Шаге 0, даже не доходя до Шага 1.
Установите набор инструментов (пропустите, если scripts/google/ уже существует):
BASE=https://plank.md/help/kits
for KIT in $(curl -fsS "$BASE/google-base/manifest.json" | jq -r '.installOrder[]'); do
curl -fsS "$BASE/$KIT/manifest.json" | jq -r '.files[].path' | while read -r p; do
mkdir -p "$(dirname "$p")"
curl -fsS "$BASE/$KIT/files/$p" -o "$p"
done
done
Запросите общий клиент. $PLANK_API_URL и $PLANK_WORKSPACE_ID уже есть в окружении.
mkdir -p scripts/google
attempt=1
while [ "$attempt" -le 3 ]; do
code=$(curl -s -o /tmp/gclient.json -w '%{http_code}' \
"$PLANK_API_URL/internal/google/oauth-client?workspaceId=$PLANK_WORKSPACE_ID")
case "$code" in
503) [ "$attempt" -lt 3 ] && sleep 3 ;;
429) [ "$attempt" -lt 3 ] && sleep 15 ;;
*) break ;;
esac
attempt=$((attempt + 1))
done
echo "http $code"
Записывайте ответ в файл, никогда в stdout — в нём client secret, и он не должен попасть в переписку. Используйте -w, а не -f: -f отбрасывает тело ошибки, а вам нужно прочитать reason, чтобы выбрать правильную ветку.
Есть четыре исхода, и они не взаимозаменяемы:
-
200— клиент доступен. Переместите файл на место и переходите сразу к Шагу 5 — Авторизация ниже; Шаги 1-4 не нужны.mv /tmp/gclient.json scripts/google/credentials.json -
403— окончательный отказ. Тело ответа:{"reason": "cap" | "disabled", "setupUrl": "..."}— либо лимит общего проекта Google почти исчерпан, либо переключатель на платформе выключен. В любом случае общее подключение Plank сейчас действительно недоступно — продолжайте с Шага 1 ниже и создавайте собственный проект Google Cloud пользователя. Это тот единственный случай, ради которого существует ручная инструкция. -
503— эндпоинт не смог определить ответ (кратковременно не удалось прочитать базу данных), а не отказал. Тело ответа:{"reason": "unknown"}— намеренно безsetupUrl, потому что ручная инструкция — неправильный ответ на «мы не знаем», а не правильный. Цикл выше уже делает несколько повторных попыток, прежде чем сдаться; большинство503проходят за несколько секунд. Переходите к Шагу 1 только если после повторов всё ещё503— и даже тогда прямо скажите пользователю, что проверка общего подключения временно недоступна, а не подавайте это как «общего подключения не существует». -
429— эндпоинт ограничивает частоту запросов, это не отказ. Обращайтесь с ним так же, как с503: можно повторить, это не повод переходить к Шагу 1. Это не гипотетический случай — лимит привязан к адресу хоста песочницы, вызывающего эндпоинт, а его делят все контейнеры на этом хосте, так что всплеск активности чужого контейнера может ограничить и ваш, хотя вы ничего не нарушали. Цикл выше уже ждёт между повторами дольше для429, чем для503(окно ограничения — целая минута, а не разовый сбой), и тело ответа здесь читать не за чем. Переходите к Шагу 1 только если после повторов всё ещё429— и скажите пользователю, что общее подключение временно перегружено, а не что его не существует.
Ошибка в этой развилке отправляет пользователя в десятиминутный обход через Cloud Console из-за того, что могло быть односекундным сбоем. Если сомневаетесь — сначала повторите попытку, а уже потом переходите дальше.
Что понадобится
(Только если Шаг 0 не сработал — 403 с reason: "cap" или "disabled", либо стойкий 503/429 после повторов.)
Аккаунт Google и около десяти минут в Google Cloud Console (бесплатно). Эту часть вы делаете сами, потому что она привязана к вашему собственному аккаунту Google; ассистент не может кликать за вас по экранам согласия Google. После того как вы пройдёте это один раз, повторять не придётся.
Шаг 1 — Создайте проект Google Cloud и включите API
- Откройте console.cloud.google.com, войдя под тем аккаунтом Google, который хотите подключить.
- Создайте новый проект (например, «Plank Tools»).
- Перейдите в APIs & Services → Library и включите (Enable) эти API. По умолчанию включите все — тогда ассистент сможет работать с любым из этих инструментов позже, не отправляя вас сюда снова:
- Gmail API — чтение, упорядочивание и отправка почты
- Google Calendar API — события и расписание
- Google Drive API — файлы и папки
- Google Docs API — документы
- Google Sheets API — таблицы
- Google Slides API — презентации
- Google Tasks API — задачи
- Нужен другой сервис Google (Contacts, Forms, Apps Script, …)? Включите и его API — просто найдите по названию в Library.
Шаг 2 — Настройте экран согласия OAuth
- Перейдите в APIs & Services → OAuth consent screen (в новой консоли это Google Auth Platform → Branding / Audience).
- Личный аккаунт
@gmail.com: выберите External. (Вариант Internal существует только для аккаунтов организаций Google Workspace.) Рабочий аккаунт / Google Workspace (например,name@yourcompany.com): выберите Internal — это избавит от возни с тест-пользователями и проверкой. - Заполните название приложения, вашу почту поддержки и контактную почту разработчика.
- Добавьте разрешения (scopes). По умолчанию запросите полный набор ниже, чтобы ассистент мог работать со всеми сервисами Google и не заставлял вас переподключаться, когда вы позже попросите что-то новое:
https://www.googleapis.com/auth/gmail.modifyи.../gmail.send(чтение, упорядочивание и отправка почты)https://www.googleapis.com/auth/calendar(Calendar)https://www.googleapis.com/auth/drive(Drive)https://www.googleapis.com/auth/documents(Docs)https://www.googleapis.com/auth/spreadsheets(Sheets)https://www.googleapis.com/auth/presentations(Slides)https://www.googleapis.com/auth/tasks(Tasks)- Просили ассистента про другой сервис Google? Добавьте сюда и его scope.
- Аккаунт External: в разделе Test users добавьте адрес Google, который будете подключать. (На следующем шаге вы выведете приложение из режима Testing, так что это просто чтобы вход работал пока что.)
Шаг 3 — Опубликуйте приложение в Production (не пропускайте)
Именно этот шаг сохраняет соединение живым. На экране согласия OAuth (новая консоль: Google Auth Platform → Audience) установите Publishing status в In production — нажмите Publish app, затем Confirm.
Почему это обязательно: пока приложение остаётся в режиме Testing, Google аннулирует его refresh-токен примерно каждые 7 дней — поэтому ассистент «разлогинивается», и вам приходится авторизоваться снова и снова. В Production токен продолжает работать, и вы входите всего один раз.
Завершать проверку (verification) Google для личного использования не нужно. Поскольку полный набор включает ограниченные (restricted) scopes (Gmail), Google покажет здесь жёлтый баннер «requires verification» и экран «unverified app» при входе — для приложения, которым пользуетесь только вы, это нормально; просто продолжите. Проверка лишь убирает это предупреждение и снимает лимит в 100 пользователей; на то, останется ли токен живым, она не влияет.
Шаг 4 — Создайте клиент OAuth
- Перейдите в APIs & Services → Credentials → Create Credentials → OAuth client ID.
- Тип приложения: Desktop app. (Это важно — клиент типа Desktop app использует редирект
http://localhost, на котором основан описанный ниже способ со вставкой ссылки. Не используйте клиент «Web application», если не знаете его redirect URIs.) - Скачайте JSON и передайте его ассистенту (вставьте в чат или загрузите файл). Ассистент сохранит его в рабочем пространстве как
scripts/google/credentials.json.
Шаг 5 — Авторизация (часть, которую вы делаете в своём браузере)
Поскольку ваш ассистент работает на удалённой машине без собственного браузера, вы разрешаете доступ в своём браузере и возвращаете результат. Ассистент ведёт вас по этому процессу — вот что вы увидите:
- Ассистент даёт вам ссылку для входа. Откройте её в своём браузере и разрешите доступ.
- Google покажет «Google hasn't verified this app» — для вашего собственного приложения это нормально. Нажмите Advanced → Go to (название вашего приложения) и продолжите.
- После того как вы разрешите доступ, Google перенаправит вас на адрес
http://localhost…, который не загрузится — это тоже нормально. Скопируйте полную ссылку (URL) из адресной строки браузера (в ней содержится одноразовый код). - Вставьте эту ссылку целиком обратно в чат. Ассистент обменяет её на токен, сохранит в
scripts/google/token.jsonи подтвердит, прочитав профиль вашего аккаунта.
Вот и всё — Google подключён, а инструменты Google ассистента появляются в боковой панели Интеграции.
Работа с Таблицами — для ассистента
В наборе уже есть ридер — scripts/google/sheets/read-sheet.py. Проверьте scripts/google/sheets/, прежде чем писать что-то своё. Чтение таблицы нужно почти любой реальной задаче — построить дашборд, вытащить цифры в отчёт, сверить с другой системой, — поэтому используйте его напрямую, а не изобретайте одноразовый ридер под каждую задачу; раньше, пока ридер вообще не входил в набор, это было главным источником потерянных ходов в работе с Таблицами.
Сначала прочитайте Запуск команд на машине. Два факта оттуда определяют, запустятся ли эти скрипты вообще: интерпретатор — python3 (никогда python), а библиотеки Google лежат в пользовательском site-packages, поэтому PYTHONNOUSERSITE=1 ломает здесь любой импорт. Если при запуске печатается FutureWarning про версию Python — добавьте префикс PYTHONWARNINGS=ignore и работайте дальше.
read-sheet.py импортирует общий помощник scripts/google/lib/google_auth.py (он уже лежит рядом с credentials.json и token.json), поэтому обновление токена достаётся бесплатно. Скрипта записи в наборе пока нет — напишите scripts/google/sheets/update-sheet.py сами, один раз, по образцу ниже, когда задаче впервые понадобится записать ячейки.
scripts/google/sheets/read-sheet.py — уже установлен
Принимает полный URL таблицы или просто ID. Без указания диапазона возвращает структуру книги — заголовок, gid и размер каждой вкладки. Именно это и нужно на первом вызове, чтобы дальше адресовать вкладки точно. Флаги: --range (диапазон A1), --gid (чтение по id вкладки — имеет приоритет над --range), --formulas (вернуть формулы вместо вычисленных значений).
scripts/google/sheets/update-sheet.py — не входит в набор, напишите сами
#!/usr/bin/env python3
# @plank-integration
# provider: google
# service: sheets
# name: Update Google Sheet
# description: Writes values or formulas to a specified Google Sheets range.
# requires:
# - file: scripts/google/credentials.json
# - file: scripts/google/token.json
from __future__ import annotations
import argparse
import json
import re
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "lib"))
from google_auth import google_service # noqa: E402
def spreadsheet_id(value: str) -> str:
match = re.search(r"/spreadsheets/d/([^/]+)", value)
return match.group(1) if match else value
def main() -> None:
parser = argparse.ArgumentParser(description="Update cells in Google Sheets.")
parser.add_argument("spreadsheet", help="Spreadsheet URL or ID.")
parser.add_argument("--range", dest="cell_range", required=True, help="A1 range, e.g. 'Sheet1'!C13:N13.")
parser.add_argument("--values-json", required=True, help="Two-dimensional JSON array of values.")
args = parser.parse_args()
try:
values = json.loads(args.values_json)
except json.JSONDecodeError as exc:
raise SystemExit(f"--values-json is not valid JSON: {exc}")
if not isinstance(values, list) or any(not isinstance(row, list) for row in values):
raise SystemExit("--values-json must be a two-dimensional JSON array")
result = google_service("sheets", "v4").spreadsheets().values().update(
spreadsheetId=spreadsheet_id(args.spreadsheet),
range=args.cell_range,
valueInputOption="USER_ENTERED",
body={"values": values},
).execute()
print(json.dumps({
"updatedRange": result.get("updatedRange"),
"updatedRows": result.get("updatedRows", 0),
"updatedCells": result.get("updatedCells", 0),
}, ensure_ascii=False))
if __name__ == "__main__":
main()
Использование
# 1. Сначала структура — все вкладки, их gid и размер
PYTHONWARNINGS=ignore python3 scripts/google/sheets/read-sheet.py "<url-or-id>"
# 2. Значения одной вкладки (имена с пробелами или кириллицей — в кавычках)
PYTHONWARNINGS=ignore python3 scripts/google/sheets/read-sheet.py "<id>" --range "'P&L'!A1:O40"
# 3. По gid, когда имя вкладки неудобно экранировать
PYTHONWARNINGS=ignore python3 scripts/google/sheets/read-sheet.py "<id>" --gid 1234567890
# 4. Формулы вместо вычисленных значений — когда разбираетесь, как устроена модель
PYTHONWARNINGS=ignore python3 scripts/google/sheets/read-sheet.py "<id>" --range "'P&L'!A1:O40" --formulas
# 5. Запись обратно
PYTHONWARNINGS=ignore python3 scripts/google/sheets/update-sheet.py "<id>" \
--range "'P&L'!C13:N13" --values-json '[[1,2,3]]'
Построение дашборда по таблице
Не перечитывайте таблицу на каждую правку страницы. Прочитайте один раз, сохраните, дальше итерируйте локально:
- Прочитайте структуру, затем те вкладки, которые действительно нужны. Уточните у пользователя, какие вкладки важны, вместо того чтобы тянуть все подряд.
- Запишите значения в один JSON-файл в рабочем пространстве, например
finances/<имя>-data.json. Это источник данных дашборда и то, что вы перегенерируете, когда цифры меняются. - Напишите скрипт синхронизации —
scripts/google/sheets/sync-<имя>.py, — который пересобирает этот JSON одной командой. Дайте ему заголовок@plank-integration, чтобы пользователь мог перезапустить его из боковой панели при изменении таблицы. Именно это превращает «пересобери дашборд» из разговора в одно нажатие. - Стройте HTML по JSON, следуя тому, как Plank выдаёт готовые документы. Дорабатывайте страницу вообще без обращений к сети.
Правило простое: один проход по сети, много проходов по локальной копии. Чтение из Таблиц быстрое, но каждое — это round-trip ценой в ход, а дашборду обычно нужен десяток правок уже после того, как данные получены.
Чтобы соединение не отвалилось
- Токен обновляется сам. Настройка запрашивает офлайн-доступ, поэтому сохранённый токен включает refresh token, и ассистент обновляет его автоматически — для обычной работы повторно входить не придётся.
- Если вас всё же просят переподключиться каждые несколько дней — приложение вернулось в Testing. Снова откройте Google Auth Platform → Audience и убедитесь, что Publishing status = In production (Шаг 3). Именно эта настройка прекращает еженедельный «разлогин».
- Но если другие файлы токенов на том же OAuth-клиенте продолжают обновляться, дело не в сроке Testing. Статус публикации действует на весь клиент сразу. В рабочем пространстве может лежать безымянный
token.jsonрядом с файламиtoken-<email>.json, и неделями отказывать сinvalid_grantможет только безымянный, пока именные обновляются каждый день. Переподключите этот один файл (или переведите скрипт на токен именного аккаунта) — не отправляйте пользователя менять статус публикации.
Уже подключено? Не проходите настройку заново
Эта страница — первоначальная настройка. Если Google в этом рабочем пространстве уже работал, ничего из неё повторять не нужно — и отсутствие файла там, где вы его ждали, не повод начинать сначала.
Прочитайте это, прежде чем сказать пользователю, что Google отключён:
- Ищите токен там, откуда его читают сами скрипты. В старом рабочем пространстве он может лежать за пределами
scripts/— обычно/home/coder/.config/google/<workspace>/token.json. Путь указан в заголовкеrequires:и в описании самого скрипта; авторитет — они, а не соглашение с этой страницы. «Нет вscripts/google/» ≠ «отключено». - Токен в старом расположении просто используется там, где лежит — тот же аккаунт Google, без повторной авторизации. Не переносите его в
scripts/google/по своей инициативе: этот каталог доступен всем участникам рабочего пространства, а/home/coderприватен для одного пользователя. См. Где хранятся учётные данные. - Сначала проверьте, потом делайте вывод. Один недорогой запрос только на чтение (
profileили чтение одной строки — но не отправка) покажет правду. Только отказ на уровне учётных данных —invalid_grant, отозванный доступ или 401, который остаётся после обновления токена — означает, что подключение действительно потеряно, и обычная причина этого — статус Testing выше, если только другие файлы токенов на том же клиенте не продолжают обновляться: тогда переподключить нужно только этот один файл. Ошибка403из-за квоты или прав, невключённый API или сетевой сбой — это другая проблема: скажите, какая именно, вместо просьбы переподключиться. - Никогда не отправляйте человека обратно в Google Cloud Console из-за расположения файла. Создание проекта, включение API и выпуск нового OAuth-клиента — долгий путь для неподготовленного пользователя, после которого остаётся второй набор учётных данных.
Личный и рабочий аккаунты бок о бок
Вы можете подключить личный аккаунт Google в одном рабочем пространстве и рабочий аккаунт (например, info@yourcompany.kz) в другом — они никогда не смешиваются. Каждое рабочее пространство Plank хранит свои scripts/google/credentials.json и token.json, поэтому ваш личный ящик и рабочий ящик остаются чётко разделёнными. Чтобы сменить аккаунт, который использует рабочее пространство, удалите его token.json и авторизуйтесь заново.
Как это вас защищает
- Plank никогда не видит ваш пароль Google. Вы входите на собственной странице Google; обратно приходит только разрешение (токен).
- Учётные данные остаются в вашем рабочем пространстве и исключены из git — они не передаются другим рабочим пространствам и никуда не коммитятся.
- Вы можете отозвать доступ в любой момент в настройках безопасности вашего аккаунта Google или удалив файл токена.
См. также: Интеграции — общий взгляд на подключение сервисов, Скрипты рабочего пространства и боковая панель — как инструменты Google появляются после подключения, и Автоматизации — чтобы ассистент проверял почту по расписанию.