fixed some bugs

This commit is contained in:
2026-07-08 15:51:20 +03:30
parent 4af6689c11
commit 0e8ad469b1
5 changed files with 86 additions and 26 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ class PriceChannelTest extends Command
$this->line('قابلیت روشن (price_channel_enabled): ' . ($info['enabled'] ? 'بله' : 'خیر'));
$this->line('پیام‌رسان‌های انتخاب‌شده: ' . (implode(', ', $info['selected_messengers']) ?: '— (خالی)'));
$this->line('حداقل فاصله ارسال (ثانیه): ' . $info['min_seconds']);
$this->line('فاصله cooldown گذشته؟ ' . ($info['cooldown_elapsed'] ? 'بله' : 'خیر (هنوز زوده)'));
$this->line('فاصله cooldown گذشته؟ ' . ($info['cooldown_elapsed'] ? 'بله' : 'خیر (هنوز ' . ($info['cooldown_seconds_remaining'] ?? 0) . ' ثانیه مانده)'));
$this->line('منبع قالب: ' . ($info['template_source'] === 'db' ? 'تنظیمات ادمین' : 'پیش‌فرض داخلی'));
if (! empty($info['live_trade_buy_rial'])) {
$this->line('قیمت فروش اپ (buy API): ' . number_format((int) $info['live_trade_buy_rial']) . ' ریال');
+37 -22
View File
@@ -76,9 +76,8 @@ TXT;
}
try {
// شرط ۱: از آخرین «ارسال واقعی» حداقل price_channel_min_seconds گذشته باشد.
// چون فقط روی ارسال واقعی markSent می‌کنیم، یک بررسیِ بی‌ارسال تایمر را ریست نمی‌کند.
if (! $this->cooldownElapsed()) {
// شرط ۱: حداقل price_channel_min_seconds از آخرین ارسال گذشته باشد (اتمی — جلوی race و چند worker)
if (! $this->acquireSendCooldown()) {
return;
}
@@ -111,8 +110,7 @@ TXT;
$results[$messenger] = $this->dispatchToMessenger($messenger, $target['token'], $target['chat_ids'], $message);
}
// فقط بعد از ارسال واقعی: تایمر cooldown و امضای قیمت به‌روزرسانی می‌شوند
$this->markSent();
// فقط بعد از ارسال واقعی: امضا به‌روزرسانی می‌شود (cooldown در acquireSendCooldown ست شد)
Cache::put(self::SIGNATURE_CACHE_KEY, $signature, now()->addDay());
Log::info('PriceChannel: قیمت به کانال ارسال شد', [
@@ -176,6 +174,11 @@ TXT;
return;
}
if (! $this->acquireSendCooldown()) {
Log::debug('PriceChannel: قالب عوض شد ولی cooldown هنوز تمام نشده');
return;
}
$message = $this->buildMessage($products, $boardProduct);
if ($message === '') {
return;
@@ -187,10 +190,9 @@ TXT;
}
$signature = $this->broadcastSignature();
$this->markSent();
Cache::put(self::SIGNATURE_CACHE_KEY, $signature, now()->addDay());
Log::info('PriceChannel: قالب پیام تغییر کرد — ارسال فوری به کانال', [
Log::info('PriceChannel: قالب پیام تغییر کرد — ارسال به کانال', [
'targets' => array_keys($targets),
'results' => $results,
]);
@@ -221,10 +223,8 @@ TXT;
}
if ($message !== '' && $targets !== []) {
$this->markSent();
if ($this->resolveLiveTradePrices() !== null) {
Cache::put(self::SIGNATURE_CACHE_KEY, $this->broadcastSignature(), now()->addDay());
}
$this->acquireSendCooldown(true);
Cache::put(self::SIGNATURE_CACHE_KEY, $this->broadcastSignature(), now()->addDay());
}
return [
@@ -266,6 +266,7 @@ TXT;
'selected_messengers' => $selected,
'min_seconds' => $this->config->getInt('price_channel_min_seconds', 120),
'cooldown_elapsed' => $this->cooldownElapsed(),
'cooldown_seconds_remaining' => $this->cooldownSecondsRemaining(),
'eligible_products' => count($this->eligibleProducts()),
'messengers' => $messengers,
'active_targets' => array_keys($this->resolveActiveTargets()),
@@ -718,25 +719,39 @@ TXT;
]);
}
/** حداقل فاصله‌ی مجاز بین دو ارسال — جلوگیری از سیل پیام در هر تیک ۱۰ثانیه‌ای */
private function cooldownElapsed(): bool
/** حداقل فاصلهٔ مجاز بین دو ارسال — Cache::add اتمی (TTL = min_seconds) */
private function acquireSendCooldown(bool $force = false): bool
{
$minSeconds = max(0, $this->config->getInt('price_channel_min_seconds', 120));
if ($minSeconds === 0) {
if ($minSeconds === 0 || $force) {
if ($minSeconds > 0) {
Cache::put(self::COOLDOWN_CACHE_KEY, now()->timestamp, $minSeconds);
}
return true;
}
$lastSent = Cache::get(self::COOLDOWN_CACHE_KEY);
if ($lastSent === null) {
return true;
}
return now()->timestamp - (int) $lastSent >= $minSeconds;
return Cache::add(self::COOLDOWN_CACHE_KEY, now()->timestamp, $minSeconds);
}
private function markSent(): void
private function cooldownElapsed(): bool
{
Cache::put(self::COOLDOWN_CACHE_KEY, now()->timestamp, now()->addDay());
return $this->cooldownSecondsRemaining() === 0;
}
private function cooldownSecondsRemaining(): int
{
$minSeconds = max(0, $this->config->getInt('price_channel_min_seconds', 120));
if ($minSeconds === 0 || ! Cache::has(self::COOLDOWN_CACHE_KEY)) {
return 0;
}
$lastSent = (int) Cache::get(self::COOLDOWN_CACHE_KEY, 0);
if ($lastSent <= 0) {
return 0;
}
return max(0, $minSeconds - (now()->timestamp - $lastSent));
}
/** @return array<int, string> */
+16 -1
View File
@@ -42,7 +42,22 @@ SET @invite_link_desc = 'متن پیام اشتراک‌گذاری دعوت در
SET @deposit_guide_desc = 'متن راهنمای واریز در مودال شارژ حساب. متغیرها: ٪نام یا %نام = نام صاحب حساب؛ ٪کارت یا %کارت = شماره کارت؛ ٪شبا یا %شبا = شبا. فاصله بعد از ٪ اختیاری است. اگر خالی باشد از متن پیش‌فرض اپ استفاده می‌شود.';
SET @deposit_support_msg_desc = 'متن پیام هنگام کلیک روی «پیام به پشتیبانی» و «پیام به پشتیبانی (sms)» در مودال واریز. فقط متغیر ٪نام یا %نام = نام صاحب حساب (deposit_account_holder). پیش‌فرض: «شماره حساب».';
SET @price_channel_message_desc = 'قالب متن پیام ارسالی به کانال. متغیرهای قیمت تابلو: ٪گرم_خرید ٪گرم_فروش ٪مثقال_خرید ٪مثقال_فروش (یا با %). زمان: ٪تاریخ ٪ساعت. اپ: ٪اسم_اپ یا ٪نام (نام فارسی)؛ ٪عنوان = خط «📊 قیمت لحظه‌ای + نام». تماس جدا: ٪آیدی ٪شماره ٪لینک. اختیاری: ٪محصولات (لیست همه محصولات)؛ ٪فوتر (ترکیب آیدی+شماره+لینک). فاصله بعد از ٪ اختیاری است. خالی = قالب پیش‌فرض.';
SET @price_channel_message_desc = CONCAT(
'متن پیامی که با تغییر قیمت به کانال فرستاده می‌شود. هر متغیر را دقیقاً همین‌طور بنویسید (با ٪ یا %).', CHAR(10),
CHAR(10),
'قیمت تابلو — مثل اپ:', CHAR(10),
'• %گرم_فروش = عدد بزرگ صفحهٔ اصلی اپ (فروش هر گرم)', CHAR(10),
'• %گرم_خرید = خرید هر گرم', CHAR(10),
'• %مثقال_فروش = فروش هر مثقال', CHAR(10),
'• %مثقال_خرید = خرید هر مثقال', CHAR(10),
CHAR(10),
'زمان: %تاریخ (شمسی) • %ساعت', CHAR(10),
'نام: %نام (فقط نام فروشگاه) • %عنوان (خط آماده 📊 قیمت لحظه‌ای + نام)', CHAR(10),
'تماس: %شماره • %آیدی • %لینک (از فیلدهای پایین همین بخش)', CHAR(10),
'اختیاری: %محصولات • %فوتر', CHAR(10),
CHAR(10),
'خالی = قالب پیش‌فرض.'
);
SET @price_channel_default_text = CONCAT(
'سلام 👋', CHAR(10),
'%عنوان', CHAR(10),
@@ -41,7 +41,22 @@ SET @invite_link_desc = 'متن پیام اشتراک‌گذاری دعوت در
SET @deposit_guide_desc = 'متن راهنمای واریز در مودال شارژ حساب. متغیرها: ٪نام یا %نام = نام صاحب حساب؛ ٪کارت یا %کارت = شماره کارت؛ ٪شبا یا %شبا = شبا. فاصله بعد از ٪ اختیاری است. اگر خالی باشد از متن پیش‌فرض اپ استفاده می‌شود.';
SET @deposit_support_msg_desc = 'متن پیام هنگام کلیک روی «پیام به پشتیبانی» و «پیام به پشتیبانی (sms)» در مودال واریز. فقط متغیر ٪نام یا %نام = نام صاحب حساب (deposit_account_holder). پیش‌فرض: «شماره حساب».';
SET @price_channel_message_desc = 'قالب متن پیام ارسالی به کانال. متغیرهای قیمت تابلو: ٪گرم_خرید ٪گرم_فروش ٪مثقال_خرید ٪مثقال_فروش (یا با %). زمان: ٪تاریخ ٪ساعت. اپ: ٪اسم_اپ یا ٪نام (نام فارسی)؛ ٪عنوان = خط «📊 قیمت لحظه‌ای + نام». تماس جدا: ٪آیدی ٪شماره ٪لینک. اختیاری: ٪محصولات (لیست همه محصولات)؛ ٪فوتر (ترکیب آیدی+شماره+لینک). فاصله بعد از ٪ اختیاری است. خالی = قالب پیش‌فرض.';
SET @price_channel_message_desc = CONCAT(
'متن پیامی که با تغییر قیمت به کانال فرستاده می‌شود. هر متغیر را دقیقاً همین‌طور بنویسید (با ٪ یا %).', CHAR(10),
CHAR(10),
'قیمت تابلو — مثل اپ:', CHAR(10),
'• %گرم_فروش = عدد بزرگ صفحهٔ اصلی اپ (فروش هر گرم)', CHAR(10),
'• %گرم_خرید = خرید هر گرم', CHAR(10),
'• %مثقال_فروش = فروش هر مثقال', CHAR(10),
'• %مثقال_خرید = خرید هر مثقال', CHAR(10),
CHAR(10),
'زمان: %تاریخ (شمسی) • %ساعت', CHAR(10),
'نام: %نام (فقط نام فروشگاه) • %عنوان (خط آماده 📊 قیمت لحظه‌ای + نام)', CHAR(10),
'تماس: %شماره • %آیدی • %لینک (از فیلدهای پایین همین بخش)', CHAR(10),
'اختیاری: %محصولات • %فوتر', CHAR(10),
CHAR(10),
'خالی = قالب پیش‌فرض.'
);
SET @price_channel_default_text = CONCAT(
'سلام 👋', CHAR(10),
'%عنوان', CHAR(10),
+16 -1
View File
@@ -11,7 +11,22 @@
START TRANSACTION;
SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci;
SET @price_channel_message_desc = 'قالب متن پیام ارسالی به کانال. متغیرهای قیمت تابلو: ٪گرم_خرید ٪گرم_فروش ٪مثقال_خرید ٪مثقال_فروش (یا با %). زمان: ٪تاریخ ٪ساعت. اپ: ٪اسم_اپ یا ٪نام (نام فارسی)؛ ٪عنوان = خط «📊 قیمت لحظه‌ای + نام». تماس جدا: ٪آیدی ٪شماره ٪لینک. اختیاری: ٪محصولات (لیست همه محصولات)؛ ٪فوتر (ترکیب آیدی+شماره+لینک). فاصله بعد از ٪ اختیاری است. خالی = قالب پیش‌فرض.';
SET @price_channel_message_desc = CONCAT(
'متن پیامی که با تغییر قیمت به کانال فرستاده می‌شود. هر متغیر را دقیقاً همین‌طور بنویسید (با ٪ یا %).', CHAR(10),
CHAR(10),
'قیمت تابلو — مثل اپ:', CHAR(10),
'• %گرم_فروش = عدد بزرگ صفحهٔ اصلی اپ (فروش هر گرم)', CHAR(10),
'• %گرم_خرید = خرید هر گرم', CHAR(10),
'• %مثقال_فروش = فروش هر مثقال', CHAR(10),
'• %مثقال_خرید = خرید هر مثقال', CHAR(10),
CHAR(10),
'زمان: %تاریخ (شمسی) • %ساعت', CHAR(10),
'نام: %نام (فقط نام فروشگاه) • %عنوان (خط آماده 📊 قیمت لحظه‌ای + نام)', CHAR(10),
'تماس: %شماره • %آیدی • %لینک (از فیلدهای پایین همین بخش)', CHAR(10),
'اختیاری: %محصولات • %فوتر', CHAR(10),
CHAR(10),
'خالی = قالب پیش‌فرض.'
);
SET @price_channel_default_text = CONCAT(
'سلام 👋', CHAR(10),
'%عنوان', CHAR(10),