feat: make scraper and job service account-aware

- OptimizedTelegramScraper accepts account_id parameter;
  sets DATA_DIR to data/accounts/<id>/ when provided
- Session path uses account_session_path() for per-account session files
- initialize_client() reads per-account credentials from account state store
- ScraperJobService passes account_id from payload to scraper constructor
- Per-account state loaded from AccountStateStore before scraping
This commit is contained in:
2026-06-27 15:20:15 +02:00
parent 6e3966aeb5
commit 220d04b3a6
2 changed files with 34 additions and 9 deletions
+12 -3
View File
@@ -1,6 +1,6 @@
import asyncio import asyncio
import logging import logging
from typing import Any, Dict, List from typing import Any, Dict, List, Optional
from app_state import StateStore from app_state import StateStore
@@ -25,12 +25,21 @@ class ScraperJobService:
asyncio.run(self._run_async(job_type, payload)) asyncio.run(self._run_async(job_type, payload))
async def _run_async(self, job_type: str, payload: Dict[str, Any]) -> None: async def _run_async(self, job_type: str, payload: Dict[str, Any]) -> None:
# Extract account_id from payload, default to None (legacy)
account_id: Optional[str] = payload.get("account_id")
ScraperClass = self._import_scraper_class() ScraperClass = self._import_scraper_class()
scraper = ScraperClass() scraper = ScraperClass(account_id=account_id)
if account_id:
from app_state import load_account
acc_state = load_account(self.state_store.path.parent, account_id)
# Load per-account state into scraper state
scraper.state = acc_state
initialized = await scraper.initialize_client(interactive=False) initialized = await scraper.initialize_client(interactive=False)
if not initialized: if not initialized:
raise RuntimeError( raise RuntimeError(
"Telegram client is not ready. Check credentials, session, and write access to /app/session." "Telegram client is not ready. Check credentials, session, and write access."
) )
try: try:
if job_type == "scrape_channel": if job_type == "scrape_channel":
+22 -6
View File
@@ -21,7 +21,14 @@ from telethon.tl.types import (
) )
from telethon.errors import FloodWaitError, SessionPasswordNeededError from telethon.errors import FloodWaitError, SessionPasswordNeededError
import qrcode import qrcode
from app_state import StateStore from app_state import (
StateStore,
account_data_dir,
account_session_path,
get_account_store,
load_account,
save_account,
)
warnings.filterwarnings( warnings.filterwarnings(
"ignore", message="Using async sessions support is an experimental feature" "ignore", message="Using async sessions support is an experimental feature"
@@ -73,13 +80,21 @@ class ForwardingRule:
class OptimizedTelegramScraper: class OptimizedTelegramScraper:
def __init__(self): def __init__(self, account_id: Optional[str] = None):
self.DATA_DIR = Path("data") self.account_id = account_id
self.SESSION_DIR = Path("session") self.SESSION_DIR = Path("session")
self.DATA_DIR.mkdir(exist_ok=True)
self.SESSION_DIR.mkdir(exist_ok=True) self.SESSION_DIR.mkdir(exist_ok=True)
if account_id:
self.DATA_DIR = Path("data") / "accounts" / account_id
self.state_store = get_account_store(Path("data"), account_id)
else:
self.DATA_DIR = Path("data")
self.state_store = StateStore(self.DATA_DIR / "state.json")
self.DATA_DIR.mkdir(parents=True, exist_ok=True)
self.STATE_FILE = str(self.DATA_DIR / "state.json") self.STATE_FILE = str(self.DATA_DIR / "state.json")
self.state_store = StateStore(self.DATA_DIR / "state.json")
self.state = self.load_state() self.state = self.load_state()
self.client = None self.client = None
self.continuous_scraping_active = False self.continuous_scraping_active = False
@@ -1480,8 +1495,9 @@ class OptimizedTelegramScraper:
print("Invalid API ID. Must be a number.") print("Invalid API ID. Must be a number.")
return False return False
session = account_session_path(self.SESSION_DIR, self.account_id or "session")
self.client = TelegramClient( self.client = TelegramClient(
str(self.SESSION_DIR / "session"), session,
self.state["api_id"], self.state["api_id"],
self.state["api_hash"], self.state["api_hash"],
) )