File size: 3,730 Bytes
0dd2082
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
const BaseProvider = require('./BaseProvider');
const logger = require('../utils/logger');

class OSMProvider extends BaseProvider {
    constructor() {
        super('osm_nominatim', { priority: 2, timeout: 5000 });
        this.baseUrl = 'https://nominatim.openstreetmap.org/search';
    }

    async search(intent, pagination) {
        const { category = 'place', location, neighborhood } = intent;

        const area = [neighborhood, location].filter(Boolean).join(', ');

        // If neither neighborhood nor location were parsed by AI, use the raw query as fallback
        const searchTerm = category === 'all'
            ? `places ${area ? 'in ' + area : ''}`.trim()
            : `${category} ${area ? 'in ' + area : ''}`.trim();
        logger.info(`OSMProvider requested Nominatim query: "${searchTerm}"`);

        const params = new URLSearchParams({
            q: searchTerm,
            format: 'json',
            limit: 15,
            addressdetails: 1,
            extratags: 1
        });

        const url = `${this.baseUrl}?${params.toString()}`;

        const response = await fetch(url, {
            method: 'GET',
            headers: {
                'User-Agent': 'RedThreadBot/1.0 (https://github.com/ayush/redthread)'
            }
        });

        if (!response.ok) {
            throw new Error(`Nominatim API Error: ${response.status}`);
        }

        const data = await response.json();

        const results = data.map(el => {
            const extratags = el.extratags || {};

            // Extract genuine real-world features from OSM metadata (no fakeism)
            const realFeatures = [];
            if (extratags.internet_access === 'wlan' || extratags.wifi === 'yes') realFeatures.push('WiFi');
            if (extratags.wheelchair === 'yes') realFeatures.push('Wheelchair Accessible');
            if (extratags.outdoor_seating === 'yes') realFeatures.push('Outdoor Seating');
            if (extratags.air_conditioning === 'yes') realFeatures.push('AC');
            if (extratags.takeaway === 'yes') realFeatures.push('Takeaway');
            if (extratags.delivery === 'yes') realFeatures.push('Delivery');
            if (extratags.diet_vegan === 'yes' || extratags.vegan === 'yes') realFeatures.push('Vegan Options');
            if (extratags.parking === 'yes') realFeatures.push('Parking');

            return {
                id: `osm-${el.place_id}`,
                name: el.name || `Unnamed ${el.type || category}`,
                category: el.type || category,
                address: el.display_name,
                rating: extratags.rating || 'N/A', // OSM rarely has ratings, fallback to honest N/A
                priceRange: extratags.price || extratags.charge || 'N/A', // Honest fallback instead of RNG
                features: realFeatures,
                coordinates: { lat: el.lat, lon: el.lon },
                source: this.name
            };
        });

        return {
            results,
            meta: {
                total: results.length,
                source: this.name,
                providerTime: new Date().toISOString()
            }
        };
    }

    async healthCheck() {
        try {
            const start = Date.now();
            const res = await fetch(`${this.baseUrl}?q=Bhubaneswar&format=json&limit=1`, {
                headers: { 'User-Agent': 'RedThreadBot/1.0' }
            });
            if (res.ok) {
                return { status: 'up', latency: Date.now() - start };
            }
            return { status: 'down', error: `HTTP ${res.status}` };
        } catch (err) {
            return { status: 'down', error: err.message };
        }
    }
}

module.exports = OSMProvider;