fix some handling 3

This commit is contained in:
2026-07-08 12:45:14 +03:30
parent 5781d5bf53
commit a4e98d8047
6 changed files with 311 additions and 61 deletions
@@ -611,29 +611,32 @@ class ProductController extends SuperController
if (!Product::hasOrderLimitColumns()) {
unset($data['order_limit_enabled'], $data['min_order_cash'], $data['max_order_cash']);
} elseif (empty($data['order_limit_enabled'])) {
$data['order_limit_enabled'] = false;
$data['min_order_cash'] = null;
$data['max_order_cash'] = null;
} else {
$min = $data['min_order_cash'] ?? null;
$max = $data['max_order_cash'] ?? null;
$hasMin = $min !== null && $min !== '';
$hasMax = $max !== null && $max !== '';
$min = $this->normalizeOrderLimitCash($data['min_order_cash'] ?? null);
$max = $this->normalizeOrderLimitCash($data['max_order_cash'] ?? null);
$orderLimitEnabled = filter_var($data['order_limit_enabled'] ?? false, FILTER_VALIDATE_BOOLEAN)
|| $min !== null
|| $max !== null;
if (!$hasMin && !$hasMax) {
throw new UnprocessableEntityHttpException(
'با فعال بودن محدودیت مبلغ سفارش، حداقل یکی از حداقل یا حداکثر مبلغ باید وارد شود'
);
if (!$orderLimitEnabled) {
$data['order_limit_enabled'] = false;
$data['min_order_cash'] = null;
$data['max_order_cash'] = null;
} else {
if ($min === null && $max === null) {
throw new UnprocessableEntityHttpException(
'با فعال بودن محدودیت مبلغ سفارش، حداقل یکی از حداقل یا حداکثر مبلغ باید وارد شود'
);
}
if ($min !== null && $max !== null && $min > $max) {
throw new UnprocessableEntityHttpException('حداقل مبلغ نمی‌تواند بیشتر از حداکثر باشد');
}
$data['order_limit_enabled'] = true;
$data['min_order_cash'] = $min;
$data['max_order_cash'] = $max;
}
if ($hasMin && $hasMax && (float) $min > (float) $max) {
throw new UnprocessableEntityHttpException('حداقل مبلغ نمی‌تواند بیشتر از حداکثر باشد');
}
$data['order_limit_enabled'] = true;
$data['min_order_cash'] = $hasMin ? (int) $min : null;
$data['max_order_cash'] = $hasMax ? (int) $max : null;
}
if (!Product::hasShowInAssetsColumn()) {
@@ -704,4 +707,39 @@ class ProductController extends SuperController
return (float) $value;
}
private function normalizeOrderLimitCash(mixed $value): ?int
{
if ($value === null || $value === '') {
return null;
}
if (is_int($value)) {
return $value >= 0 ? $value : null;
}
if (is_float($value)) {
return $value >= 0 ? (int) $value : null;
}
if (! is_string($value)) {
return null;
}
$value = $this->normalizeAsciiDigits(trim(str_replace([' ', ',', '٬'], '', $value)));
if ($value === '' || ! ctype_digit($value)) {
return null;
}
return (int) $value;
}
private function normalizeAsciiDigits(string $value): string
{
$persian = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'];
$arabic = ['٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩'];
$latin = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
return str_replace(array_merge($persian, $arabic), array_merge($latin, $latin), $value);
}
}
+4 -4
View File
@@ -146,10 +146,10 @@ class Product extends Model
}
try {
static::$orderLimitColumnsExist = Schema::hasColumn(
(new static)->getTable(),
'order_limit_enabled'
);
$table = (new static)->getTable();
static::$orderLimitColumnsExist = Schema::hasColumn($table, 'order_limit_enabled')
&& Schema::hasColumn($table, 'min_order_cash')
&& Schema::hasColumn($table, 'max_order_cash');
} catch (\Throwable) {
static::$orderLimitColumnsExist = false;
}
+162 -33
View File
@@ -4,6 +4,7 @@ namespace App\Services;
use App\Http\Controllers\Controller;
use App\Models\Product;
use Hekmatinasser\Verta\Verta;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
@@ -29,6 +30,22 @@ class PriceChannelBroadcastService
/** وزن هر مثقال به گرم — ضریب تبدیل قیمت هر گرم به هر مثقال (مثقال = گرم × این عدد) */
private const DEFAULT_CHANGE_MESGHAL = 4.3318;
/** قالب پیش‌فرض پیام کانال — وقتی price_channel_message_text خالی باشد */
private const DEFAULT_MESSAGE_TEMPLATE = <<<'TXT'
سلام 👋
%عنوان
📅 %تاریخ 🕐 %ساعت
🔴 خرید گرم: %گرم_خرید
🟢 فروش گرم: %گرم_فروش
🔴 خرید مثقال: %مثقال_خرید
🟢 فروش مثقال: %مثقال_فروش
📞 %شماره
%آیدی
%لینک
TXT;
public function __construct(private readonly ConfigService $config)
{
}
@@ -66,19 +83,19 @@ class PriceChannelBroadcastService
}
$products = $this->eligibleProducts();
if ($products === []) {
Log::debug('PriceChannel: محصول واجد شرایطی نیست (active/قیمت/ساعت کاری)');
$boardProduct = $this->resolveBoardProduct($products);
if ($boardProduct === null || (int) ($boardProduct->price ?? 0) <= 0) {
Log::debug('PriceChannel: محصول تابلو (آبشده) واجد شرایطی نیست');
return;
}
// شرط ۲: قیمتِ نمایشی نسبت به آخرین ارسال واقعاً تغییر کرده باشد؛ وگرنه چیزی نفرست
// و تایمر را هم دست نزن (تا سرِ اولین تغییرِ بعدی بلافاصله بفرستد).
$signature = $this->priceSignature($products);
// شرط ۲: قیمت تابلو نسبت به آخرین ارسال واقعاً تغییر کرده باشد
$signature = $this->priceSignature($boardProduct);
if ($signature === Cache::get(self::SIGNATURE_CACHE_KEY)) {
return;
}
$message = $this->buildMessage($products);
$message = $this->buildMessage($products, $boardProduct);
if ($message === '') {
return;
}
@@ -111,9 +128,16 @@ class PriceChannelBroadcastService
*
* @param array<int, Product> $products
*/
private function priceSignature(array $products): string
private function priceSignature(Product $boardProduct): string
{
return md5($this->buildPriceBody($products));
$prices = $this->computeDisplayPrices($boardProduct);
return md5(implode('|', [
$prices['gram_buy'],
$prices['gram_sell'],
$prices['mesghal_buy'],
$prices['mesghal_sell'],
]));
}
/**
@@ -137,8 +161,9 @@ class PriceChannelBroadcastService
if ($message !== '' && $targets !== []) {
$this->markSent();
$products = $this->eligibleProducts();
if ($products !== []) {
Cache::put(self::SIGNATURE_CACHE_KEY, $this->priceSignature($products), now()->addDay());
$boardProduct = $this->resolveBoardProduct($products);
if ($boardProduct !== null) {
Cache::put(self::SIGNATURE_CACHE_KEY, $this->priceSignature($boardProduct), now()->addDay());
}
}
@@ -239,27 +264,135 @@ class PriceChannelBroadcastService
}
/**
* ساخت متن پیام قیمت — مرتب و بر اساس داده‌ی هر محصول.
* ساخت متن پیام قیمت — قالب از price_channel_message_text (یا پیش‌فرض داخلی).
*/
public function buildMessage(?array $products = null): string
public function buildMessage(?array $products = null, ?Product $boardProduct = null): string
{
$products = $products ?? $this->eligibleProducts();
$body = $this->buildPriceBody($products);
if ($body === '') {
$boardProduct = $boardProduct ?? $this->resolveBoardProduct($products);
if ($boardProduct === null || (int) ($boardProduct->price ?? 0) <= 0) {
return '';
}
return trim($this->header() . "\n\n" . $body);
return $this->renderMessageTemplate($this->messageTemplate(), $products, $boardProduct);
}
private function messageTemplate(): string
{
$fromConfig = trim((string) $this->config->getString('price_channel_message_text', ''));
return $fromConfig !== '' ? $fromConfig : self::DEFAULT_MESSAGE_TEMPLATE;
}
/**
* بدنهٔ قیمت (بدون هدر/ساعت) — همان چیزی که «تغییر قیمت» را می‌سازد.
* امضای تشخیص تغییر روی همین بدنه‌ی نمایشیِ رُندشده گرفته می‌شود تا اختلاف چند ریالی
* که به یک عدد نمایشیِ یکسان می‌رسد، پیام تکراری نفرستد.
* جایگزینی متغیرهای قالب.
* قیمت تابلو: %گرم_خرید %گرم_فروش %مثقال_خرید %مثقال_فروش
* زمان: %تاریخ %ساعت — اپ: %اسم_اپ %نام %عنوان
* تماس: %آیدی %شماره %لینک — اختیاری: %محصولات %فوتر
*
* @param array<int, Product> $products
*/
private function buildPriceBody(array $products): string
private function renderMessageTemplate(string $template, array $products, Product $boardProduct): string
{
$name = trim((string) $this->config->getString('name_fa', ''));
$title = $name !== '' ? "📊 قیمت لحظه‌ای {$name}" : '📊 قیمت لحظه‌ای';
$prices = $this->computeDisplayPrices($boardProduct);
$message = $this->applyTemplatePlaceholders($template, [
'مثقال_فروش' => $prices['mesghal_sell'],
'مثقال_خرید' => $prices['mesghal_buy'],
'گرم_فروش' => $prices['gram_sell'],
'گرم_خرید' => $prices['gram_buy'],
'اسم_اپ' => $name,
'محصولات' => $this->buildProductLines($products),
'عنوان' => $title,
'تاریخ' => Verta::now()->format('Y/n/j'),
'ساعت' => now()->format('H:i'),
'آیدی' => trim((string) $this->config->getString('price_channel_footer_id', '')),
'شماره' => trim((string) $this->config->getString('price_channel_footer_phone', '')),
'لینک' => trim((string) $this->config->getString('price_channel_footer_link', '')),
'فوتر' => $this->buildFooterBlock(),
'نام' => $name,
]);
return preg_replace("/\n{3,}/", "\n\n", trim($message)) ?? trim($message);
}
/** @param array<string, string> $replacements */
private function applyTemplatePlaceholders(string $template, array $replacements): string
{
$keys = array_keys($replacements);
usort($keys, static fn (string $a, string $b): int => mb_strlen($b) <=> mb_strlen($a));
$message = $template;
foreach ($keys as $key) {
$message = preg_replace(
'/[%٪]\s*' . preg_quote($key, '/') . '/u',
$replacements[$key],
$message
) ?? $message;
}
return $message;
}
/**
* محصول مرجع قیمت تابلو — اول آبشده (گرم)، بعد اولین محصول گرم، بعد اولین واجد شرایط.
*
* @param array<int, Product> $products
*/
private function resolveBoardProduct(array $products): ?Product
{
if ($products === []) {
return null;
}
foreach ($products as $product) {
if (trim((string) ($product->group_type ?? '')) === 'آبشده' && $this->isGramUnit($product)) {
return $product;
}
}
foreach ($products as $product) {
if ($this->isGramUnit($product)) {
return $product;
}
}
return $products[0];
}
/**
* قیمت‌های نمایشی تابلو (گرم + مثقال) — همان منطق اپ + price_channel_diff.
*
* @return array{gram_buy: string, gram_sell: string, mesghal_buy: string, mesghal_sell: string}
*/
private function computeDisplayPrices(Product $product): array
{
$channelDiff = (float) $this->config->getInt('price_channel_diff', 0);
$diffBuy = (float) $this->config->displayBuyPriceDiffRial();
$diffSell = (float) $this->config->displaySellPriceDiffRial();
$mesghal = $this->changeMesghal();
$base = (float) ($product->price ?? 0);
// 🔴 خرید (مشتری می‌فروشد، پایین‌تر) — 🟢 فروش (مشتری می‌خرد، بالاتر)
$gramBuy = $base - $diffSell - $channelDiff;
$gramSell = $base + $diffBuy + $channelDiff;
return [
'gram_buy' => $this->formatPrice($gramBuy),
'gram_sell' => $this->formatPrice($gramSell),
'mesghal_buy' => $mesghal > 0 ? $this->formatPrice($gramBuy * $mesghal) : '',
'mesghal_sell' => $mesghal > 0 ? $this->formatPrice($gramSell * $mesghal) : '',
];
}
/**
* بدنهٔ قیمت محصولات (بدون فوتر) — مبنای تشخیص تغییر قیمت.
*
* @param array<int, Product> $products
*/
private function buildProductLines(array $products): string
{
if ($products === []) {
return '';
@@ -283,7 +416,6 @@ class PriceChannelBroadcastService
$lines[] = '🔴 خرید ' . $this->formatPrice($low) . ' 🟢 فروش ' . $this->formatPrice($high);
if ($this->isGramUnit($product) && $mesghal > 0) {
// قیمت پایه «هر گرم» است؛ مثقال = گرم × ضریب (کنوانسیون اپ: مثقال = گرم × 4.3318)
$lines[] = '💰 هر مثقال: ' . $this->formatPrice($low * $mesghal)
. ' / ' . $this->formatPrice($high * $mesghal);
}
@@ -291,25 +423,22 @@ class PriceChannelBroadcastService
$lines[] = '';
}
$footer = $this->footerLines();
if ($footer !== []) {
$lines[] = '➖➖➖➖➖➖';
foreach ($footer as $line) {
$lines[] = $line;
}
}
return trim(implode("\n", $lines));
}
private function header(): string
private function buildFooterBlock(): string
{
$name = trim((string) $this->config->getString('name_fa', ''));
$time = now()->format('H:i');
$footer = $this->footerLines();
if ($footer === []) {
return '';
}
$title = $name !== '' ? "📊 قیمت لحظه‌ای {$name}" : '📊 قیمت لحظه‌ای';
$lines = ['➖➖➖➖➖➖'];
foreach ($footer as $line) {
$lines[] = $line;
}
return $title . "\n🕐 ساعت " . $time;
return implode("\n", $lines);
}
/**
+42 -3
View File
@@ -42,6 +42,22 @@ SET @invite_link_desc = 'متن پیام اشتراک‌گذاری دعوت در
SET @deposit_guide_desc = 'متن راهنمای واریز در مودال شارژ حساب. متغیرها: ٪نام یا %نام = نام صاحب حساب؛ ٪کارت یا %کارت = شماره کارت؛ ٪شبا یا %شبا = شبا. فاصله بعد از ٪ اختیاری است. اگر خالی باشد از متن پیش‌فرض اپ استفاده می‌شود.';
SET @deposit_support_msg_desc = 'متن پیام هنگام کلیک روی «پیام به پشتیبانی» و «پیام به پشتیبانی (sms)» در مودال واریز. همان متغیرهای ٪نام، ٪کارت و ٪شبا. پیش‌فرض: «شماره حساب».';
SET @price_channel_message_desc = 'قالب متن پیام ارسالی به کانال. متغیرهای قیمت تابلو: ٪گرم_خرید ٪گرم_فروش ٪مثقال_خرید ٪مثقال_فروش (یا با %). زمان: ٪تاریخ ٪ساعت. اپ: ٪اسم_اپ یا ٪نام (نام فارسی)؛ ٪عنوان = خط «📊 قیمت لحظه‌ای + نام». تماس جدا: ٪آیدی ٪شماره ٪لینک. اختیاری: ٪محصولات (لیست همه محصولات)؛ ٪فوتر (ترکیب آیدی+شماره+لینک). فاصله بعد از ٪ اختیاری است. خالی = قالب پیش‌فرض.';
SET @price_channel_default_text = CONCAT(
'سلام 👋', CHAR(10),
'%عنوان', CHAR(10),
'📅 %تاریخ 🕐 %ساعت', CHAR(10),
CHAR(10),
'🔴 خرید گرم: %گرم_خرید', CHAR(10),
'🟢 فروش گرم: %گرم_فروش', CHAR(10),
'🔴 خرید مثقال: %مثقال_خرید', CHAR(10),
'🟢 فروش مثقال: %مثقال_فروش', CHAR(10),
CHAR(10),
'📞 %شماره', CHAR(10),
'%آیدی', CHAR(10),
'%لینک'
);
-- ستون‌های اختیاری settings (بعضی DBهای قدیمی ندارند)
SET @sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
@@ -379,6 +395,10 @@ SELECT 'price_channel_min_seconds', '120', 'حداقل فاصله ارسال (ث
'حداقل فاصله بین دو ارسال به کانال؛ جلوی سیل پیام را می‌گیرد. صفر = هر تغییر قیمت.', 'number'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'price_channel_min_seconds' LIMIT 1);
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
SELECT 'price_channel_message_text', @price_channel_default_text, 'قالب متن پیام کانال', 1, 1, @price_channel_message_desc, 'longtext'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'price_channel_message_text' LIMIT 1);
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
SELECT 'price_channel_footer_id', '', 'آیدی کانال در پیام', 1, 1,
'اگر پر باشد در انتهای پیام نمایش داده می‌شود (مثلاً یوزرنیم کانال). خالی = نمایش داده نمی‌شود.', 'text'
@@ -706,7 +726,7 @@ WHERE `key` = 'main_product';
-- longtext
UPDATE `settings` SET `type` = 'longtext' WHERE `key` IN (
'text_rule', 'text_about', 'support_messenger_text', 'invite_link_text',
'deposit_support_message_text',
'deposit_support_message_text', 'price_channel_message_text',
'montzar_order_response', 'montzar_last_product_log'
);
@@ -1127,6 +1147,13 @@ UPDATE `settings` SET `description` = 'نوع پیام‌رسان برای دک
UPDATE `settings` SET `description` = 'شناسه تماس پشتیبانی: یوزرنیم (تلگرام)، شماره (واتساپ/بله) یا آیدی بسته به پیام‌رسان انتخاب‌شده.' WHERE `key` = 'support_messenger_id';
UPDATE `settings` SET `description` = @deposit_guide_desc WHERE `key` = 'support_messenger_text';
UPDATE `settings` SET `description` = @deposit_support_msg_desc WHERE `key` = 'deposit_support_message_text';
UPDATE `settings` SET `description` = @price_channel_message_desc WHERE `key` = 'price_channel_message_text';
UPDATE `settings` SET
`title_fa` = 'قالب متن پیام کانال',
`type` = 'longtext',
`can_edit` = 1,
`description` = @price_channel_message_desc
WHERE `key` = 'price_channel_message_text';
UPDATE `settings` SET `description` = 'متن پیام اشتراک‌گذاری دعوت در اپ کاربر (دکمه «اشتراک با دوستان» در صفحه اصلی، بخش معرف در پروفایل، صفحه کسب درآمد و مودال اشتراک). متغیرهای جایگزین: ٪نام یا %نام = نام کامل کاربر دعوت‌کننده؛ ٪لینک یا %لینک = آدرس کامل صفحه ثبت‌نام همراه با کد معرف (بر اساس تنظیم app_url یا دامنه فعلی مرورگر). فاصله اختیاری بعد از ٪ یا % مجاز است (مثلاً ٪ نام). اگر این فیلد خالی باشد، اپ از متن پیش‌فرض داخلی استفاده می‌کند.' WHERE `key` = 'invite_link_text';
UPDATE `settings` SET `description` = 'شماره تماس پشتیبانی؛ در صفحه واریز، درباره ما و قوانین نمایش داده می‌شود. متن پیامک/پشتیبانی از «متن پیام به پشتیبانی (واریز)» خوانده می‌شود.' WHERE `key` = 'deposit_sms_support_number';
@@ -1166,10 +1193,21 @@ UPDATE `settings` SET
`description` = @deposit_support_msg_desc
WHERE `key` = 'deposit_support_message_text';
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
SELECT 'price_channel_message_text', @price_channel_default_text, 'قالب متن پیام کانال', 1, 1, @price_channel_message_desc, 'longtext'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'price_channel_message_text' LIMIT 1);
UPDATE `settings` SET
`title_fa` = 'قالب متن پیام کانال',
`type` = 'longtext',
`can_edit` = 1,
`description` = @price_channel_message_desc
WHERE `key` = 'price_channel_message_text';
UPDATE `settings` SET `type` = 'longtext'
WHERE `key` IN (
'text_rule', 'text_about', 'support_messenger_text', 'invite_link_text',
'deposit_support_message_text',
'deposit_support_message_text', 'price_channel_message_text',
'montzar_order_response', 'montzar_last_product_log'
);
@@ -1360,7 +1398,8 @@ UPDATE `settings` SET `can_edit` = 1 WHERE `key` IN (
'deposit_support_message_text', 'deposit_account_holder', 'deposit_card_number', 'deposit_sheba',
'text_about', 'text_rule',
'price_channel_enabled', 'price_channel_messengers', 'price_channel_diff',
'price_channel_min_seconds', 'price_channel_footer_id', 'price_channel_footer_phone',
'price_channel_min_seconds', 'price_channel_message_text',
'price_channel_footer_id', 'price_channel_footer_phone',
'price_channel_footer_link', 'change_mesghal'
);
@@ -41,6 +41,22 @@ SET @invite_link_desc = 'متن پیام اشتراک‌گذاری دعوت در
SET @deposit_guide_desc = 'متن راهنمای واریز در مودال شارژ حساب. متغیرها: ٪نام یا %نام = نام صاحب حساب؛ ٪کارت یا %کارت = شماره کارت؛ ٪شبا یا %شبا = شبا. فاصله بعد از ٪ اختیاری است. اگر خالی باشد از متن پیش‌فرض اپ استفاده می‌شود.';
SET @deposit_support_msg_desc = 'متن پیام هنگام کلیک روی «پیام به پشتیبانی» و «پیام به پشتیبانی (sms)» در مودال واریز. همان متغیرهای ٪نام، ٪کارت و ٪شبا. پیش‌فرض: «شماره حساب».';
SET @price_channel_message_desc = 'قالب متن پیام ارسالی به کانال. متغیرهای قیمت تابلو: ٪گرم_خرید ٪گرم_فروش ٪مثقال_خرید ٪مثقال_فروش (یا با %). زمان: ٪تاریخ ٪ساعت. اپ: ٪اسم_اپ یا ٪نام (نام فارسی)؛ ٪عنوان = خط «📊 قیمت لحظه‌ای + نام». تماس جدا: ٪آیدی ٪شماره ٪لینک. اختیاری: ٪محصولات (لیست همه محصولات)؛ ٪فوتر (ترکیب آیدی+شماره+لینک). فاصله بعد از ٪ اختیاری است. خالی = قالب پیش‌فرض.';
SET @price_channel_default_text = CONCAT(
'سلام 👋', CHAR(10),
'%عنوان', CHAR(10),
'📅 %تاریخ 🕐 %ساعت', CHAR(10),
CHAR(10),
'🔴 خرید گرم: %گرم_خرید', CHAR(10),
'🟢 فروش گرم: %گرم_فروش', CHAR(10),
'🔴 خرید مثقال: %مثقال_خرید', CHAR(10),
'🟢 فروش مثقال: %مثقال_فروش', CHAR(10),
CHAR(10),
'📞 %شماره', CHAR(10),
'%آیدی', CHAR(10),
'%لینک'
);
-- ستون‌های اختیاری settings (بعضی DBهای قدیمی ندارند)
SET @sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
+29 -1
View File
@@ -11,6 +11,22 @@
START TRANSACTION;
SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci;
SET @price_channel_message_desc = 'قالب متن پیام ارسالی به کانال. متغیرهای قیمت تابلو: ٪گرم_خرید ٪گرم_فروش ٪مثقال_خرید ٪مثقال_فروش (یا با %). زمان: ٪تاریخ ٪ساعت. اپ: ٪اسم_اپ یا ٪نام (نام فارسی)؛ ٪عنوان = خط «📊 قیمت لحظه‌ای + نام». تماس جدا: ٪آیدی ٪شماره ٪لینک. اختیاری: ٪محصولات (لیست همه محصولات)؛ ٪فوتر (ترکیب آیدی+شماره+لینک). فاصله بعد از ٪ اختیاری است. خالی = قالب پیش‌فرض.';
SET @price_channel_default_text = CONCAT(
'سلام 👋', CHAR(10),
'%عنوان', CHAR(10),
'📅 %تاریخ 🕐 %ساعت', CHAR(10),
CHAR(10),
'🔴 خرید گرم: %گرم_خرید', CHAR(10),
'🟢 فروش گرم: %گرم_فروش', CHAR(10),
'🔴 خرید مثقال: %مثقال_خرید', CHAR(10),
'🟢 فروش مثقال: %مثقال_فروش', CHAR(10),
CHAR(10),
'📞 %شماره', CHAR(10),
'%آیدی', CHAR(10),
'%لینک'
);
-- توکن روبیکا (توکن بله/تلگرام از قبل موجودند) — سیستمی (can_edit=0)
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`)
SELECT 'rubika_bot_token', '', 'توکن بات روبیکا', 0, 1, NULL
@@ -39,6 +55,17 @@ SELECT 'price_channel_min_seconds', '120', 'حداقل فاصله ارسال (ث
'حداقل فاصله بین دو ارسال به کانال؛ جلوی سیل پیام را می‌گیرد. صفر = هر تغییر قیمت.', 'number'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'price_channel_min_seconds' LIMIT 1);
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
SELECT 'price_channel_message_text', @price_channel_default_text, 'قالب متن پیام کانال', 1, 1, @price_channel_message_desc, 'longtext'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'price_channel_message_text' LIMIT 1);
UPDATE `settings` SET
`title_fa` = 'قالب متن پیام کانال',
`type` = 'longtext',
`can_edit` = 1,
`description` = @price_channel_message_desc
WHERE `key` = 'price_channel_message_text';
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
SELECT 'price_channel_footer_id', '', 'آیدی کانال در پیام', 1, 1,
'اگر پر باشد در انتهای پیام نمایش داده می‌شود (مثلاً یوزرنیم کانال). خالی = نمایش داده نمی‌شود.', 'text'
@@ -82,7 +109,8 @@ UPDATE `settings` SET `can_edit` = 0 WHERE `key` IN (
);
UPDATE `settings` SET `can_edit` = 1 WHERE `key` IN (
'price_channel_enabled', 'price_channel_messengers', 'price_channel_diff',
'price_channel_min_seconds', 'price_channel_footer_id', 'price_channel_footer_phone',
'price_channel_min_seconds', 'price_channel_message_text',
'price_channel_footer_id', 'price_channel_footer_phone',
'price_channel_footer_link', 'change_mesghal'
);