Инструмент для сбора метрик постов Threads и загрузки их в Google Sheets.
- Асинхронно получает посты Threads по списку аккаунтов из листа
accounts_threads, повторно используя сохранённые курсоры пагинации, чтобы не скачивать старые записи заново. - Запрашивает Insights только для тех постов, чьи данные устарели, и хранит отметки времени обновления в локальном состоянии.
- Объединяет «сырые» данные постов и Insights, дополняя таблицу метрик без потери уже записанных колонок и обновляя строки по ключу
account_name + post_idбез дублирования. - Добавляет в таблицу время публикации постов, конвертируя его в часовой пояс Афин, и сортирует строки по возрастанию даты.
- После каждой выгрузки отключает перенос текста в диапазоне и принудительно устанавливает высоту строк в 21 пиксель, чтобы таблица оставалась компактной.
- Каждые 15 минут копирует лист
Data_Po_kagdomy_postyв публичную таблицу, очищая целевой лист и выравнивая высоту строк в 21 пиксель. - Ведёт JSON-логирование с heartbeat-сообщениями и автоматически останавливается по таймауту, чтобы не зависать бесконечно.
- Игнорирует аккаунты на листе
accounts_threads, у которых в колонкеAстоит заливка#9fc5e8, и выводит в логи список пропущенных никнеймов. - Для каждого ответа Threads API со статусом, отличным от
200, пишет в лог никнейм аккаунта, полный URL и текст ответа, что упрощает разбор проблемных запросов. - Автоматически повторяет неуспешные запросы Insights: после основного прохода запускает до трёх дополнительных попыток с паузами 20–30 секунд для каждого URL и отмечает прогресс в логах.
- Python 3.11 или новее.
- Учётные данные сервисного аккаунта Google с правами на чтение/запись в нужную таблицу.
- Доступ к Threads Graph API и рабочие токены аккаунтов Threads.
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
export PYTHONPATH=srcЗаполните файл .env на основе .env.example. Критичные переменные:
ID_GOOGLE_TABLE— идентификатор Google-таблицы с данными.ID_GOOGLE_TABLE_PUBLIC_DANNYE— идентификатор публичной Google-таблицы для публикации листаData_Po_kagdomy_posty.URL_GAS_RAZVERTIVANIA— URL развёрнутого Google Apps Script.GOOGLE_SERVICE_ACCOUNT_JSON— JSON-ключ сервисного аккаунта Google.GOOGLE_MAX_STRING_PARSING— максимальное число строк для копирования в публичную таблицу (0 — без ограничений, значение для workflow берётся из одноимённого секрета).THREADS_API_BASE_URL— базовый URL Threads Graph API (по умолчаниюhttps://graph.threads.net, запросы выполняются к версии APIv1.0).THREADS_REQUEST_TIMEOUT— таймаут HTTP-запросов в секундах.THREADS_CONCURRENCY— максимальное число параллельных запросов.THREADS_STATE_FILE— путь к файлу состояния.THREADS_METRICS_TTL_MIN— TTL метрик в минутах.THREADS_RUN_TIMEOUT_MIN— максимальная продолжительность работы сервиса в минутах.
Остальные параметры можно оставить по умолчанию: запросы к API выполняются с таймаутом THREADS_REQUEST_TIMEOUT, а параллелизм ограничивается THREADS_CONCURRENCY, чтобы не превысить лимиты Threads Graph API.
Приложение не читает .env автоматически — переменные должны попасть в окружение перед запуском. В POSIX-оболочках можно сделать так:
set -a
source .env
set +a
python -m threads_metrics.main runНа Windows можно воспользоваться python-dotenv (python -m dotenv run -- python -m threads_metrics.main run) либо задать переменные вручную через set KEY=VALUE перед запуском.
THREADS_STATE_FILEзадаёт путь к JSON-файлу, где сервис кеширует курсоры пагинации и отметки о последнем обновлении метрик.THREADS_METRICS_TTL_MINопределяет, спустя сколько минут метрики считаются устаревшими и требуется новый сбор.THREADS_CONCURRENCYограничивает количество одновременных запросов к Threads Graph API и тем самым защищает от rate limit.
- Активируйте виртуальное окружение и убедитесь, что переменная
PYTHONPATHуказывает на каталогsrc, чтобы импорты модулей работали одинаково и в тестах, и в CLI. - Для интерактивных экспериментов используйте
python -m threads_metrics.main --help— команда выведет доступные подкоманды и опции запуска. - Чтобы сбросить кеш и повторно собрать все метрики, удалите файл, заданный переменной
THREADS_STATE_FILE. При следующем запуске сервис создаст его заново и запросит свежие данные без использования сохранённых курсоров.
- Логи пишутся в STDOUT в формате JSON; поля
context.account,context.postиcontext.actionпомогут быстро понять, на каком шаге возникла ошибка. - Таймауты запросов регулируются
THREADS_REQUEST_TIMEOUT, а количество ретраев определяется настройкойstop_after_attempt(5)вthreads_metrics.threads_client. При необходимости скорректируйте её, чтобы изменить допустимое число повторов. - Если Threads API временно возвращает статусы вроде
403или5xx, сервис автоматически запустит дополнительный раунд повторов (до трёх попыток для каждого URL) и явно отметит в логах номер попытки и никнейм аккаунта. - Для подробной диагностики установите
LOG_LEVEL=debugперед запуском — сервис будет выводить сообщения о каждом запросе и пропуске постов, что упрощает поиск проблемных мест.
threads_metrics.main— CLI-обёртка, конфигурирование зависимостей и управление жизненным циклом сервиса (heartbeat, обработка сигналов, таймаут).threads_metrics.threads_client— асинхронный клиент Threads Graph API с ретраями наhttpxи семафором для ограничения параллелизма.threads_metrics.google_sheets— чтение токенов и запись агрегированных метрик в Google Sheets, синхронизация с локальным состоянием.threads_metrics.state_store— файловое хранилище курсоров и отметок времени, применяемое для дедупликации и TTL метрик.threads_metrics.aggregation— объединение данных о постах и их Insights перед выгрузкой.
Логирование ведётся в JSON-формате (ts, level, msg, context), поэтому сбор логов интегрируется с системами наблюдаемости без дополнительной обработки.
- Команда
python -m threads_metrics.sync_sheetsпереносит листData_Po_kagdomy_postyиз таблицыID_GOOGLE_TABLEв таблицуID_GOOGLE_TABLE_PUBLIC_DANNYE. - Перед записью целевой лист полностью очищается, после чего переносится прямоугольный диапазон значений и устанавливается высота строк 21 пиксель для сохранения компактного вида.
- После копирования столбец
Bпереключается в текстовый формат, чтобы длинные идентификаторы постов сохранялись без экспоненциальной записи и потери точности. - При необходимости можно ограничить количество копируемых строк, задав переменную
GOOGLE_MAX_STRING_PARSING; значение0отключает ограничение. - По умолчанию используется лист
Data_Po_kagdomy_posty; чтобы выбрать другой лист, задайте переменную окруженияSOURCE_WORKSHEET_NAME.
python -m threads_metrics.main runПеред выполнением убедитесь, что переменные окружения загружены согласно разделу «Конфигурация».
Команда запуска организует асинхронный сбор постов из Threads по токенам с листа accounts_threads,
агрегирует метрики и записывает их на лист Data_Po_kagdomy_posty. Прогресс обработки хранится в
файле состояния, а логи выводятся в формате JSON с heartbeat-сообщениями.
- Загрузка конфигурации. Переменные окружения проверяются и валидируются перед запуском; при ошибке сервис завершится с понятным сообщением.
- Чтение токенов и курсоров. Для каждого аккаунта берётся сохранённый курсор пагинации, чтобы продолжить с места предыдущего запуска. Строки с никнеймами, подсвеченными цветом
#9fc5e8, пропускаются, чтобы не обращаться к забаненным профилям. - Сбор постов. Параллельные запросы ограничены семафором, данные запрашиваются по эндпоинту
https://graph.threads.net/v1.0/me/threads?fields=id,permalink,text,timestamp,media_type,media_url,like_count,repost_count,reply_count, а курсор обновляется только после успешного шага. - Сбор Insights. Для каждого поста проверяется TTL; свежие записи пропускаются, а метрики запрашиваются по эндпоинту
https://graph.threads.net/v1.0/{post_id}/insights?metric=views,likes,replies,reposts,quotes,shares, что заметно сокращает число запросов к API. - Запись в Google Sheets. Данные сливаются с текущей таблицей: строки ищутся по сочетанию
account_nameиpost_id, числовые метрики обновляются без создания дублей, новые колонки добавляются автоматически, а отметкаupdated_atобновляется при каждой выгрузке. Лист полностью перезаписывается, строки сортируются по колонкеtime_publish_(GR_time)(самые старые публикации сверху), перенос текста отключается, а высота строк принудительно выставляется в 21 пиксель. Текущее расположение колонок:time_publish_(GR_time),account_name,post_id,permalink,text,views,likes,replies,reposts,quotes,shares,updated_at.
- Расписание (
*/60 * * * *) запускает сбор каждые 60 минут, при этом блокconcurrencyгарантирует, что одновременно выполняется только один экземпляр workflow — новое срабатывание дождётся завершения предыдущего. - Workflow
sheets-sync.ymlкаждые 15 минут синхронизирует листData_Po_kagdomy_postyмежду таблицамиID_GOOGLE_TABLEиID_GOOGLE_TABLE_PUBLIC_DANNYE, используя тот же сервисный аккаунт и опциональный лимит строк изGOOGLE_MAX_STRING_PARSING. - Джобу ограничивает внешний таймаут в 100 минут, а сам сервис завершится раньше (по умолчанию через 100 минут ожидания, значение настраивается через
THREADS_RUN_TIMEOUT_MIN), если зависнет или получит сигнал остановки. - Для локального запуска используется та же команда
python -m threads_metrics.main run, поэтому отладка и CI ведут себя одинаково.
Для предотвращения накопления очереди workflow threads-metrics.yml добавлена подкоманда cancel-pending:
export GITHUB_OWNER=acme
export GITHUB_REPO=threads-metrics
export GITHUB_TOKEN=ghp_xxx
python -m threads_metrics.main cancel-pending --interval 10Переменные окружения GITHUB_OWNER, GITHUB_REPO и GITHUB_TOKEN обязательны. Токен должен иметь доступ к GitHub Actions выбранного репозитория, чтобы читать список запусков и отправлять запросы отмены.
Скрипт раз в --interval секунд (по умолчанию 10) опрашивает GitHub REST API: ищет активные (in_progress) запуски workflow threads-metrics.yml и отменяет все ожидающие (queued). При превышении лимита запросов он использует экспоненциальный бэкофф и повторяет попытку после паузы, а на SIGINT/SIGTERM корректно завершает работу. Логи остаются в JSON-формате (ts, level, msg, context), поэтому отмена очередей вписывается в существующую систему наблюдаемости.
pytestЧтобы ускорить разработку, можно запускать подмножество тестов:
pytest tests/test_google_sheets.py -k update_rowsКоманда выполнит только проверки, связанные с обновлением строк в Google Sheets, и позволит быстрее отлавливать регрессии в интеграции с таблицей.