# نصب Telegram WooCommerce Product Bridge

## پیش‌نیاز

- هاست خارجی cPanel با PHP 8.1 یا 8.2، MySQL 5.7+ یا MariaDB 10.3+ و Cron. نسخه دارای اصلاحات امنیتی PHP را انتخاب کنید.
- افزونه‌های PHP سرور بات: `pdo_mysql`, `curl`, `mbstring`, `openssl`, `fileinfo`, `session`. PHP باید ۶۴ بیتی باشد.
- سایت ایران: WordPress 6.4+، WooCommerce 8.5+ فعال، PHP 8.1+، `openssl`, `mbstring` و GD یا Imagick برای تولید تصاویر بندانگشتی.
- هر دو دامنه HTTPS معتبر؛ ساعت هاست‌ها دقیق؛ خروجی HTTPS سرور خارجی به Telegram و دامنه فروشگاه باز باشد. وردپرس هیچ ارتباطی با Telegram برقرار نمی‌کند.
- سرور وردپرس: `memory_limit=256M` یا بیشتر، `post_max_size=32M` یا بیشتر و سقف درخواست وب‌سرور/WAF حداقل ۳۲ MiB. برای محصولات پرتصویر ۵۱۲ MiB توصیه می‌شود.
- MySQL باید توابع `GET_LOCK` و `RELEASE_LOCK` را مجاز کند. MariaDB/MySQL معمولی اشتراکی این توابع را دارد.

## ۱. نصب سرور بات

۱. در cPanel یک Database و یک Database User بسازید و دسترسی کامل همان پایگاه داده را به آن کاربر بدهید. نام کامل دارای پیشوند حساب cPanel را استفاده کنید.

۲. `telegram-product-importer.zip` را Extract کنید. پوشه پروژه شامل `app`, `bin`, `database`, `public`, `storage` است. Document Root زیردامنه مخصوص بات را روی پوشه **public داخل پروژه** تنظیم کنید. فقط `public` باید از اینترنت قابل دسترس باشد. فایل‌های `.htaccess` را هنگام انتقال حفظ کنید. `config.php`، `app`، `database` و `storage` را داخل Document Root کپی نکنید. در Apache، پروژه دارای محدودیت دسترسی و پوشه public دارای مجوز مستقل است.

۳. از Terminal cPanel وارد پوشه `telegram-product-importer` شوید و اجرا کنید:

```bash
/opt/cpanel/ea-php82/root/usr/bin/php bin/install.php
```

برای PHP 8.1 از مسیر `ea-php81` استفاده کنید. اگر هاست مسیر متفاوتی دارد، مسیر PHP CLI همان نسخه را از بخش Cron Jobs/پشتیبانی هاست بگیرید. در این نصب به Composer یا SSH با دسترسی root نیاز نیست. اگر Terminal غیرفعال است، از پشتیبانی هاست بخواهید همین نصب CLI تعاملی را در حساب شما اجرا کند؛ نصب عمومی از طریق وب وجود ندارد.

۴. Installer نام پایگاه داده، کاربر، رمز و مدیر مالک را می‌پرسد؛ رمز مدیر حداقل ۱۲ نویسه باشد. کلید رمزگذاری و Secret Webhook را خودش تصادفی تولید می‌کند. `config.php` با دسترسی 0600 و storage با دسترسی 0700 ذخیره می‌شود. فایل config را در نسخه پشتیبان امن نگه دارید؛ کلید آن برای بازیابی توکن‌های رمزگذاری‌شده لازم است. Installer روی پایگاه داده‌ای که مدیر دارد دوباره نصب نمی‌کند.

۵. زیردامنه بات را با HTTPS باز کنید و وارد شوید. اگر HTTPS پشت Reverse Proxy خاتمه می‌یابد، وب‌سرور مورد اعتماد باید وضعیت HTTPS را برای PHP به‌درستی تنظیم کند؛ برنامه به هدر قابل جعل کاربر اعتماد نمی‌کند.

## ۲. نصب افزونه وردپرس

۱. از پیشخوان WordPress → افزونه‌ها → افزودن → بارگذاری، فایل `tw-product-bridge.zip` را انتخاب و فعال کنید. WooCommerce باید قبلاً فعال باشد.

۲. WooCommerce → TW Product Bridge را باز کنید. واحد پول را تنظیم کنید:

| واحد فروشگاه | تنظیم پل | تبدیل قیمت ورودی |
|---|---|---|
| IRR | ریال | تومان × ۱۰ |
| IRT / TOM / TOMAN | تومان | بدون تغییر |

پل از واحدهای «هزار تومان/هزار ریال» پشتیبانی نمی‌کند و در صورت ناسازگاری واحد پول واردسازی را متوقف می‌کند. واحد تومان باید توسط تنظیم/افزونه واحد پول فروشگاه ثبت شده باشد.

۳. API را فعال کنید. بخش «مشاهده کلید فعلی» را باز کنید و کلید را کپی کنید. کلید را در پیام یا لاگ عمومی قرار ندهید.

۴. در پنل بات → اتصال وردپرس، نشانی کامل سایت با HTTPS را بدون `/wp-json` وارد کنید؛ اگر WordPress در زیرپوشه نصب شده، آن زیرپوشه بخشی از نشانی است. کلید مشترک را ذخیره و «آزمون اتصال HMAC» را اجرا کنید. نتیجه باید واحد پول و نسخه WooCommerce را نشان دهد.

۵. پیوندهای یکتا و مسیر `/wp-json/` باید فعال باشند. WAF/افزونه امنیتی باید POST به `/wp-json/tw-bridge/v1/health` و `/wp-json/tw-bridge/v1/products` و هدرهای `X-TW-Timestamp`, `X-TW-Nonce`, `X-TW-Signature` را عبور بدهد. REST API اصلی فروشگاه را عمومی نکنید؛ این دو مسیر با امضا کنترل می‌شوند.

کلید افزونه با Salt وردپرس رمزگذاری می‌شود. پس از تغییر Salt، کلید جدید در تنظیم افزونه تولید و در سرور بات ثبت کنید. تغییر کلید فوراً کلید قبلی را باطل می‌کند.

## ۳. تلگرام و فروشندگان

۱. با BotFather بات بسازید. در پنل → تنظیمات تلگرام، Bot Token، شناسه عددی اشخاص مجاز و نشانی HTTPS فایل `webhook.php` را ذخیره کنید. شناسه اشخاص را از اطلاعات حساب خودتان یا پیام `from.id` ثبت‌شده در Telegram استخراج کنید؛ نام کاربری جای شناسه عددی شخص نیست.

۲. «ثبت Webhook و مشاهده وضعیت» را بزنید. برنامه Secret Webhook را به Telegram می‌فرستد و سپس وضعیت را دریافت می‌کند. Webhookهای قبلی بات جایگزین می‌شوند؛ پیام‌های در انتظار حذف نمی‌شوند. `pending_update_count` و خطای آخر را همین صفحه نمایش می‌دهد. Long Polling وجود ندارد.

۳. در فروشندگان، برای هر فروشنده نام و پیشوند SKU لاتین مستقل ثبت کنید؛ مثلاً `BAZAARJO`. شناسه عددی کانال، ID فروشنده یا Username کانال را تعیین کنید. برای کانال از مقدار کامل `-100…` استفاده کنید. اطلاعات Forward پیام اصلی از `forward_origin` خوانده می‌شود. Telegram ID/Channel ID و Username تکراری مجاز نیست. پیشوند فروشنده‌ای که محصول دارد تغییر نمی‌کند.

۴. دسته فروشنده، مبنای Retail/Wholesale، قانون افزایش قیمت، گرد کردن، سیاست تکرار و راهبرد تنوع را انتخاب کنید. بدون قانون افزایش، قیمت پایه تغییر نمی‌کند. برای مبلغ ثابت ۲۰۰٬۰۰۰، یک قانون Fixed بسازید و آن را به فروشنده متصل کنید.

۵. در راهبرد خودکار، پوشاک دارای چند سایز یا چند رنگ، متغیر می‌شود؛ کفش/بوت ساده و سایزهای آن ویژگی نمایشی می‌شوند. راهبردهای Size، Color و Both این تصمیم را برای فروشنده تغییر می‌دهند. فری‌سایز یک بازه اندازه مناسب است و به سایزهای مجزا تبدیل نمی‌شود.

۶. در صورت انتخاب ارسال خودکار، فقط محصول دارای نام، شناسه و قیمت مثبت به صف ارسال می‌رود. قیمت نامشخص در سرور بات draft باقی می‌ماند. ارسال دستی از صفحه محصول با قیمت خالی، یک **draft** در WordPress می‌سازد.

برای دریافت مستقیم پیام کانال، بات را در کانال عضو/مدیر مناسب کنید و Channel ID آن را در Sources ثبت کنید. برای Forward خصوصی، فرستنده باید در فهرست افراد مجاز باشد. پیام کانال ثبت‌نشده و فرد غیرمجاز پذیرفته نمی‌شود. Forward با هویت پنهان و فروشنده ناشناخته در ورودی‌ها نگه‌داری می‌شود؛ در آن صفحه فروشنده و متن اصلاح‌شده را تعیین و بازپردازش کنید. متن اصلی جداگانه حفظ می‌شود.

## ۴. Cron در cPanel

Installer سه خط Cron با **مسیر واقعی PHP و پروژه نصب‌شده** چاپ می‌کند. همان خطوط را در Cron Jobs کپی کنید:

- Queue: هر دقیقه، `bin/queue.php`.
- Retry اختیاری: روزانه ساعت 03:17 به وقت سرور، `bin/retry.php --failed`.
- Maintenance: روزانه ساعت 03:31، `bin/maintenance.php`.

برای اجرای فوری، از ریشه پروژه اجرا کنید:

```bash
/opt/cpanel/ea-php82/root/usr/bin/php bin/queue.php
/opt/cpanel/ea-php82/root/usr/bin/php bin/retry.php --failed
/opt/cpanel/ea-php82/root/usr/bin/php bin/maintenance.php
```

خط‌های تولیدشده خروجی را در `storage/cron.log` خصوصی می‌نویسند. Worker قبل از انتخاب کار بعدی حدود ۵۰ ثانیه بودجه دارد؛ یک کار جاری با زمان‌های محدود شبکه ممکن است طولانی‌تر شود. قفل اتصال MySQL از اجرای هم‌زمان جلوگیری می‌کند و با پایان/قطع فرایند آزاد می‌شود. پس از قطع Worker، اجرای بعدی کار running را بازمی‌گرداند. تلاش‌های شبکه با تأخیر تصاعدی و jitter انجام می‌شوند و پس از ۶ شکست به failed می‌روند. Retry فقط کارهای failed را دوباره زمان‌بندی می‌کند؛ لازم نیست هر دقیقه آن را اجرا کنید.

آلبوم پس از ۲۰ ثانیه سکوت جمع می‌شود؛ پیام دیررس همان آلبوم نسل جدید کار می‌سازد و پس از پردازش دوباره، گالری کامل می‌شود. همه تصاویر قبل از ایجاد/به‌روزرسانی رکورد محصول دانلود می‌شوند. اولین تصویر براساس ترتیب پیام Telegram، Featured و بقیه Gallery است. تصویر حداکثر ۱۰ MiB، مجموع تصاویر ۲۰ MiB و تعداد ۱۰ است. پیام متنی بعدی برای همان محصول تصاویر قبلی را حفظ می‌کند؛ ورودی جدید دارای تصویر، مجموعه تصاویر محصول را جایگزین می‌کند.

## ۵. کار با پنل

- محصولات: ویرایش نام، شرح، قیمت، نوع، ویژگی‌ها، دسته، برچسب، تعداد و وضعیت موجودی؛ انتخاب ذخیره و ارسال. ویژگی‌های متغیر در JSON دارای `options` و `variation` هستند؛ کلیدهای قابل ویرایش `Size`, `Color`, `Material` است.
- ورودی‌ها: مشاهده متن خام و وضعیت، انتساب فروشنده، اصلاح متن پردازش و ارسال مجدد به Parser؛ تاریخچه ارسال‌های WordPress نیز نمایش داده می‌شود.
- قواعد قیمت: None، Fixed، Percentage، Combined و Tier. تغییر قانون روی دریافت‌های بعدی اثر دارد؛ در صفحه فروشنده «محاسبه مجدد» برای محصولات قبلی وجود دارد و در حالت Auto Import ارسال مجدد می‌کند.
- دسته‌ها: آرایه JSON مرتب از `category` و `keywords`. دسته فروشنده اولویت دارد، سپس اولین قاعده دارای واژه منطبق. دسته/برچسب نام‌دار با API رسمی WordPress ایجاد می‌شود؛ شناسه عددی باید قبلاً وجود داشته باشد.
- Queue/Logs: خطای آخر، تعداد تلاش و زمان UTC؛ Retry کار شکست‌خورده.
- مدیران: Owner تنظیمات و حساب‌ها را مدیریت می‌کند؛ Operator فقط محصولات، ورودی‌ها، صف و گزارش‌ها را مدیریت می‌کند. نشست پس از ۳۰ دقیقه بی‌کاری منقضی می‌شود.

نمونه تنظیم **واقعی و قابل استفاده** پلکانی در پنل:

```json
[
  {"min": 0, "max": 999999, "percentage": 0, "fixed_amount": 200000},
  {"min": 1000000, "max": null, "percentage": 20, "fixed_amount": 0}
]
```

مرزها شامل دو سر بازه هستند؛ بازه‌ها مرتب و بدون هم‌پوشانی باشند. قیمت فاقد بازه به خطا می‌رود و صفر یا قیمت حدسی ایجاد نمی‌شود. اگر مبنا Wholesale باشد و قیمت همکاری اعلام نشده باشد، فقط قیمت عمومی قابل جایگزینی است؛ قیمت Retail به‌جای Wholesale استفاده نمی‌شود.

## ۶. بررسی پس از نصب

۱. اتصال HMAC را از پنل تست کنید. نخست یک Source با Auto Import خاموش بسازید.

۲. نمونه 3920، نمونه 24760، نمونه 69370، کفش 852 و نمونه بدون قیمت 6632 را از شخص مجاز به بات بفرستید؛ یک آلبوم چندعکس هم Forward کنید.

۳. Cron را اجرا و در پنل نام، قیمت، اندازه‌ها و تصاویر را ببینید. محصول بدون قیمت باید draft باشد.

۴. یک محصول را دستی ارسال کنید و در WooCommerce تصویر شاخص، گالری، ویژگی‌ها، تنوع‌ها و تبدیل تومان/ریال را بررسی کنید. محدودیت ترکیب تنوع‌ها ۱۰۰ است؛ محصول بیشتر از این حد را با راهبرد ساده‌تر یا اصلاح ویژگی‌ها ارسال کنید.

۵. همان SKU را با نسخه جدید بفرستید؛ محصول جدید تکراری نباید ساخته شود. تکرار یک درخواست امضاشده با nonce قبلی پاسخ 409 دارد. تغییر Body پاسخ 401 می‌دهد. SKU متعلق به محصولی که این پل ایجاد نکرده، بازنویسی نمی‌شود.

آزمون‌های خودکار Parser و اعتبارسنجی مستقل:

```bash
/opt/cpanel/ea-php82/root/usr/bin/php tests/run.php
```

آزمون افزونه از مسیر پوشه خود افزونه:

```bash
/opt/cpanel/ea-php82/root/usr/bin/php tests/validation.php
```

آزمون‌های اتصال در `tests/database.php` و `tests/integration.php` فقط روی نصب آزمایشی با نام پایگاه داده `tw_bridge_test…` اجرا می‌شوند؛ روی فروشگاه واقعی اجرا نکنید. دستورهای کامل در README است.

## ۷. خطاهای قابل رفع

| علامت | اقدام |
|---|---|
| 401 HMAC | کلید دو سمت، ساعت‌ها، هدرهای WAF و مسیر REST را بررسی کنید. |
| 409 replay | درخواست مجدد باید timestamp، nonce و امضای جدید داشته باشد؛ Worker این کار را می‌کند. |
| 409 tw_busy | کار با تأخیر دوباره اجرا می‌شود؛ قفل هم‌زمانی فعال است. |
| 409 tw_currency | واحد پول WooCommerce را با تنظیم افزونه هماهنگ کنید. |
| 409 tw_sku_conflict | SKU با محصول قبلی فروشگاه تداخل دارد؛ پیشوند مستقل فروشنده انتخاب کنید. |
| 413 / درخواست خالی | محدودیت PHP، وب‌سرور و WAF برای Body را افزایش دهید. |
| ارسال failed | Queue و error_log وردپرس را بررسی و پس از اصلاح Retry کنید. |
| Source unavailable | فروشنده غیرفعال یا حذف شده؛ انتساب ورودی را اصلاح کنید. |
| نیاز به بررسی Parser | از صفحه ورودی، نام/شناسه/متن را اصلاح و بازپردازش کنید. |
| Telegram getFile/download | توکن، ارتباط خروجی، محدودیت فایل و فضای storage را بررسی کنید. |
| cron اجرا نمی‌شود | مسیر PHP CLI، مجوز خواندن config و نوشتن storage، و cron.log را بررسی کنید. |

## ۸. پشتیبان‌گیری و حذف

از Database سرور بات، `config.php` و `storage/media` همراه با هم نسخه پشتیبان بگیرید. متن‌ها و تصاویر محصول به‌طور خودکار حذف نمی‌شوند. Maintenance گزارش‌های DB قدیمی‌تر از ۹۰ روز، کارهای done قدیمی‌تر از ۳۰ روز و فایل دانلود موقت قدیمی را پاک و cron.log بزرگ را به `.1` منتقل می‌کند.

غیرفعال‌کردن افزونه اطلاعات را نگه می‌دارد. حذف افزونه تنظیمات و جدول nonce پل را حذف می‌کند؛ محصول‌ها، تنوع‌ها، دسته‌ها و تصاویر فروشگاه باقی می‌مانند. برای انتقال هاست، مسیر مطلق `storage_path` در config را مطابق محل جدید به‌روزرسانی کنید و Cron را به مسیر جدید ببرید.
