Skip to content

Repository files navigation

مولتی سندر (Multi Sender)

English README →

یک ابزار پخش همزمان یک پست در چند پیام‌رسان: یک محتوا (متن + عکس/ویدیو) را یک‌بار می‌سازید، و به هر تعداد کانال در تلگرام، بله، روبیکا و ایتا — یا هر پیام‌رسان دیگری که خودتان اضافه کنید — ارسال می‌شود. وضعیت تحویل هر پلتفرم جدا پیگیری می‌شود، پس شکست در یک پلتفرم مانع یا باعث تکرار در بقیه نمی‌شود.

آن پست از کجا می‌آید کاملاً به شما بستگی دارد و اختیاری است — می‌توانید مستقیم از یک فرم پستش کنید، آن را از یک کانال تلگرام رله کنید، یا یک منبع خزنده به یک سایت/API دیگر وصل کنید. به‌صورت پیش‌فرض یک منبع دیوار کاملاً کاربردی همراه ریپازیتوری می‌آید، اما این فقط یکی از چند گزینهٔ موجود است، نه هستهٔ اصلی پروژه — لایهٔ منبع و لایهٔ ارسال‌کننده‌ها از طریق دو رابط سادهٔ برنامه‌نویسی (interface) کاملاً از هم جدا شده‌اند، پس می‌توانید این ریپازیتوری را فورک کنید و به یک منبع دیگر (یا هیچ‌کدام، فقط فرم دستی) وصلش کنید، بدون اینکه لازم باشد منطق ارسال را دست بزنید.

منبع دیوار، نسخهٔ به‌شدت تغییریافته‌ای از debMan/divar-telegram-bot (که خودش از ehcaning/divar-telegram-bot گرفته شده) است. از زمان نوشته‌شدن پروژهٔ اصلی، API غیررسمی دیوار تغییر کرده، بنابراین منطق خزیدن اینجا کاملاً متفاوت است.

امکانات

  • ارسال چندپیام‌رسانه‌ای — تلگرام عکس/آلبوم کامل دریافت می‌کند؛ بله، روبیکا و ایتا متن + اولین عکس.
  • منبع محتوا کاملاً اختیاری و قابل‌تعویض — پست دستی از طریق فرم، رله از تلگرام، یا یک خزندهٔ سفارشی؛ به main.py فرقی نمی‌کند از کدام منبع می‌آید (به بخش معماری نگاه کنید).
  • روی گیت‌هاب اکشنز اجرا می‌شود — نیازی به هاست یا سرور جداگانه نیست. یک ورک‌فلوی زمان‌بندی‌شده ربات را هر چند دقیقه یک‌بار اجرا می‌کند.
  • قالب‌بندی آمادهٔ کانال — عکس‌ها/آلبوم‌ها را با کپشن HTML و یک بلوک تماس/فوتر ثابت ارسال می‌کند، بدون لینک مستقیم خروجی.
  • منبع دیوار، به‌عنوان یک گزینهٔ آمادهٔ خزیدن (اختیاری) — جست‌وجوی هم‌زمان در چند شهر، فیلدهای ساختاریافتهٔ آگهی (متراژ، تعداد اتاق، ظرفیت، نرخ شبانه، امکانات) به‌جای فقط عنوان/قیمت، و هشتگ خودکار ترکیبی از کلیدواژهٔ متن و مسیر دسته‌بندی دیوار.

معماری

main.py                 # نقطهٔ ورود - یک Source را به چند Sender وصل می‌کند و یک بار اجرا می‌کند
core/
  models.py              # Item - ساختار عمومی که هر منبع تولید و هر گیرنده مصرف می‌کند
  orchestrator.py         # حلقهٔ اصلی: گرفتن شناسه‌های جدید -> گرفتن هر آیتم -> ارسال -> ذخیرهٔ وضعیت
sources/
  base.py                 # رابط Source: ‎fetch_new_ids(state)‎, ‎fetch_item(id)‎, ‎target_senders‎
  registry.py              # متغیر محیطی SOURCE_TYPE -> نمونهٔ Source
  divar/                   # منبع آماده دیوار (آگهی ساختاریافته -> پیام قالب‌بندی‌شده)
    client.py                # DivarSource - پاسخ API دیوار را به Item تبدیل می‌کند
    _raw_client.py           # فراخوانی‌های سطح‌پایین API دیوار + پارس کردن
    hashtags.py              # تولید هشتگ مخصوص دیوار
  telegram_relay/          # خودِ تلگرام به‌عنوان منبع (به بخش «منبع رله تلگرام» پایین‌تر نگاه کنید)
    client.py                # TelegramRelaySource - با getUpdates گوش می‌دهد و فقط به روبیکا/ایتا رله می‌کند
  website/                 # یک فرم ثبت پست عمومی به‌عنوان منبع (به بخش «منبع سایت» پایین‌تر نگاه کنید)
    client.py                # WebsiteSource - از Worker داخل پوشهٔ worker/ برای پست‌های در انتظار پرس‌وجو می‌کند
  common/                  # ابزارهای مشترک بین منابع رله‌ای (telegram_relay، website)
    quotes.py                 # گرفتن یک نقل‌قول طبیعت فارسی تصادفی
    channel_links.py          # ساخت فوتر «ما را جای دیگر هم دنبال کنید»
senders/
  base.py                  # رابط Sender: ‎enabled()‎, ‎send(item)‎
  registry.py                # لیست همهٔ گیرنده‌های داخلی و فیلتر آن‌هایی که پیکربندی شده‌اند
  formatting.py               # Item -> متن پیام، مشترک بین همهٔ گیرنده‌ها
  telegram.py                  # از طریق python-telegram-bot
  rubika.py                     # از طریق کتابخانهٔ rubka (تماس‌های یک‌بارهٔ async، بدون حلقهٔ polling)
  bale.py, eitaa.py              # HTTP خام - هیچ کتابخانهٔ یک‌بارهٔ مناسبی برای این دو پیدا نشد؛ توضیح پایین‌تر
  http_helpers.py             # ابزارهای مشترک HTTP برای گیرنده‌های شبیه Bot API
  text_utils.py                # ابزارهای تقسیم متن طولانی به چند پیام
storage.py               # وضعیت tokens.json (پیگیری تحویل به تفکیک گیرنده)، مستقل از نوع منبع
config.py                # متغیرهای محیطی و ثابت‌ها
docs/index.html          # فرم عمومی ثبت پست (گیت‌هاب‌پیجز) - فقط با worker/ صحبت می‌کند، هرگز با این ریپازیتوری
worker/                  # Cloudflare Worker پشتیبان docs/index.html - برای استقرار به worker/README.md نگاه کنید
requirements.txt
.github/workflows/run-bots.yml

اضافه کردن یک منبع جدید (مثلاً یک سایت آگهی دیگر، یک فید RSS، جست‌وجوی توییتر): یک sources/<name>/client.py بسازید که یک نمونهٔ SOURCE صادر می‌کند و کلاسش fetch_new_ids(state) و fetch_item(id) -> Item | None را پیاده می‌کند. آن را در sources/registry.py ثبت کنید، سپس SOURCE_TYPE=<name> را تنظیم کنید. هیچ بخش دیگری از ریپازیتوری نیاز به تغییر ندارد — همهٔ گیرنده‌ها از قبل با Item صحبت می‌کنند. اگر منبع شما نیاز به نگه‌داشتن یک نشانگر/آفست بین اجراها دارد (مثل آفست آپدیت‌های telegram_relay)، داخل fetch_new_ids مقدار state["source_state"][self.name] را بخوانید/بنویسید — این مقدار به‌طور خودکار در tokens.json ذخیره می‌شود. اگر منبع شما داده‌های پشتیبان خودش را دارد که باید فقط پس از تحویل واقعی پاک‌سازی شوند (مثل پست‌های website)، متد on_delivered(item_id, fully_delivered) را بازنویسی کنید — این متد یک‌بار به ازای هر آیتم، بعد از هر تلاش برای تحویل، صدا زده می‌شود و fully_delivered فقط زمانی True است که همهٔ گیرنده‌های هدف آن آیتم، تحویل را با موفقیت ثبت کرده باشند.

دو نوع محتوای Item: آیتم‌های دیوار داده‌های ساختاریافتهٔ آگهی هستند (قیمت، مشخصات و...) که گیرنده‌ها آن‌ها را در یک قالب می‌ریزند. همهٔ منابع این‌طور نیستند — telegram_relay و website یک پست از‌پیش‌نوشته‌شده را همان‌طور که هست رله می‌کنند. با تنظیم Item.raw_text، گیرنده‌ها همان متن را عیناً ارسال می‌کنند، بدون ساختن قالب دیواری دورش.

محدود کردن مقصد ارسال بر اساس منبع یا بر اساس هر آیتم: با تنظیم لیست Source.target_senders (مثلاً ["rubika", "eitaa"]) می‌توانید مشخص کنید که آیتم‌های یک منبع نباید به همهٔ گیرنده‌های پیکربندی‌شده بروند — telegram_relay از همین قابلیت استفاده می‌کند، چون خودِ پست از قبل در تلگرام موجود است. برای تحویلی که بر اساس هر آیتم متفاوت است (نه بر اساس کل منبع) — website به این نیاز دارد، چون هر مشتری مقصد خودش را مشخص می‌کند — به‌جای آن، Item.destination_overrides را برابر یک نگاشت {sender_name: chat_id} قرار دهید؛ یک گیرنده به‌جای مقدار پیش‌فرض خودش از config، همین شناسهٔ چت را استفاده می‌کند و تحویل فقط به همین گیرنده‌ها محدود می‌شود. اگر هر دو را خالی/None (پیش‌فرض) بگذارید، مثل دیوار به همهٔ گیرنده‌های پیکربندی‌شده با شناسهٔ چت پیش‌فرض خودشان ارسال می‌شود.

اضافه کردن یک گیرندهٔ جدید (مثلاً دیسکورد، واتساپ، یک وب‌هوک): یک senders/<name>.py بسازید با کلاسی که enabled() و async send(item) -> bool را پیاده می‌کند. یک نمونه از آن را در senders/registry.py ثبت کنید. به‌محض تنظیم متغیرهای محیطی موردنیازش، به‌طور خودکار شناسایی و استفاده می‌شود. اگر می‌خواهید این گیرنده از destination_overrides پشتیبانی کند، به‌جای استفادهٔ همیشگی از مقدار پیش‌فرض config، شناسهٔ چت را از item.destination_overrides.get(self.name, config.YOUR_DEFAULT_CHATID) بخوانید — به هر کدام از چهار گیرندهٔ آماده برای الگو نگاه کنید.

چرا بله و ایتا از HTTP خام استفاده می‌کنند نه یک کتابخانه: کتابخانهٔ python-bale-bot وجود دارد، اما متد Bot.connect() آن قبل از این‌که نشست HTTP قابل‌استفاده شود، یک حلقهٔ polling بی‌نهایت را اجرا می‌کند - این کتابخانه برای رباتی طراحی شده که دائم در حال اجراست، نه یک اجرای یک‌بارهٔ کرون، بنابراین استفاده از آن یعنی وابستگی به جزئیات داخلی مستندنشده. برای ایتا هم هیچ کتابخانهٔ نگه‌داری‌شده‌ای وجود ندارد. در مقابل، کتابخانهٔ rubka برای روبیکا تماس‌های async یک‌باره و بدون مرحلهٔ polling دارد، بنابراین گزینهٔ مناسبی است و در rubika.py استفاده شده.

منبع رله تلگرام (telegram_relay)

با SOURCE_TYPE=telegram_relay کل ایده برعکس می‌شود: به‌جای خزیدن روی یک سایت آگهی، خودِ ربات تلگرام شما منبع است. یک پست برایش بفرستید — یک عکس یا ویدیو همراه با کپشن، چه به‌صورت پیام خصوصی به ربات و چه به‌صورت پست کانالی در کانالی که ربات در آن ادمین است — و همان پست به روبیکا و ایتا رله می‌شود. تلگرام از مقصدهای ارسال حذف می‌شود، چون پست از قبل همان‌جا موجود است.

راه‌اندازی:

  1. از همان رباتِ مرحلهٔ ۱ استفاده کنید (یا یک ربات جداگانه) - در هر صورت باید BOT_TOKEN تنظیم شده باشد.
  2. برای رله کردن پست‌های کانال: ربات را به‌عنوان ادمین کانال اضافه کنید (کانال ← مدیران ← افزودن مدیر). نیازی به دسترسی خاصی فراتر از خواندن پیام‌ها ندارد.
  3. شناسهٔ عددی چت‌هایی که می‌خواهید پست از آن‌ها پذیرفته شود را پیدا کنید:
    • شناسهٔ کاربری خودتان، برای پیام‌دادن مستقیم به ربات — به @userinfobot پیام دهید.
    • شناسهٔ عددی کانال (چیزی شبیه -1001234567890‎) — یک پیام از کانال را به @userinfobot فوروارد کنید، یا بعد از یک‌بار پست کردن، پاسخ getUpdates ربات را بررسی کنید.
  4. مقدار TELEGRAM_RELAY_CHAT_IDS را برابر لیستی از این شناسه‌ها با کاما جدا کنید (مثلاً 123456789,-1001234567890‎). این مقدار الزامی است — بدون آن، منبع هیچ‌چیزی را پردازش نمی‌کند، تا یک پیام خصوصی ناخواسته از یک نفر دیگر به کانال‌های شما رله نشود.
  5. مقدار SOURCE_TYPE=telegram_relay را به‌عنوان یک سیکرت ریپازیتوری تنظیم کنید.

نقل‌قول طبیعت + فوتر لینک کانال‌ها: هر پست رله‌شده یک نقل‌قول تصادفی با موضوع طبیعت دریافت می‌کند (در لحظهٔ اجرا از فایل موضوعی tabiat.json در ریپازیتوری aliaslany/persian-quotes گرفته می‌شود، بدون نیاز به داخل‌ریپو بودن داده‌ها)، به‌همراه یک فوتر «ما را جای دیگر هم دنبال کنید» که به همان محتوا در کانال‌های تلگرام/بله/روبیکا لینک می‌دهد. هر گیرنده این لینک‌ها را در همان قالبی که آن پلتفرم واقعاً پشتیبانی می‌کند رندر می‌کند — لینک واقعاً کلیک‌پذیر در روبیکا (از طریق تبدیل HTML به متادیتای لینک روبیکا)، و متن ساده به شکل برچسب: آدرس در ایتا (چون ایتا از لینک غنی پشتیبانی نمی‌کند). این‌ها را می‌توانید از طریق CHANNEL_LINK_LABEL، TELEGRAM_CHANNEL_URL، BALE_CHANNEL_URL، RUBIKA_CHANNEL_URL و NATURE_QUOTES_URL تنظیم کنید (جدول سیکرت‌ها را پایین‌تر ببینید) — هر کدام از *_CHANNEL_URL را خالی بگذارید تا آن پلتفرم از فوتر حذف شود.

محدودیت شناخته‌شده: تلگرام هر عکس از یک آلبوم چندعکسی را به‌صورت یک آپدیت جداگانه می‌فرستد. این منبع فعلاً هر پیام را یک پست مستقل در نظر می‌گیرد، پس یک آلبوم چندعکسی به چند پست جدا در روبیکا/ایتا تبدیل می‌شود، نه یک آلبوم گروه‌بندی‌شده. برای پست‌های تک‌عکس/تک‌ویدیو مشکلی ندارد؛ اگر آلبوم زیاد پست می‌کنید، گروه‌بندی بر اساس media_group_id قدم بعدی طبیعی خواهد بود.

منبع سایت (website)

با SOURCE_TYPE=website‎، فرم عمومیِ docs/index.html‎ (که از طریق گیت‌هاب‌پیجز سرو می‌شود) به‌عنوان منبع استفاده می‌شود. هر کسی که یک کد تبلیغیِ معتبر داشته باشد می‌تواند یک پست ثبت کند — متن، یک عکس/ویدیوی اختیاری، و شناسهٔ چت مقصد برای هر پلتفرمی که می‌خواهد پستش در آن منتشر شود. هیچ توکن ربات یا اعتبارنامهٔ گیت‌هابی از فرستنده گرفته نمی‌شود.

چرا این‌طور ساخته شده: این فرم قبلاً از بازدیدکننده یک Personal Access Token گیت‌هاب می‌خواست و پست‌ها را مستقیماً در همین ریپازیتوری کامیت می‌کرد. این، صرف‌نظر از این‌که توکن مال چه کسی باشد، یک مشکل امنیتی واقعی است — گیت‌هاب نمی‌تواند دسترسی «Contents: write» را فقط به یک پوشه محدود کند، پس همان توکن می‌توانست فایل‌های .github/workflows/*.yml‎ را هم بازنویسی کند و سکرت‌های واقعی ربات‌های این ریپازیتوری را در اجرای بعدی ورک‌فلو بدزدد. یک راه‌حل کاملاً سمت‌کلاینت (فراخوانی مستقیم API تلگرام/بله/روبیکا/ایتا از مرورگر فرستنده با توکن ربات خودش) هم ممکن نیست — هیچ‌کدام از این چهار API هدر CORS نمی‌فرستند، پس مرورگرها این فراخوانی‌ها را کلاً مسدود می‌کنند. راه‌حل، یک بک‌اند کوچک (پوشهٔ worker/‎، یک Cloudflare Worker) است که تنها چیزی است که فرم عمومی با آن صحبت می‌کند. این Worker هم هیچ توکن رباتی نگه نمی‌دارد — فرستنده‌ها فقط یک کد تبلیغی و یک شناسهٔ چت ساده می‌دهند، که هیچ‌کدام حساس نیستند.

چون هر پست مقصد خودش را مشخص می‌کند، website‎ به‌جای یک لیست ثابت target_senders‎، برای هر آیتم مقدار Item.destination_overrides‎ را تنظیم می‌کند — پستی که فقط شناسهٔ چت تلگرام را نام می‌برد، فقط به تلگرام تحویل داده می‌شود، هرگز به کانال‌های پیش‌فرض بله/روبیکا/ایتای خودتان. همان رفتار نقل‌قول طبیعت + فوتر لینک کانال‌ها که در telegram_relay‎ توضیح داده شد اینجا هم اعمال می‌شود، چون هر دو از همان ابزارهای مشترک در sources/common/‎ استفاده می‌کنند.

راه‌اندازی:

  1. Worker را مستقر کنید — مراحل یک‌بارهٔ wrangler deploy‎ در worker/README.md‎ توضیح داده شده. در پایان یک آدرس مثل https://multi-sender-submissions.<subdomain>.workers.dev‎ چاپ می‌شود.
  2. همان آدرس را در ثابت API_BASE‎ نزدیک ابتدای بخش <script>‎ فایل docs/index.html‎ قرار دهید.
  3. گیت‌هاب‌پیجز را فعال کنید: Settings → Pages → Source ← «Deploy from a branch» ← شاخهٔ main‎، پوشهٔ /docs‎. یکی دو دقیقه بعد فرم شما در آدرس https://<username>.github.io/<repo>/‎ در دسترس خواهد بود.
  4. مقادیر WEBSITE_API_URL‎ (آدرس Worker) و WEBSITE_API_TOKEN‎ (باید با سکرت API_TOKEN‎ که روی Worker تنظیم کرده‌اید یکی باشد) را به‌عنوان سیکرت ریپازیتوری اضافه کنید.
  5. مقدار SOURCE_TYPE=website‎ را به‌عنوان یک سیکرت ریپازیتوری تنظیم کنید.
  6. یک کد تبلیغی بسازید — دستور curl‎ مربوطه در worker/README.md‎ هست. کد را به هر کسی که باید بتواند پست ثبت کند بدهید.

اجرای دورهٔ آزمایشی واقعی است، نه صرفاً یک مانع سمت‌کلاینت: شمارش دورهٔ آزمایشی یک کد تبلیغی از اولین استفادهٔ واقعی آن شروع می‌شود (این بررسی داخل خود Worker انجام می‌شود، نه در مرورگر)، پس با پاک کردن کوکی یا localStorage‎ قابل بازنشانی نیست. فعلاً راهی برای فهرست کردن یا لغو کدها بعد از ساختشان، جز ویرایش مستقیم دادهٔ KV در Worker، وجود ندارد.

پاک‌سازی: برخلاف طراحی قبلی، یک پست فقط زمانی از Worker حذف می‌شود که تحویل آن به همهٔ پلتفرم‌هایی که مشتری هدف قرار داده، کاملاً موفق شده باشد (Source.on_delivered‎ با fully_delivered=True‎ صدا زده می‌شود) — یک شکست جزئی، پست را دست‌نخورده نگه می‌دارد تا اجرای بعدی همان پلتفرم‌های ناموفق را دوباره امتحان کند، به‌جای این‌که محتوا از بین برود.

نحوهٔ کار

چون هاست رایگان جایی برای اجرای یک پردازش دائمی نمی‌دهد، ربات به‌طور پیوسته اجرا نمی‌شود. در عوض، یک ورک‌فلوی گیت‌هاب اکشنز آن را طبق زمان‌بندی (مثلاً هر ۱۰ دقیقه) اجرا می‌کند. هر اجرا:

  1. آیتم‌های جدید را از منبع پیکربندی‌شده می‌گیرد (آگهی‌های دیوار، پیام‌های تلگرام برای telegram_relay، یا پست‌های ثبت‌شده از طریق Worker برای website).
  2. هر آیتم جدید را به گیرنده‌هایی که آن منبع (یا خودِ آن آیتم، از طریق destination_overrides) اجازه می‌دهد ارسال می‌کند.
  3. متد on_delivered هر منبع را اجرا می‌کند، سپس وضعیت به‌روزشده (tokens.json) را به ریپازیتوری کامیت می‌کند تا اجرای بعدی از همان‌جا ادامه دهد.

راه‌اندازی

۱. ساخت ربات

@BotFather را در تلگرام باز کنید، یک ربات بسازید و توکن آن را یادداشت کنید.

۲. گرفتن شناسهٔ چت/کانال

  • چت خصوصی: به ربات پیام بدهید، سپس آدرس https://api.telegram.org/bot<TOKEN>/getUpdates را باز کنید و مقدار chat.id را بخوانید.
  • کانال عمومی: می‌توانید مستقیماً از @username‎ آن به‌عنوان شناسهٔ چت استفاده کنید.
  • کانال/گروه خصوصی: ربات را به‌عنوان ادمین با دسترسی «ارسال پیام» اضافه کنید، یک پیام در آن بفرستید، سپس getUpdates را همان‌طور بررسی کنید — شناسه یک عدد منفی بزرگ خواهد بود.

۳. پیدا کردن شناسهٔ شهر(ها) و اسلاگ دسته‌بندی

به divar.ir بروید، شهر و دسته‌بندی موردنظر را انتخاب کنید و در حین مرور نتایج جست‌وجو، تب Network مرورگر (DevTools) را باز کنید. مقادیر city_ids و category را در درخواست ارسالی به api.divar.ir/v8/postlist/w/search پیدا کنید. به‌طور جایگزین، آدرسی که هنگام مرور divar.ir/s/...‎ نشان داده می‌شود اغلب همان اسلاگ دسته‌بندی را نشان می‌دهد (مثل real-estate‎, villa‎, temporary-rent‎).

۴. فورک این ریپازیتوری، سپس افزودن سیکرت‌ها

در فورک خودتان به Settings → Secrets and variables → Actions بروید و موارد زیر را اضافه کنید:

سیکرت الزامی؟ مثال توضیح
BOT_TOKEN اختیاری 123456:ABC-DEF...‎ توکن ربات تلگرام از BotFather
BOT_CHATID اختیاری -1001234567890‎ یا @mychannel‎ چت/کانال مقصد در تلگرام
BALE_BOT_TOKEN اختیاری توکن ربات بله
BALE_CHATID اختیاری چت/کانال مقصد در بله
RUBIKA_BOT_TOKEN اختیاری توکن ربات روبیکا
RUBIKA_CHATID اختیاری چت/کانال مقصد در روبیکا
EITAA_TOKEN اختیاری توکن API ایتایار
EITAA_CHATID اختیاری چت/کانال مقصد در ایتا
SOURCE_TYPE اختیاری website‎ کدام منبع بررسی شود (به sources/registry.py نگاه کنید)؛ پیش‌فرض website‎
TELEGRAM_RELAY_CHAT_IDS برای telegram_relay‎ الزامی 123456789,-1001234567890‎ شناسه‌های چتی که اجازهٔ پست از طریق رله را دارند (به منبع رله تلگرام نگاه کنید)
CHANNEL_LINK_LABEL اختیاری دیوار ادز اسیست‎ برچسب کلیک‌پذیر برای هر لینک فوتر «ما را جای دیگر هم دنبال کنید»
TELEGRAM_CHANNEL_URL اختیاری https://t.me/divaradsassist‎ لینک فوتر به کانال تلگرام شما؛ خالی بگذارید تا حذف شود
BALE_CHANNEL_URL اختیاری (خالی) لینک فوتر به کانال بله شما؛ خالی بگذارید تا حذف شود
RUBIKA_CHANNEL_URL اختیاری (خالی) لینک فوتر به کانال روبیکای شما؛ خالی بگذارید تا حذف شود
NATURE_QUOTES_URL اختیاری آدرس jsDelivr برای tabiat.json‎ برای استفاده از یک دیتاست/موضوع نقل‌قول دیگر تغییرش دهید
WEBSITE_API_URL برای website‎ الزامی https://multi-sender-submissions.<subdomain>.workers.dev‎ آدرس Worker مستقرشدهٔ ثبت پست (به منبع سایت نگاه کنید)
WEBSITE_API_TOKEN برای website‎ الزامی باید با سکرت API_TOKEN‎ روی Worker (تنظیم‌شده با wrangler secret put‎) یکی باشد
SEARCH_CITY_IDS 823,1996,1999‎ شناسه‌های عددی شهر با کاما جدا (فقط منبع دیوار)
SEARCH_CATEGORY real-estate‎ اسلاگ دسته‌بندی دیوار
PROXY_URL اختیاری فقط اگر ران‌ر شما نمی‌تواند مستقیم به دیوار/تلگرام دسترسی داشته باشد

حداقل یک جفت توکن/شناسهٔ چت را تنظیم کنید. تلگرام ارسال کامل عکس یا آلبوم را حفظ می‌کند؛ بله اولین عکس آگهی و سپس متن قالب‌بندی‌شده را می‌فرستد؛ روبیکا و ایتا آگهی قالب‌بندی‌شده را به‌صورت متن دریافت می‌کنند. کلاینت‌های غیر از تلگرام از اندپوینت‌های سازگار با Bot API استفاده می‌کنند و با متغیرهای محیطی اختیاری BALE_API_BASE_URL، RUBIKA_API_BASE_URL یا EITAA_API_BASE_URL می‌توان آن‌ها را به گیت‌وی‌های جایگزین وصل کرد.

tokens.json اکنون تحویل هر پلتفرم را جداگانه ثبت می‌کند. اگر یک پلتفرم شکست بخورد، اجرای بعدی فقط همان پلتفرم را دوباره امتحان می‌کند و از پست تکراری در پلتفرم‌هایی که موفق بوده‌اند جلوگیری می‌شود.

۵. فعال‌سازی دسترسی نوشتن اکشنز

Settings → Actions → General → Workflow permissions ← گزینهٔ «Read and write permissions» را انتخاب کنید (لازم است تا ورک‌فلو بتواند tokens.json را به ریپازیتوری کامیت کند).

۶. اجرای آن

به تب Actions بروید ← ورک‌فلو را انتخاب کنید ← Run workflow. پس از موفقیت، طبق زمان‌بندی تعریف‌شده در .github/workflows/run-bots.yml‎ به‌طور خودکار اجرا می‌شود.

توسعهٔ محلی

git clone https://github.com/aliaslany/Multi_sender.git
cd Multi_sender
pip install -r requirements.txt
cp .env.example .env
# مقادیر .env را با توکن‌ها/شناسه‌های واقعی خودتان پر کنید
export $(grep -v '^#' .env | xargs)
echo '{}' > tokens.json
python main.py

یا با داکر:

docker compose up --build

محدودیت‌های شناخته‌شده

  • این پروژه از API غیررسمی دیوار (همان چیزی که خودِ divar.ir صدا می‌زند) استفاده می‌کند که از ترافیک مرورگر مهندسی معکوس شده است. اگر دیوار هدرها، اندپوینت‌ها یا ساختار پاسخ را تغییر دهد، ممکن است دوباره بشکند.
  • تشخیص هشتگ بر اساس کلیدواژه/زیررشته است، پس عبارت‌های غیرمعمول در متن آگهی ممکن است دیده نشوند.
  • telegram_relay‎ هر پیام تلگرام را یک پست مستقل در نظر می‌گیرد، پس یک آلبوم چندعکسی به چند پست جدا در پلتفرم‌های مقصد تبدیل می‌شود، نه یک آلبوم گروه‌بندی‌شده.
  • کدهای تبلیغی website‎ فعلاً هیچ مکانیزم فهرست‌کردن یا لغوی جز ویرایش مستقیم دادهٔ KV در Worker ندارند.
  • رسانهٔ website‎ به‌صورت base64 در KV ذخیره می‌شود (سقف ۲۵ مگابایت برای هر مقدار)، پس ویدیوهای بسیار بزرگ (حدود ۲۰ مگابایت به بالا) رد می‌شوند؛ اگر این محدودیت مشکل‌ساز شد، مهاجرت به R2 آن را برطرف می‌کند.

مجوز

به پروژهٔ اصلی بالادستی نگاه کنید — در این فورک مجوز جداگانه‌ای اضافه نشده است.

About

ربات چندپیام‌رسانه ماژولار: منبع‌ها (دیوار، ریلی تلگرام) را به تلگرام، بله، روبیکا و ایتا ارسال می‌کند.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages