diff --git a/services/admin_bot.py b/services/admin_bot.py
index a408516..52ec4f9 100644
--- a/services/admin_bot.py
+++ b/services/admin_bot.py
@@ -17,7 +17,7 @@ def get_main_menu_keyboard():
[Button.text("🔑 Request Login Code", resize=True), Button.text("📊 Fleet Statistics", resize=True)],
[Button.text("📡 Monitored Sources", resize=True), Button.text("🎯 Target Channels", resize=True)],
[Button.text("➕ Add Source Guide", resize=True), Button.text("➕ Add Target Guide", resize=True)],
- [Button.text("📥 Scrape History Guide", resize=True), Button.text("❓ Help & Documentation", resize=True)]
+ [Button.text("❓ Help & Documentation", resize=True)]
]
class AdminBotService:
@@ -137,21 +137,7 @@ class AdminBotService:
result = await self.collector.submit_password(pwd)
await status_msg.edit(result, parse_mode="html", buttons=get_main_menu_keyboard())
- # --- History Scraper Commands ---
- @self.client.on(events.NewMessage(pattern=r"(?i)^(📥 Scrape History Guide)$"))
- async def cmd_scrape_guide(event: events.NewMessage.Event):
- if not self.is_admin(event.sender_id):
- return
- guide = (
- "📥 Historical Channel Scraper:\n\n"
- "Import previous/past posts from any channel:\n"
- "/scrape_history <channel_id> [number_of_posts]\n\n"
- "Example (Scrape last 30 posts):\n"
- "/scrape_history -1001234567890 30\n\n"
- "(Default is 20 posts if count is omitted)."
- )
- await event.reply(guide, parse_mode="html")
-
+ # --- Direct History Scraper Command ---
@self.client.on(events.NewMessage(pattern=r"^/scrape_history\s+(-?\d+)(?:\s+(\d+))?"))
async def cmd_scrape_history(event: events.NewMessage.Event):
if not self.is_admin(event.sender_id):
@@ -191,19 +177,31 @@ class AdminBotService:
)
await event.reply(text, parse_mode="html", buttons=get_main_menu_keyboard())
- # --- Sources Management ---
+ # --- Sources Management with Interactive Scrape Buttons ---
@self.client.on(events.NewMessage(pattern=r"(?i)^(/sources|📡 Monitored Sources)$"))
async def cmd_sources(event: events.NewMessage.Event):
if not self.is_admin(event.sender_id):
return
sources = await self.repo.get_active_sources()
if not sources:
- await event.reply("No active source channels configured.\nUse ➕ Add Source Guide to add one.", parse_mode="html")
+ await event.reply("No active source channels configured.\nTap ➕ Add Source Guide below to add one.", parse_mode="html", buttons=get_main_menu_keyboard())
return
- lines = ["📡 Active Monitored Sources:\n"]
+
+ await event.reply(f"📡 Monitored Sources ({len(sources)} Active):\nTap any button below to scrape past posts:", parse_mode="html")
+
for s in sources:
- lines.append(f"• ID: {s.channel_id} | {s.title or 'N/A'} (@{s.username or 'none'})")
- await event.reply("\n".join(lines), parse_mode="html", buttons=get_main_menu_keyboard())
+ card = (
+ f"📢 {s.title or 'Channel'}\n"
+ f"• ID: {s.channel_id}\n"
+ f"• Username: @{s.username or 'none'}"
+ )
+ buttons = [
+ [
+ Button.inline(f"📥 Scrape 20 Posts", data=f"hist:{s.channel_id}:20"),
+ Button.inline(f"📥 Scrape 50 Posts", data=f"hist:{s.channel_id}:50"),
+ ]
+ ]
+ await event.reply(card, parse_mode="html", buttons=buttons)
@self.client.on(events.NewMessage(pattern=r"(?i)^(➕ Add Source Guide)$"))
async def cmd_add_source_guide(event: events.NewMessage.Event):
@@ -226,7 +224,18 @@ class AdminBotService:
title = event.pattern_match.group(2)
username = event.pattern_match.group(3)
await self.repo.add_source(channel_id=ch_id, title=title, username=username)
- await event.reply(f"✅ Added source channel {title} ({ch_id}) to monitoring.", parse_mode="html", buttons=get_main_menu_keyboard())
+
+ buttons = [
+ [
+ Button.inline(f"📥 Scrape 20 Posts Now", data=f"hist:{ch_id}:20"),
+ Button.inline(f"📥 Scrape 50 Posts Now", data=f"hist:{ch_id}:50")
+ ]
+ ]
+ await event.reply(
+ f"✅ Added source channel {title} ({ch_id}).\n\nWould you like to scrape past posts now?",
+ parse_mode="html",
+ buttons=buttons
+ )
# --- Targets Management ---
@self.client.on(events.NewMessage(pattern=r"(?i)^(/targets|🎯 Target Channels)$"))
@@ -290,15 +299,13 @@ class AdminBotService:
return
help_text = (
"📖 Copykar Bot Quick Help\n\n"
- "1. Authentication: Click 🔑 Request Login Code and submit via /code <12345>.\n"
- "2. Monitored Sources: Add channels via /add_source.\n"
- "3. Historical Posts: Backfill existing posts using /scrape_history <channel_id> [limit].\n"
- "4. Review Flow: AI scans posts, checks duplicates, and sends drafts to the review channel with inline approval buttons.\n"
- "5. Publishing: Approved posts are published to your target channels strictly according to their interval minutes."
+ "1. Monitored Sources: Tap 📡 Monitored Sources to view channels and click [📥 Scrape Posts] on any channel.\n"
+ "2. Review Flow: AI scans posts, checks duplicates, and sends drafts to the review channel with inline approval buttons.\n"
+ "3. Publishing: Approved posts are published to your target channels strictly according to their interval minutes."
)
await event.reply(help_text, parse_mode="html", buttons=get_main_menu_keyboard())
- # --- Review Keyboard Callbacks ---
+ # --- Inline Callback Queries ---
@self.client.on(events.CallbackQuery)
async def on_callback(event: events.CallbackQuery.Event):
if not self.is_admin(event.sender_id):
@@ -306,7 +313,27 @@ class AdminBotService:
return
data = event.data.decode("utf-8")
- if data.startswith("appr:"):
+
+ # 1. Historical Scraping Callbacks
+ if data.startswith("hist:"):
+ _, ch_id_str, limit_str = data.split(":")
+ ch_id = int(ch_id_str)
+ limit = int(limit_str)
+
+ if not self.collector:
+ await event.answer("Collector service not linked.", alert=True)
+ return
+
+ await event.edit(f"⏳ Scraping the last {limit} posts from {ch_id}...", parse_mode="html", buttons=None)
+
+ async def progress_notify(txt: str):
+ await event.edit(txt, parse_mode="html")
+
+ await self.collector.scrape_channel_history(channel_id=ch_id, limit=limit, progress_callback=progress_notify)
+ await event.answer(f"Started scraping {limit} posts!")
+
+ # 2. Approval Callbacks
+ elif data.startswith("appr:"):
_, post_id_str, target_id_str = data.split(":")
post_id = int(post_id_str)
target_id = int(target_id_str)
@@ -324,6 +351,7 @@ class AdminBotService:
)
await event.answer(f"Approved for {target_title}!")
+ # 3. Reject Callbacks
elif data.startswith("rej:"):
_, post_id_str = data.split(":")
post_id = int(post_id_str)