Add utils/check_token.py preflight checker and integrate into main.py and scheduler

Agent-Logs-Url: https://github.com/thaitien280401-stack/RedditVideoMakerBot/sessions/adc9d93e-b8a2-4b45-8f6c-50427edeee51

Co-authored-by: thaitien280401-stack <271128961+thaitien280401-stack@users.noreply.github.com>
pull/2482/head
copilot-swe-agent[bot] 3 days ago committed by GitHub
parent b4c6c370b9
commit 46ba3f3bb6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -199,6 +199,12 @@ if __name__ == "__main__":
)
sys.exit()
# Kiểm tra access token trước khi chạy (chỉ cho Threads mode)
if not args.reddit:
from utils.check_token import preflight_check
preflight_check()
try:
if args.mode == "scheduled":
# Chế độ lên lịch tự động

@ -50,6 +50,11 @@ def run_pipeline(post_id: Optional[str] = None) -> Optional[str]:
print_step("🚀 Bắt đầu pipeline tạo video...")
# Preflight: kiểm tra access token trước khi gọi API
from utils.check_token import preflight_check
preflight_check()
try:
# Step 1: Lấy nội dung từ Threads
print_step("📱 Bước 1: Lấy nội dung từ Threads...")

@ -0,0 +1,198 @@
"""
Preflight Access-Token Checker chạy trước khi pipeline bắt đầu.
Kiểm tra:
1. access_token được cấu hình trong config.toml không.
2. Token hợp lệ trên Threads API (/me endpoint) không.
3. Nếu token hết hạn tự động thử refresh.
4. user_id trong config khớp với user sở hữu token không.
Usage:
# Gọi trực tiếp:
python -m utils.check_token
# Hoặc import trong code:
from utils.check_token import preflight_check
preflight_check() # raises SystemExit on failure
"""
import sys
from typing import Optional
import requests
from utils import settings
from utils.console import print_step, print_substep
THREADS_API_BASE = "https://graph.threads.net/v1.0"
_REQUEST_TIMEOUT = 15 # seconds preflight should be fast
class TokenCheckError(Exception):
"""Raised when the access-token preflight fails."""
def _call_me_endpoint(access_token: str) -> dict:
"""GET /me?fields=id,username&access_token=… with minimal retry."""
url = f"{THREADS_API_BASE}/me"
params = {
"fields": "id,username",
"access_token": access_token,
}
response = requests.get(url, params=params, timeout=_REQUEST_TIMEOUT)
# HTTP-level errors
if response.status_code == 401:
raise TokenCheckError(
"Access token không hợp lệ hoặc đã hết hạn (HTTP 401).\n"
"→ Cập nhật [threads.creds] access_token trong config.toml."
)
if response.status_code == 403:
raise TokenCheckError(
"Token thiếu quyền (HTTP 403).\n"
"→ Đảm bảo token có quyền threads_basic_read trong Meta Developer Portal."
)
response.raise_for_status()
data = response.json()
# Graph API may return 200 with an error body
if "error" in data:
err = data["error"]
msg = err.get("message", "Unknown error")
code = err.get("code", 0)
raise TokenCheckError(f"Threads API trả về lỗi: {msg} (code={code})")
return data
def _try_refresh(access_token: str) -> Optional[str]:
"""Attempt to refresh a long-lived Threads token.
Returns new token string, or None if refresh is not possible.
"""
url = f"{THREADS_API_BASE}/refresh_access_token"
try:
resp = requests.get(
url,
params={
"grant_type": "th_refresh_token",
"access_token": access_token,
},
timeout=_REQUEST_TIMEOUT,
)
resp.raise_for_status()
data = resp.json()
if "error" in data:
return None
return data.get("access_token") or None
except requests.RequestException:
return None
def preflight_check() -> None:
"""Validate the Threads access token configured in *config.toml*.
On success, prints a confirmation and returns normally.
On failure, prints actionable diagnostics and raises ``SystemExit(1)``.
"""
print_step("🔑 Kiểm tra access token trước khi chạy...")
# --- 1. Check config values exist -----------------------------------
try:
threads_creds = settings.config["threads"]["creds"]
access_token: str = threads_creds.get("access_token", "").strip()
user_id: str = threads_creds.get("user_id", "").strip()
except (KeyError, TypeError):
print_substep(
"❌ Thiếu cấu hình [threads.creds] trong config.toml.\n"
" Cần có access_token và user_id.",
style="bold red",
)
sys.exit(1)
if not access_token:
print_substep(
"❌ access_token trống trong config.toml!\n"
" Lấy token tại: https://developers.facebook.com/docs/threads/get-started",
style="bold red",
)
sys.exit(1)
if not user_id:
print_substep(
"❌ user_id trống trong config.toml!\n"
" Lấy user_id bằng cách gọi /me với access token.",
style="bold red",
)
sys.exit(1)
# --- 2. Validate token via /me endpoint -----------------------------
try:
me_data = _call_me_endpoint(access_token)
except TokenCheckError as exc:
# Token invalid → try refresh
print_substep(
f"⚠️ Token hiện tại không hợp lệ: {exc}\n" " Đang thử refresh token...",
style="bold yellow",
)
new_token = _try_refresh(access_token)
if new_token:
try:
me_data = _call_me_endpoint(new_token)
access_token = new_token
# Update in-memory config so downstream code uses the new token
settings.config["threads"]["creds"]["access_token"] = new_token
print_substep("✅ Token đã được refresh thành công!", style="bold green")
except TokenCheckError as inner:
print_substep(
f"❌ Token mới sau refresh vẫn lỗi: {inner}\n"
" Vui lòng lấy token mới từ Meta Developer Portal:\n"
" https://developers.facebook.com/docs/threads/get-started",
style="bold red",
)
sys.exit(1)
else:
print_substep(
"❌ Không thể refresh token.\n"
" Vui lòng lấy token mới từ Meta Developer Portal:\n"
" https://developers.facebook.com/docs/threads/get-started",
style="bold red",
)
sys.exit(1)
except requests.RequestException as exc:
print_substep(
f"❌ Lỗi kết nối khi kiểm tra token: {exc}\n" " Kiểm tra kết nối mạng và thử lại.",
style="bold red",
)
sys.exit(1)
# --- 3. Cross-check user_id ----------------------------------------
api_user_id = me_data.get("id", "")
api_username = me_data.get("username", "N/A")
if api_user_id and api_user_id != user_id:
print_substep(
f"⚠️ user_id trong config ({user_id}) khác với user sở hữu token ({api_user_id}).\n"
" Nếu bạn muốn lấy threads của chính mình, hãy cập nhật user_id trong config.toml.\n"
" Đang tiếp tục với token hiện tại...",
style="bold yellow",
)
print_substep(
f"✅ Access token hợp lệ — @{api_username} (ID: {api_user_id})",
style="bold green",
)
# Allow running standalone: python -m utils.check_token
if __name__ == "__main__":
from pathlib import Path
directory = Path().absolute()
settings.check_toml(
f"{directory}/utils/.config.template.toml",
f"{directory}/config.toml",
)
preflight_check()
print_step("🎉 Tất cả kiểm tra đều OK — sẵn sàng chạy!")
Loading…
Cancel
Save