false, 'ERROR' => 'method_not_allowed'], 405); } $visitor = requireVisitorAuth(); $dmChannelId = $visitor['DMChannelID'] ? (int)$visitor['DMChannelID'] : null; // No DM channel yet = no messages if (!$dmChannelId) { jsonResponse([ 'OK' => true, 'Messages' => [], 'DMChannelID' => null, 'HasMore' => false, ]); } // Pagination $before = (int)($_GET['Before'] ?? 0); $after = (int)($_GET['After'] ?? 0); $limit = min(100, max(1, (int)($_GET['Limit'] ?? 50))); $where = ['m.ChannelID = ?', 'm.IsDeleted = 0']; $params = [$dmChannelId]; if ($before > 0) { $where[] = 'm.ID < ?'; $params[] = $before; } if ($after > 0) { $where[] = 'm.ID > ?'; $params[] = $after; } $whereClause = implode(' AND ', $where); $fetchLimit = $limit + 1; $order = ($after > 0) ? 'ASC' : 'DESC'; $rows = queryTimed( "SELECT m.ID, m.SenderAddress, m.Content, m.IsEdited, m.CreatedAt FROM Hub_Messages m WHERE $whereClause ORDER BY m.ID $order LIMIT $fetchLimit", $params ); $hasMore = count($rows) > $limit; if ($hasMore) { array_pop($rows); } if ($after > 0) { $rows = array_reverse($rows); } $visitorId = (int)$visitor['ID']; $messages = []; foreach ($rows as $row) { // Determine if sender is visitor or host $isVisitor = str_starts_with($row['SenderAddress'], 'visitor:'); $senderName = $isVisitor ? $visitor['DisplayName'] : $row['SenderAddress']; if (!$isVisitor) { // Resolve agent name $agent = queryOne( "SELECT AgentName FROM Sprinter_Agents WHERE FullAddress = ? LIMIT 1", [$row['SenderAddress']] ); if ($agent) { $senderName = $agent['AgentName']; } } $messages[] = [ 'ID' => (int)$row['ID'], 'SenderAddress' => $row['SenderAddress'], 'SenderName' => $senderName, 'IsVisitor' => $isVisitor, 'Content' => $row['Content'], 'IsEdited' => (bool)$row['IsEdited'], 'CreatedAt' => toISO8601($row['CreatedAt']), ]; } jsonResponse([ 'OK' => true, 'Messages' => $messages, 'DMChannelID' => $dmChannelId, 'HasMore' => $hasMore, ]);