feat(collector): implement historical channel scraper command and ui guide

This commit is contained in:
mamad
2026-08-27 21:05:06 +03:30
parent 00aca80180
commit 963d7f365f
2 changed files with 114 additions and 5 deletions
+77 -1
View File
@@ -65,7 +65,6 @@ class CollectorService:
async def submit_code(self, code: str) -> str:
if not self.phone or not self.phone_code_hash:
# Re-request code
sent = await self.client.send_code_request(self.phone)
self.phone_code_hash = sent.phone_code_hash
@@ -144,6 +143,83 @@ class CollectorService:
except Exception as e:
logger.error(f"Error handling message from {event.chat_id}: {e}", exc_info=True)
async def scrape_channel_history(
self,
channel_id: int,
limit: int = 20,
progress_callback: Optional[Callable[[str], Awaitable[None]]] = None
) -> int:
"""Scrape historical messages from a source channel."""
if not self.client.is_connected() or not await self.client.is_user_authorized():
if progress_callback:
await progress_callback("❌ Collector Userbot is not authorized. Please log in first.")
return 0
collected_count = 0
skipped_count = 0
try:
entity = await self.client.get_input_entity(channel_id)
messages = []
async for msg in self.client.iter_messages(entity, limit=limit):
messages.append(msg)
messages.reverse()
for message in messages:
raw_text = message.raw_text or ""
if not raw_text and not message.media:
continue
media_path = None
media_type = None
media_hash = None
if message.media:
if isinstance(message.media, MessageMediaPhoto):
media_type = "photo"
elif isinstance(message.media, MessageMediaDocument):
media_type = "document"
else:
media_type = "other"
filename = f"{channel_id}_{message.id}"
download_target = os.path.join(MEDIA_DIR, filename)
downloaded_file = await message.download_media(file=download_target)
if downloaded_file:
media_path = downloaded_file
media_hash = compute_file_hash(downloaded_file)
content_hash = compute_content_hash(raw_text, media_hash)
post_id = await self.repo.create_raw_post(
source_channel_id=channel_id,
source_message_id=message.id,
raw_text=raw_text,
media_path=media_path,
media_type=media_type,
content_hash=content_hash,
)
if post_id:
collected_count += 1
COLLECTED_POSTS_TOTAL.labels(source_channel_id=str(channel_id)).inc()
logger.info(f"Backfilled historical post ID {post_id} from {channel_id}")
await self.ai_processor.process_post(post_id)
else:
skipped_count += 1
if progress_callback:
await progress_callback(
f"✅ Scraped <b>{collected_count}</b> new historical posts from <code>{channel_id}</code> (Skipped {skipped_count} existing/empty)."
)
return collected_count
except Exception as e:
logger.error(f"Error scraping history from {channel_id}: {e}", exc_info=True)
if progress_callback:
await progress_callback(f"❌ Error scraping channel <code>{channel_id}</code>: {e}")
return collected_count
async def stop(self):
if self.client.is_connected():
await self.client.disconnect()