diff --git a/services/admin_bot.py b/services/admin_bot.py
index cb76873..ecf2213 100644
--- a/services/admin_bot.py
+++ b/services/admin_bot.py
@@ -634,7 +634,7 @@ class AdminBotService:
])
return buttons
- def _format_raw_post_caption(self, post: Post) -> str:
+ async def _format_raw_post_caption(self, post: Post) -> str:
published_lines = ""
if post.published_to:
published_lines = "📤 وضعیت ارسال به کانالهای مقصد:\n"
@@ -654,8 +654,27 @@ class AdminBotService:
tag_line = f"🏷 موضوع: #{post.subject}\n\n" if getattr(post, "subject", None) else ""
+ 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.name:
+ source_label = f"🌐 وبسایت {site.name}"
+ elif site and site.url:
+ source_label = f"🌐 وبسایت {site.url}"
+ else:
+ source_label = f"🌐 وبسایت #{site_id}"
+ else:
+ src = await self.repo.get_source_by_channel_id(post.source_channel_id)
+ if src and src.title:
+ user_part = f" (@{src.username})" if src.username else ""
+ source_label = f"📢 کانال {src.title}{user_part}"
+ elif src and src.username:
+ source_label = f"📢 کانال @{src.username}"
+ else:
+ source_label = f"کانال {post.source_channel_id}"
+
caption = (
- f"📥 پست جدید از مبدا ({post.source_channel_id}):\n\n"
+ f"📥 پست جدید از {source_label}:\n\n"
f"{rejection_line}"
f"{published_lines}"
f"{tag_line}"
@@ -693,7 +712,7 @@ class AdminBotService:
targets = await self.repo.get_active_targets()
keyboard = self._build_raw_post_keyboard(post, targets)
- caption = self._format_raw_post_caption(post)
+ caption = await self._format_raw_post_caption(post)
if refresh_only:
# Update the card that already exists instead of posting a second one.
@@ -1236,10 +1255,12 @@ class AdminBotService:
st_icon = "🛑 متوقف (Paused)" if is_paused else "🟢 فعال و در حال کار (Running)"
pause_btn_text = "▶️ راهاندازی سیستم" if is_paused else "🛑 توقف سیستم"
pause_cb = "sys_resume" if is_paused else "sys_pause"
+ def_adm = await self.repo.get_default_admin_channel()
+ adm_label = f"{def_adm.title} ({def_adm.channel_id})" if (def_adm and def_adm.title) else (f"{self.review_channel_id}" if self.review_channel_id else "تعریفنشده")
text = (
f"⚙️ بخش کنترل و مدیریت سیستم (System Hub):\n\n"
f"• ⚡️ وضعیت کنونی سیستم: {st_icon}\n"
- f"• 📋 کانال نظارت ادمین: {self.review_channel_id}\n\n"
+ f"• 📋 کانال نظارت ادمین: {adm_label}\n\n"
f"عملیات مورد نظر را انتخاب کنید:"
)
buttons = [
@@ -1274,7 +1295,9 @@ class AdminBotService:
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")])
+ def_adm = await self.repo.get_default_admin_channel()
+ def_name = f" ({def_adm.title})" if (def_adm and def_adm.title) else (f" ({self.review_channel_id})" if self.review_channel_id else "")
+ buttons.append([Button.inline(f"{def_mark}⚙️ پیشفرض سیستم{def_name}", 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
@@ -1299,7 +1322,9 @@ class AdminBotService:
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")])
+ def_adm = await self.repo.get_default_admin_channel()
+ def_name = f" ({def_adm.title})" if (def_adm and def_adm.title) else (f" ({self.review_channel_id})" if self.review_channel_id else "")
+ buttons.append([Button.inline(f"{def_mark}⚙️ پیشفرض سیستم{def_name}", 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
@@ -1432,7 +1457,9 @@ class AdminBotService:
except Exception as e:
logger.warning(f"Error deleting chunk from channel {ch_id}: {e}")
- summary = f"🗑 پیامهای کانال نظارت {ch_id} پاکسازی شدند:\n\n• 🗑 پیامهای حذفشده از تلگرام: {deleted_tg_count}\n• 🗂 پستهای بایگانیشده در سیستم: {cleared_db_count}"
+ adm = await self.repo.get_admin_channel_by_telegram_id(ch_id)
+ ch_name = f"«{adm.title}»" if adm and adm.title else f"{ch_id}"
+ summary = f"🗑 پیامهای کانال نظارت {ch_name} پاکسازی شدند:\n\n• 🗑 پیامهای حذفشده از تلگرام: {deleted_tg_count}\n• 🗂 پستهای بایگانیشده در سیستم: {cleared_db_count}"
return deleted_tg_count, cleared_db_count, summary
@@ -1693,10 +1720,13 @@ class AdminBotService:
me = await self.collector.client.get_me()
userbot_status = f"🟢 آنلاین ({me.first_name})"
+ def_adm = await self.repo.get_default_admin_channel()
+ adm_label = f"{def_adm.title} ({def_adm.channel_id})" if (def_adm and def_adm.title) else (f"{self.review_channel_id}" if self.review_channel_id else "تعریفنشده")
+
welcome_text = (
"👋 به پنل مدیریت سیستم هوشمند کپیکار خوش آمدید!\n\n"
f"• 🤖 وضعیت ربات جمعآوریکننده: {userbot_status}\n"
- f"• 📋 شناسه کانال ادمینها: {self.review_channel_id}\n\n"
+ f"• 📋 کانال نظارت ادمین: {adm_label}\n\n"
"برای مدیریت کانالها از دکمههای زیر استفاده کنید:"
)
menu = await self.get_menu()
@@ -3250,10 +3280,13 @@ class AdminBotService:
me = await self.collector.client.get_me()
userbot_status = f"🟢 آنلاین ({me.first_name})"
+ def_adm = await self.repo.get_default_admin_channel()
+ adm_label = f"{def_adm.title} ({def_adm.channel_id})" if (def_adm and def_adm.title) else (f"{self.review_channel_id}" if self.review_channel_id else "تعریفنشده")
+
welcome_text = (
"👋 منوی اصلی سیستم هوشمند کپیکار:\n\n"
f"• 🤖 وضعیت ربات جمعآوریکننده: {userbot_status}\n"
- f"• 📋 شناسه کانال ادمینها: {self.review_channel_id}\n\n"
+ f"• 📋 کانال نظارت ادمین: {adm_label}\n\n"
"برای مدیریت بخشهای مختلف از دکمههای زیر استفاده کنید:"
)
buttons = get_persian_inline_hub_menu()
@@ -3667,7 +3700,7 @@ class AdminBotService:
updated_post = await self.repo.get_post_by_id(post_id)
targets = await self.repo.get_active_targets()
- new_caption = self._format_raw_post_caption(updated_post)
+ new_caption = await self._format_raw_post_caption(updated_post)
new_buttons = self._build_raw_post_keyboard(updated_post, targets)
await event.edit(
@@ -3689,7 +3722,7 @@ class AdminBotService:
return
targets = await self.repo.get_active_targets()
- caption = self._format_raw_post_caption(post)
+ caption = await self._format_raw_post_caption(post)
buttons = self._build_raw_post_keyboard(post, targets)
await event.edit(caption, parse_mode="html", buttons=buttons)
diff --git a/services/collector.py b/services/collector.py
index 48a6f27..d553ff9 100644
--- a/services/collector.py
+++ b/services/collector.py
@@ -35,13 +35,14 @@ class ScrapeResult:
duplicates: int = 0
error: Optional[str] = None
- def summary_fa(self, channel_id: int) -> str:
+ def summary_fa(self, channel_id: int, channel_title: Optional[str] = None) -> str:
+ name_label = f"{channel_title}" if channel_title else f"{channel_id}"
if self.error:
- return f"❌ خطا در دریافت پستهای کانال {channel_id}: {self.error}"
+ return f"❌ خطا در دریافت پستهای کانال {name_label}: {self.error}"
header = (
- f"✅ {self.collected} پست جدید از {channel_id} دریافت و به کانال ادمین ارسال شد."
+ f"✅ {self.collected} پست جدید از کانال {name_label} دریافت و به کانال ادمین ارسال شد."
if self.collected
- else f"ℹ️ هیچ پست جدیدی در {channel_id} پیدا نشد."
+ else f"ℹ️ هیچ پست جدیدی در کانال {name_label} پیدا نشد."
)
return (
f"{header}\n\n"
@@ -388,13 +389,13 @@ class CollectorService:
result.already_stored += 1
if progress_callback:
- await progress_callback(result.summary_fa(channel_id))
+ await progress_callback(result.summary_fa(channel_id, source_title))
return result
except Exception as e:
await log_exception("collector.scrape_history", e, {"channel_id": channel_id, "limit": limit})
result.error = str(e)
if progress_callback:
- await progress_callback(result.summary_fa(channel_id))
+ await progress_callback(result.summary_fa(channel_id, source_title))
return result
async def stop(self):
diff --git a/tests/test_name_formatting.py b/tests/test_name_formatting.py
new file mode 100644
index 0000000..5fa5dc4
--- /dev/null
+++ b/tests/test_name_formatting.py
@@ -0,0 +1,45 @@
+import pytest
+from unittest.mock import AsyncMock, MagicMock
+from services.collector import ScrapeResult
+from services.admin_bot import AdminBotService
+from db.models import Post, SourceChannel, SourceWebsite
+
+def test_collector_summary_fa_with_title():
+ res = ScrapeResult(scanned=10, collected=5, already_stored=3, duplicates=2)
+ summary = res.summary_fa(channel_id=-100123456, channel_title="کانال خبری فناوری")
+ assert "کانال خبری فناوری" in summary
+ assert "-100123456" not in summary
+
+def test_collector_summary_fa_fallback_to_id():
+ res = ScrapeResult(scanned=10, collected=0, already_stored=10, duplicates=0)
+ summary = res.summary_fa(channel_id=-100123456, channel_title=None)
+ assert "-100123456" in summary
+
+@pytest.mark.asyncio
+async def test_admin_bot_format_raw_post_caption_telegram_channel():
+ repo = MagicMock()
+ repo.get_source_by_channel_id = AsyncMock(return_value=SourceChannel(
+ id=1, channel_id=-100123456, username="tech_news", title="اخبار روز تکنولوژی"
+ ))
+ bot = AdminBotService(repo=repo, ai_processor=MagicMock(), queue=MagicMock())
+
+ post = Post(id=1, source_channel_id=-100123456, source_message_id=10, raw_text="متن تست پست")
+ caption = await bot._format_raw_post_caption(post)
+
+ assert "اخبار روز تکنولوژی" in caption
+ assert "@tech_news" in caption
+
+@pytest.mark.asyncio
+async def test_admin_bot_format_raw_post_caption_website():
+ repo = MagicMock()
+ pseudo_id = -900000005
+ repo.get_source_website_by_id = AsyncMock(return_value=SourceWebsite(
+ id=5, name="دیجیاتو", url="https://digiato.com"
+ ))
+ bot = AdminBotService(repo=repo, ai_processor=MagicMock(), queue=MagicMock())
+
+ post = Post(id=2, source_channel_id=pseudo_id, source_message_id=1, raw_text="خبر جدید وبسایت")
+ caption = await bot._format_raw_post_caption(post)
+
+ assert "دیجیاتو" in caption
+ assert "وبسایت" in caption