AI Update: شروع به پیاده سازی کن

This commit is contained in:
Antigravity Bot
2026-08-30 11:54:50 +03:30
parent c2a3cdc085
commit a5342b0850
4 changed files with 648 additions and 23 deletions
+420 -16
View File
@@ -69,7 +69,8 @@ def get_source_fetch_buttons(channel_id: int, source_id: Optional[int] = None):
]
if source_id is not None:
rows.append([
Button.inline("📜 تنظیم پیام‌های زمینه (Context)", data=f"src_ctx_menu:{source_id}"),
Button.inline("📜 پیام‌های زمینه (Context)", data=f"src_ctx_menu:{source_id}"),
Button.inline("📋 کانال نظارت ادمین", data=f"src_adm:{source_id}"),
])
rows.append([
Button.inline("📁 تعیین دسته‌بندی", data=f"src_cat:{source_id}"),
@@ -663,9 +664,31 @@ class AdminBotService:
)
return caption
async def get_effective_review_channel_id(self, post: Post) -> Optional[int]:
"""Determine which Admin Review Channel this post should be sent to."""
if post.source_channel_id <= -900000000:
site_id = abs(post.source_channel_id + 900000000)
site = await self.repo.get_source_website_by_id(site_id)
if site and site.admin_channel_id:
return site.admin_channel_id
else:
src = await self.repo.get_source_by_channel_id(post.source_channel_id)
if src and src.admin_channel_id:
return src.admin_channel_id
default_adm = await self.repo.get_default_admin_channel()
if default_adm and default_adm.channel_id:
return default_adm.channel_id
return self.review_channel_id or None
async def send_raw_review_post(self, post_id: int, refresh_only: bool = False):
post = await self.repo.get_post_by_id(post_id)
if not post or not self.review_channel_id or post.is_deleted:
if not post or post.is_deleted:
return
target_review_channel_id = post.review_channel_id or await self.get_effective_review_channel_id(post)
if not target_review_channel_id:
return
targets = await self.repo.get_active_targets()
@@ -678,7 +701,7 @@ class AdminBotService:
return
try:
await self.client.edit_message(
self.review_channel_id, post.review_message_id,
target_review_channel_id, post.review_message_id,
clamp_for_telegram(caption, bool(post.media_path)),
parse_mode="html", buttons=keyboard,
)
@@ -692,7 +715,7 @@ class AdminBotService:
try:
if has_media:
msg = await self.client.send_file(
self.review_channel_id,
target_review_channel_id,
file=post.media_path,
caption=caption,
parse_mode="html",
@@ -700,14 +723,14 @@ class AdminBotService:
)
else:
msg = await self.client.send_message(
self.review_channel_id,
target_review_channel_id,
caption,
parse_mode="html",
buttons=keyboard
)
await self.repo.update_review_message_id(post.id, msg.id)
await self.repo.update_review_message_id(post.id, msg.id, review_channel_id=target_review_channel_id)
except Exception as e:
await log_exception("admin_bot.send_review_post", e, {"post_id": post.id, "review_channel_id": self.review_channel_id})
await log_exception("admin_bot.send_review_post", e, {"post_id": post.id, "review_channel_id": target_review_channel_id})
async def refresh_error_metrics(self) -> int:
"""Republish the open-error gauges from the database. Returns the open count."""
@@ -954,6 +977,14 @@ class AdminBotService:
if cat:
cat_name = cat.name
adm_name = "پیش‌فرض سیستم"
if getattr(source, "admin_channel_id", None):
adm = await self.repo.get_admin_channel_by_telegram_id(source.admin_channel_id)
if adm:
adm_name = f"<b>{adm.title or adm.channel_id}</b>"
else:
adm_name = f"<code>{source.admin_channel_id}</code>"
auto_targets = await self.repo.get_targets_auto_routed_from(source.channel_id)
auto_line = (
"🤖 <b>ارسال خودکار به:</b> " + "، ".join(f"<b>{t.title}</b>" for t in auto_targets)
@@ -968,6 +999,7 @@ class AdminBotService:
f"• 🆔 <b>شناسه کانال:</b> <code>{source.channel_id}</code>\n"
f"• 🔗 <b>یوزرنیم:</b> @{source.username or 'ندارد'}\n"
f"• 📁 <b>دسته‌بندی:</b> <b>{cat_name}</b>\n"
f"• 📋 <b>کانال نظارت ادمین:</b> {adm_name}\n"
f"• 📜 <b>پیام‌های زمینه (Context):</b> <b>{ctx_label}</b>\n"
f"• 📥 <b>پست‌های دریافت‌شده:</b> <b>{collected}</b>\n"
f"{auto_line}\n\n"
@@ -1053,12 +1085,21 @@ class AdminBotService:
last_re = site.last_reanalyzed_at.strftime("%Y-%m-%d %H:%M") if site.last_reanalyzed_at else "انجام نشده"
needs = getattr(site, "custom_instructions", "") or ""
adm_name = "پیش‌فرض سیستم"
if getattr(site, "admin_channel_id", None):
adm = await self.repo.get_admin_channel_by_telegram_id(site.admin_channel_id)
if adm:
adm_name = f"<b>{adm.title or adm.channel_id}</b>"
else:
adm_name = f"<code>{site.admin_channel_id}</code>"
text = (
f"🌐 <b>وبسایت مبدا:</b> <b>{site.name}</b>\n\n"
f"• 🔗 <b>آدرس وبسایت:</b> {site.url}\n"
f"• 🔌 <b>اندپوینت دریافت:</b> <code>{endpoint}</code>\n"
f"• 🏷 <b>نوع ساختار:</b> <code>{parser_type}</code>\n"
f"• 📁 <b>دسته‌بندی:</b> <b>{cat_name}</b>\n"
f"• 📋 <b>کانال نظارت ادمین:</b> {adm_name}\n"
f"• 📝 <b>نیازمندی‌ها و فیلترهای استخراج:</b>\n"
f"<i>{needs or 'ثبت نشده (استخراج تمام اخبار و پست‌ها)'}</i>\n\n"
f"• ⏱ <b>فاصله بررسی:</b> هر <b>{site.check_interval_min} دقیقه</b>\n"
@@ -1078,11 +1119,14 @@ class AdminBotService:
Button.inline("⏱ تغییر فاصله بررسی", data=f"web_intv:{site.id}")
],
[
Button.inline("🔄 دوره تحلیل مجدد AI", data=f"web_reintv:{site.id}"),
Button.inline("📋 کانال نظارت ادمین", data=f"web_adm:{site.id}"),
Button.inline("📁 تعیین دسته‌بندی", data=f"web_cat:{site.id}")
],
[
Button.inline("🗑 حذف این وبسایت", data=f"del_web:{site.id}"),
Button.inline("🔄 دوره تحلیل مجدد AI", data=f"web_reintv:{site.id}"),
Button.inline("🗑 حذف این وبسایت", data=f"del_web:{site.id}")
],
[
Button.inline("🔙 بازگشت به لیست وبسایت‌ها", data="list_web")
]
]
@@ -1144,7 +1188,10 @@ class AdminBotService:
Button.inline("➕ افزودن مبدا / مقصد جدید", data="hub_add"),
Button.inline("📂 دسته‌بندی موضوعی کانال‌ها", data="hub_cats")
],
[Button.inline("🔙 بازگشت به منوی اصلی", data="back_to_main")]
[
Button.inline("🗑 پاکسازی پیام‌های کانال‌های ادمین", data="purge_adm_menu"),
Button.inline("🔙 بازگشت به منوی اصلی", data="back_to_main")
]
]
return text, buttons
@@ -1152,11 +1199,13 @@ class AdminBotService:
sources = await self.repo.get_active_sources()
targets = await self.repo.get_active_targets()
websites = await self.repo.get_active_source_websites()
adm_channels = await self.repo.get_admin_channels()
text = (
f"🤖 <b>بخش کانال‌ها، وبسایت‌ها و ربات‌ها (Bots Hub):</b>\n\n"
f"• 📡 <b>کانال‌های مبدا:</b> {len(sources)} کانال\n"
f"• 🎯 <b>کانال‌های مقصد:</b> {len(targets)} کانال\n"
f"• 🌐 <b>وبسایت‌های مبدا:</b> {len(websites)} وبسایت\n\n"
f"• 🌐 <b>وبسایت‌های مبدا:</b> {len(websites)} وبسایت\n"
f"• 📋 <b>کانال‌های نظارت ادمین:</b> {len(adm_channels)} کانال ثبت‌شده\n\n"
f"<i>بخش مورد نظر را برای مشاهده و مدیریت انتخاب کنید:</i>"
)
buttons = [
@@ -1173,10 +1222,13 @@ class AdminBotService:
Button.inline(" افزودن وبسایت", data="add_web")
],
[
Button.inline("🔑 احراز هویت یوزربات", data="hub_req_code"),
Button.inline("📋 کانال‌های نظارت ادمین", data="list_adm_channels"),
Button.inline("📂 مدیریت دسته‌بندی‌ها", data="list_cat")
],
[Button.inline("🔙 بازگشت به منوی اصلی", data="back_to_main")]
[
Button.inline("🔑 احراز هویت یوزربات", data="hub_req_code"),
Button.inline("🔙 بازگشت به منوی اصلی", data="back_to_main")
]
]
return text, buttons
@@ -1194,13 +1246,198 @@ class AdminBotService:
buttons = [
[Button.inline(pause_btn_text, data=pause_cb)],
[
Button.inline("📝 گزارش تغییرات (Release Notes)", data="sys_changes"),
Button.inline("❓ راهنمای سیستم", data="sys_help")
Button.inline("🗑 پاکسازی پیام‌های کانال‌های ادمین", data="purge_adm_menu"),
Button.inline("📝 گزارش تغییرات", data="sys_changes")
],
[Button.inline("🔙 بازگشت به منوی اصلی", data="back_to_main")]
[
Button.inline("❓ راهنمای سیستم", data="sys_help"),
Button.inline("🔙 بازگشت به منوی اصلی", data="back_to_main")
]
]
return text, buttons
async def _render_source_admin_channel_menu(self, source_id: int) -> Tuple[str, List[List[Button]]]:
source = await self.repo.get_source_by_id(source_id)
if not source:
return "❌ کانال مبدا یافت نشد.", []
adm_channels = await self.repo.get_admin_channels()
text = (
f"📋 <b>تعیین کانال نظارت ادمین برای کانال مبدا:</b>\n"
f"📢 <b>{source.title or source.channel_id}</b>\n\n"
f"پست‌های جدید این کانال مبدا برای بررسی به کدام کانال مدیر/نظارت ارسال شوند؟\n\n"
f"<i>یکی از کانال‌های زیر را انتخاب کنید یا گزینه پیش‌فرض را بزنید:</i>"
)
buttons = []
for a in adm_channels:
mark = "" if source.admin_channel_id == a.channel_id else ""
def_tag = " (پیش‌فرض)" if a.is_default else ""
buttons.append([Button.inline(f"{mark}📋 {a.title or a.channel_id}{def_tag}", data=f"src_set_adm:{source.id}:{a.channel_id}")])
is_def = (source.admin_channel_id is None)
def_mark = "" if is_def else ""
buttons.append([Button.inline(f"{def_mark}⚙️ پیش‌فرض سیستم ({self.review_channel_id})", data=f"src_set_adm:{source.id}:none")])
buttons.append([Button.inline("➕ افزودن کانال ادمین جدید", data=f"src_add_adm:{source.id}")])
buttons.append([Button.inline("🔙 بازگشت به تنظیمات کانال مبدا", data=f"src_view:{source.id}")])
return text, buttons
async def _render_website_admin_channel_menu(self, site_id: int) -> Tuple[str, List[List[Button]]]:
site = await self.repo.get_source_website_by_id(site_id)
if not site:
return "❌ وبسایت یافت نشد.", []
adm_channels = await self.repo.get_admin_channels()
text = (
f"📋 <b>تعیین کانال نظارت ادمین برای وبسایت مبدا:</b>\n"
f"🌐 <b>{site.name}</b>\n\n"
f"پست‌های استخراج‌شده از این وبسایت برای بررسی به کدام کانال مدیر/نظارت ارسال شوند؟\n\n"
f"<i>یکی از کانال‌های زیر را انتخاب کنید یا گزینه پیش‌فرض را بزنید:</i>"
)
buttons = []
for a in adm_channels:
mark = "" if site.admin_channel_id == a.channel_id else ""
def_tag = " (پیش‌فرض)" if a.is_default else ""
buttons.append([Button.inline(f"{mark}📋 {a.title or a.channel_id}{def_tag}", data=f"web_set_adm:{site.id}:{a.channel_id}")])
is_def = (site.admin_channel_id is None)
def_mark = "" if is_def else ""
buttons.append([Button.inline(f"{def_mark}⚙️ پیش‌فرض سیستم ({self.review_channel_id})", data=f"web_set_adm:{site.id}:none")])
buttons.append([Button.inline("➕ افزودن کانال ادمین جدید", data=f"web_add_adm:{site.id}")])
buttons.append([Button.inline("🔙 بازگشت به تنظیمات وبسایت", data=f"web_view:{site.id}")])
return text, buttons
async def _render_admin_channels_menu(self) -> Tuple[str, List[List[Button]]]:
channels = await self.repo.get_admin_channels()
lines = [
"📋 <b>مدیریت کانال‌های نظارت و بازبینی ادمین (Admin Review Channels)</b>\n",
"شما می‌توانید چندین کانال ادمین مجزا تعریف کنید تا پست‌های منابع مختلف به کانال‌های مدیر متفاوتی هدایت شوند:\n"
]
buttons = []
if channels:
for ch in channels:
def_star = " ⭐️ (پیش‌فرض)" if ch.is_default else ""
lines.append(f"• <b>{ch.title or 'کانال ادمین'}</b> (<code>{ch.channel_id}</code>){def_star}")
buttons.append([
Button.inline(f"📋 {ch.title or ch.channel_id}{def_star}", data=f"adm_view:{ch.id}"),
])
else:
lines.append(f"<i>تنها کانال پیش‌فرض محیطی فعال است: <code>{self.review_channel_id}</code></i>")
buttons.append([Button.inline("➕ افزودن کانال ادمین جدید", data="add_adm_ch")])
buttons.append([Button.inline("🔙 بازگشت به منوی ربات‌ها", data="hub_bots")])
return "\n".join(lines), buttons
async def _render_admin_channel_detail(self, admin_id: int) -> Tuple[str, List[List[Button]]]:
adm = await self.repo.get_admin_channel_by_id(admin_id)
if not adm:
return "❌ کانال ادمین یافت نشد.", [[Button.inline("🔙 بازگشت", data="list_adm_channels")]]
pending_count = await self.repo.count_review_posts_by_admin_channel(adm.channel_id)
def_line = "⭐️ <b>وضعیت:</b> کانال پیش‌فرض سیستم" if adm.is_default else "⚪️ <b>وضعیت:</b> کانال فرعی"
text = (
f"📋 <b>مشخصات کانال نظارت ادمین:</b> <b>{adm.title or adm.channel_id}</b>\n\n"
f"• 🆔 <b>شناسه عددی:</b> <code>{adm.channel_id}</code>\n"
f"• 🔗 <b>یوزرنیم:</b> @{adm.username or 'ندارد'}\n"
f"{def_line}\n"
f"• 📨 <b>پیام‌های بررسی‌نشده در این کانال:</b> <b>{pending_count}</b> پیام\n\n"
f"<i>عملیات مورد نظر را انتخاب کنید:</i>"
)
buttons = []
if not adm.is_default:
buttons.append([Button.inline("⭐️ تعیین به‌عنوان پیش‌فرض سیستم", data=f"adm_set_def:{adm.id}")])
buttons.append([Button.inline(f"🗑 پاکسازی پیام‌های این کانال ({pending_count})", data=f"purge_adm_ask:{adm.channel_id}")])
buttons.append([Button.inline("❌ حذف این کانال ادمین", data=f"adm_del:{adm.id}")])
buttons.append([Button.inline("🔙 بازگشت به لیست کانال‌های ادمین", data="list_adm_channels")])
return text, buttons
async def _render_purge_admin_channels_menu(self) -> Tuple[str, List[List[Button]]]:
adm_channels = await self.repo.get_admin_channels()
all_channels_map: Dict[int, str] = {}
if self.review_channel_id:
all_channels_map[self.review_channel_id] = "کانال پیش‌فرض محیطی"
for a in adm_channels:
all_channels_map[a.channel_id] = a.title or str(a.channel_id)
lines = [
"🗑 <b>پاکسازی پیام‌های کانال‌های ادمین (Purge Review Messages)</b>\n",
"با استفاده از این بخش می‌توانید با یک دکمه، تمام کارت‌ها و پیام‌های ارسال‌شده به کانال‌های مدیران را به‌صورت تفکیک‌شده یا یکجا پاکسازی کنید:\n"
]
buttons = []
total_pending = 0
for ch_id, title in all_channels_map.items():
cnt = await self.repo.count_review_posts_by_admin_channel(ch_id)
total_pending += cnt
lines.append(f"• 📋 <b>{title}</b> (<code>{ch_id}</code>): <b>{cnt}</b> پیام بررسی‌نشده")
buttons.append([
Button.inline(f"🗑 پاکسازی پیام‌های {title[:20]} ({cnt})", data=f"purge_adm_ask:{ch_id}")
])
if total_pending > 0 or len(all_channels_map) > 1:
buttons.append([Button.inline(f"🔥 پاکسازی همه کانال‌های ادمین (مجموع {total_pending})", data="purge_adm_ask:all")])
buttons.append([Button.inline("🔙 بازگشت به منوی سیستم", data="hub_system")])
return "\n".join(lines), buttons
async def purge_review_messages(self, channel_id_or_all: str) -> Tuple[int, int, str]:
"""
Deletes review messages from the specified admin channel (or all admin channels).
Returns: (deleted_tg_count, cleared_db_count, summary_message)
"""
deleted_tg_count = 0
cleared_db_count = 0
if channel_id_or_all == "all":
posts = await self.repo.clear_all_review_messages()
cleared_db_count = len(posts)
by_channel: Dict[int, List[int]] = {}
for p in posts:
if p.media_path and os.path.exists(p.media_path):
try:
os.remove(p.media_path)
except Exception:
pass
ch_id = p.review_channel_id or self.review_channel_id
if ch_id and p.review_message_id:
by_channel.setdefault(ch_id, []).append(p.review_message_id)
for ch_id, msg_ids in by_channel.items():
for chunk_idx in range(0, len(msg_ids), 100):
chunk = msg_ids[chunk_idx:chunk_idx+100]
try:
await self.client.delete_messages(ch_id, chunk)
deleted_tg_count += len(chunk)
except Exception as e:
logger.warning(f"Error deleting chunk from channel {ch_id}: {e}")
summary = f"🔥 <b>تمام پیام‌های کانال‌های نظارت پاکسازی شدند:</b>\n\n• 🗑 پیام‌های حذف‌شده از تلگرام: <b>{deleted_tg_count}</b>\n• 🗂 پست‌های بایگانی‌شده در سیستم: <b>{cleared_db_count}</b>"
else:
ch_id = int(channel_id_or_all)
posts = await self.repo.clear_review_messages_for_channel(ch_id)
cleared_db_count = len(posts)
msg_ids = []
for p in posts:
if p.media_path and os.path.exists(p.media_path):
try:
os.remove(p.media_path)
except Exception:
pass
if p.review_message_id:
msg_ids.append(p.review_message_id)
for chunk_idx in range(0, len(msg_ids), 100):
chunk = msg_ids[chunk_idx:chunk_idx+100]
try:
await self.client.delete_messages(ch_id, chunk)
deleted_tg_count += len(chunk)
except Exception as e:
logger.warning(f"Error deleting chunk from channel {ch_id}: {e}")
summary = f"🗑 <b>پیام‌های کانال نظارت <code>{ch_id}</code> پاکسازی شدند:</b>\n\n• 🗑 پیام‌های حذف‌شده از تلگرام: <b>{deleted_tg_count}</b>\n• 🗂 پست‌های بایگانی‌شده در سیستم: <b>{cleared_db_count}</b>"
return deleted_tg_count, cleared_db_count, summary
async def _render_monitor_hub(self) -> Tuple[str, List[List[Button]]]:
rep = await get_instant_metrics_report()
buttons = [
@@ -1597,6 +1834,22 @@ class AdminBotService:
text, buttons = await self._render_categories_menu()
await event.reply(text, parse_mode="html", buttons=buttons or None)
# --- Admin Review Channels Management ---
@self.client.on(events.NewMessage(pattern=r"(?i)^(/admin_channels|/review_channels|📋 کانال‌های نظارت|📋 کانال‌های نظارت ادمین)$"))
async def cmd_admin_channels(event: events.NewMessage.Event):
if not self.is_admin(event.sender_id):
return
text, buttons = await self._render_admin_channels_menu()
await event.reply(text, parse_mode="html", buttons=buttons or None)
# --- Purge Review Channels ---
@self.client.on(events.NewMessage(pattern=r"(?i)^(/purge|/clear_reviews|🗑 پاکسازی کانال‌های ادمین|🗑 پاکسازی کانال‌های نظارت)$"))
async def cmd_purge_admin_channels(event: events.NewMessage.Event):
if not self.is_admin(event.sender_id):
return
text, buttons = await self._render_purge_admin_channels_menu()
await event.reply(text, parse_mode="html", buttons=buttons or None)
# --- Unified Add Hub ---
@self.client.on(events.NewMessage(pattern=r"(?i)^(/add|➕ افزودن مبدا / مقصد)$"))
async def cmd_add_hub(event: events.NewMessage.Event):
@@ -1834,6 +2087,33 @@ class AdminBotService:
)
await event.reply(card, parse_mode="html", buttons=buttons)
# 2.1. Waiting for Admin Review Channel Forward or Text
elif action == "wait_admin_channel_fwd":
ch_id, title, username, err = await self._resolve_channel(event)
if err or not ch_id:
await event.reply(err or "خطا در دریافت اطلاعات کانال نظارت. لطفا مجدد ارسال کنید:", buttons=get_cancel_button())
return
src_id = state.get("src_id")
site_id = state.get("site_id")
self.user_states.pop(event.sender_id, None)
adm_id = await self.repo.add_admin_channel(channel_id=ch_id, title=title, username=username)
if src_id:
await self.repo.set_source_admin_channel(src_id, ch_id)
card, buttons = await self._render_source_config(src_id)
await event.reply(f"✅ کانال نظارت <b>{title}</b> با شناسه <code>{ch_id}</code> افزوده و به کانال مبدا اختصاص داده شد!", parse_mode="html")
await event.reply(card, parse_mode="html", buttons=buttons)
elif site_id:
await self.repo.set_website_admin_channel(site_id, ch_id)
card, buttons = await self._render_website_config(site_id)
await event.reply(f"✅ کانال نظارت <b>{title}</b> با شناسه <code>{ch_id}</code> افزوده و به وبسایت مبدا اختصاص داده شد!", parse_mode="html")
await event.reply(card, parse_mode="html", buttons=buttons)
else:
card, buttons = await self._render_admin_channel_detail(adm_id)
await event.reply(f"✅ کانال نظارت ادمین <b>{title}</b> با شناسه <code>{ch_id}</code> با موفقیت افزوده شد!", parse_mode="html")
await event.reply(card, parse_mode="html", buttons=buttons)
# 3. Waiting for Target Personality
elif action == "wait_personality":
target_id = state.get("target_id")
@@ -3128,6 +3408,130 @@ class AdminBotService:
await event.edit(card, parse_mode="html", buttons=buttons)
await event.answer("✅ دسته‌بندی کانال با موفقیت به روز شد!")
# --- Admin Review Channels Management Callbacks ---
elif data == "list_adm_channels":
text, buttons = await self._render_admin_channels_menu()
await event.edit(text, parse_mode="html", buttons=buttons)
await event.answer()
elif data == "add_adm_ch":
self.user_states[event.sender_id] = {"action": "wait_admin_channel_fwd"}
guide = (
"📋 <b>افزودن کانال نظارت و بازبینی ادمین:</b>\n\n"
"ساده‌ترین روش: <b>یک پیام از کانال نظارت مورد نظر به این ربات فوروارد (Forward) کنید!</b>\n\n"
"<i>(یا می‌توانید آیدی، یوزرنیم یا لینک کانال مثل @admin_review_channel را ارسال نمایید. مطمئن شوید ربات در آن کانال ادمین با دسترسی ارسال و حذف پیام است)</i>"
)
await event.reply(guide, parse_mode="html", buttons=get_cancel_button())
await event.answer()
elif data.startswith("adm_view:"):
adm_id = int(data.split(":")[1])
text, buttons = await self._render_admin_channel_detail(adm_id)
await event.edit(text, parse_mode="html", buttons=buttons)
await event.answer()
elif data.startswith("adm_set_def:"):
adm_id = int(data.split(":")[1])
await self.repo.set_default_admin_channel(adm_id)
text, buttons = await self._render_admin_channel_detail(adm_id)
await event.edit(text, parse_mode="html", buttons=buttons)
await event.answer("⭐️ کانال به‌عنوان پیش‌فرض سیستم تنظیم شد!")
elif data.startswith("adm_del:"):
adm_id = int(data.split(":")[1])
await self.repo.delete_admin_channel(adm_id)
text, buttons = await self._render_admin_channels_menu()
await event.edit(f"🗑 <b>کانال نظارت ادمین حذف شد.</b>\n\n{text}", parse_mode="html", buttons=buttons)
await event.answer("کانال نظارت حذف شد.")
# --- Source / Website Admin Channel Assignment Callbacks ---
elif data.startswith("src_adm:"):
src_id = int(data.split(":")[1])
text, buttons = await self._render_source_admin_channel_menu(src_id)
await event.edit(text, parse_mode="html", buttons=buttons)
await event.answer()
elif data.startswith("src_set_adm:"):
_, src_id_str, adm_ch_str = data.split(":")
src_id = int(src_id_str)
adm_ch = None if adm_ch_str == "none" else int(adm_ch_str)
await self.repo.set_source_admin_channel(src_id, adm_ch)
card, buttons = await self._render_source_config(src_id)
await event.edit(card, parse_mode="html", buttons=buttons)
await event.answer("✅ کانال نظارت برای این مبدا تنظیم شد!")
elif data.startswith("src_add_adm:"):
src_id = int(data.split(":")[1])
self.user_states[event.sender_id] = {"action": "wait_admin_channel_fwd", "src_id": src_id}
guide = (
"📋 <b>افزودن کانال نظارت جدید برای این مبدا:</b>\n\n"
"یک پیام از کانال نظارت مورد نظر به ربات فوروارد کنید یا یوزرنیم/آیدی آن را بفرستید:"
)
await event.reply(guide, parse_mode="html", buttons=get_cancel_button())
await event.answer()
elif data.startswith("web_adm:"):
site_id = int(data.split(":")[1])
text, buttons = await self._render_website_admin_channel_menu(site_id)
await event.edit(text, parse_mode="html", buttons=buttons)
await event.answer()
elif data.startswith("web_set_adm:"):
_, site_id_str, adm_ch_str = data.split(":")
site_id = int(site_id_str)
adm_ch = None if adm_ch_str == "none" else int(adm_ch_str)
await self.repo.set_website_admin_channel(site_id, adm_ch)
card, buttons = await self._render_website_config(site_id)
await event.edit(card, parse_mode="html", buttons=buttons)
await event.answer("✅ کانال نظارت برای این وبسایت تنظیم شد!")
elif data.startswith("web_add_adm:"):
site_id = int(data.split(":")[1])
self.user_states[event.sender_id] = {"action": "wait_admin_channel_fwd", "site_id": site_id}
guide = (
"📋 <b>افزودن کانال نظارت جدید برای این وبسایت:</b>\n\n"
"یک پیام از کانال نظارت مورد نظر به ربات فوروارد کنید یا یوزرنیم/آیدی آن را بفرستید:"
)
await event.reply(guide, parse_mode="html", buttons=get_cancel_button())
await event.answer()
# --- Purge Review Messages Callbacks ---
elif data == "purge_adm_menu":
text, buttons = await self._render_purge_admin_channels_menu()
await event.edit(text, parse_mode="html", buttons=buttons)
await event.answer()
elif data.startswith("purge_adm_ask:"):
target_ch = data.split(":", 1)[1]
if target_ch == "all":
prompt_text = (
"⚠️ <b>هشدار مهم: پاکسازی تمام کانال‌های نظارت ادمین!</b>\n\n"
"آیا از حذف کامل تمام کارت‌های بازبینی و پیام‌های ارسال‌شده در <b>تمام کانال‌های نظارت</b> مطمئن هستید؟\n\n"
"<i>این عملیات پیام‌ها را از کانال‌های تلگرام حذف کرده و وضعیت پست‌ها را بایگانی می‌کند.</i>"
)
else:
prompt_text = (
f"⚠️ <b>هشدار: پاکسازی پیام‌های کانال نظارت <code>{target_ch}</code></b>\n\n"
f"آیا از حذف تمام کارت‌های بازبینی ارسال‌شده به این کانال مطمئن هستید؟"
)
confirm_buttons = [
[Button.inline("🗑 بله، پیام‌ها پاکسازی شوند", data=f"purge_adm_do:{target_ch}")],
[Button.inline("❌ انصراف و بازگشت", data="purge_adm_menu")]
]
await event.edit(prompt_text, parse_mode="html", buttons=confirm_buttons)
await event.answer()
elif data.startswith("purge_adm_do:"):
target_ch = data.split(":", 1)[1]
await event.edit("⏳ <b>در حال پاکسازی پیام‌ها از تلگرام و پایگاه‌داده...</b>", parse_mode="html", buttons=None)
_, _, summary = await self.purge_review_messages(target_ch)
back_buttons = [
[Button.inline("🗑 بازگشت به منوی پاکسازی", data="purge_adm_menu")],
[Button.inline("🔙 منوی سیستم", data="hub_system")]
]
await event.edit(summary, parse_mode="html", buttons=back_buttons)
await event.answer("✅ پاکسازی با موفقیت انجام شد!")
# --- Review Channel Buttons ---
elif data.startswith("hist:"):