60, 'display' => 'Раз в минуту (CF7 Spam → TG)', ]; return $schedules; } public static function tick(): void { if (!Settings::is_configured()) { return; } $items = Queue::fetch_pending(5); $chat_id = Settings::get_chat_id(); foreach ($items as $item) { $payload = json_decode((string)$item->payload_json, true); if (!is_array($payload) || empty($payload['text'])) { Queue::mark_failed((int)$item->id, 'Invalid payload JSON', (int)$item->attempts + 1); continue; } try { TelegramClient::send_message( $chat_id, (string)$payload['text'], isset($payload['reply_markup']) && is_array($payload['reply_markup']) ? $payload['reply_markup'] : null ); Queue::mark_sent((int)$item->id); } catch (TelegramException $e) { self::handle_failure($item, $e); } usleep(100000); // 100ms between sends } } private static function handle_failure(object $item, TelegramException $e): void { $attempts = (int)$item->attempts + 1; if ($e->is_permanent() || $attempts >= self::MAX_ATTEMPTS) { Queue::mark_failed((int)$item->id, $e->getMessage(), $attempts); return; } $backoff = self::BACKOFF_SECONDS[$attempts - 1] ?? end(self::BACKOFF_SECONDS); if ($e->retry_after() > $backoff) { $backoff = $e->retry_after(); } Queue::reschedule((int)$item->id, $attempts, $e->getMessage(), $backoff); } public static function cleanup(): void { Queue::cleanup_old_sent(30); } }