// پاکسازی روزانه‌ی دیتابیس کش پیام‌ها، تا بی‌نهایت بزرگ نشه $this->cleanupLoop = new PeriodicLoop($this->tickCleanupLoop(...), 'cleanup-tick', 3600.0); $this->cleanupLoop->start(); } private function tickCleanupLoop(): bool { try { Store::cleanupOld(3); // فقط ۳ روز اخیر رو نگه دار } catch (\Throwable $e) { $this->logger('cleanup error: ' . $e->getMessage()); } return false; } private function tickClocksLoop(): bool { try { $this->tickClocks(); } catch (\Throwable $e) { $this->reportCrash('tickClocksLoop', $e); } return false; // false = ادامه بده به چرخه بعدی، true = تایمر متوقف بشه } private function tickActionLoop(): bool { try { $s = Store::load(); $mode = $s['action_mode'] ?? null; $chatId = $s['action_chat_id'] ?? null; if ($mode && $chatId && isset(self::ACTION_MODES[$mode])) { $this->messages->setTyping(peer: $chatId, action: ['_' => self::ACTION_MODES[$mode]]); } } catch (\Throwable $e) { $this->logger('action tick error: ' . $e->getMessage()); } return false; } private static array $lastUpdate = ['bio' => 0, 'name' => 0, 'channel' => 0]; private static array $lastMinuteRendered = ['name' => '', 'bio' => '', 'channel' => '']; private function tickClocks(): void { $s = Store::load(); $offset = (int) ($s['clock_offset'] ?? 0); $dtTehran = new DateTime('now', new DateTimeZone('Asia/Tehran')); if ($offset !== 0) { $dtTehran->modify(($offset > 0 ? "+{$offset}" : "{$offset}") . ' minutes'); } $currentTehranMinute = $dtTehran->format('H:i'); if ($s['time_name'] && ($currentTehranMinute !== (self::$lastMinuteRendered['name'] ?? ''))) { $this->updateNameClock($dtTehran); self::$lastMinuteRendered['name'] = $currentTehranMinute; } if ($s['time_bio'] && ($currentTehranMinute !== (self::$lastMinuteRendered['bio'] ?? ''))) { $this->updateBioClock($dtTehran); self::$lastMinuteRendered['bio'] = $currentTehranMinute; } if ($s['time_channel'] && $this->cfg()['clock_channel_id'] && ($currentTehranMinute !== (self::$lastMinuteRendered['channel'] ?? ''))) { $this->updateChannelClock($dtTehran); self::$lastMinuteRendered['channel'] = $currentTehranMinute; } } private function nextTz(string $which): array { $tzList = array_values($this->cfg()['timezones']); $labels = array_keys($this->cfg()['timezones']); $prop = "tzIndex" . ucfirst($which); $idx = $this->$prop % count($tzList); $this->$prop = $this->$prop + 1; return [$labels[$idx], $tzList[$idx]]; } private function pickStyle(): string { $names = Fonts::styleNames(); return $this->cfg()['random_font'] ? $names[array_rand($names)] : $names[array_rand($names)]; } private function clockText(string $prefix, array $tzPair, ?DateTime $dt = null): string { [$label, $tz] = $tzPair; if ($dt === null) { $s = Store::load(); $offset = (int) ($s['clock_offset'] ?? 0); $dt = new DateTime('now', new DateTimeZone($tz)); if ($offset !== 0) { $dt->modify(($offset > 0 ? "+{$offset}" : "{$offset}") . ' minutes'); } } $raw = $prefix . ' ' . $dt->format('H:i') . ' | ' . $label; return Fonts::apply($raw, $this->pickStyle()); } private function updateBioClock(?DateTime $dt = null): void { try { $text = $this->clockText('🕐', $this->nextTz('bio'), $dt); $this->account->updateProfile(about: mb_substr($text, 0, 70)); } catch (\Throwable $e) { $this->logger('bio update error: ' . $e->getMessage()); } } private function updateNameClock(?DateTime $dt = null): void { try { $s = Store::load(); $base = $s['name_clock_base'] ?? null; if ($base === null) { $me = $this->getSelf(); $base = $me['first_name'] ?? 'User'; $s['name_clock_base'] = $base;